commit 2f891fe4aabf2ccb54adb672eb9e3361b2482618 Author: Jaysyn Date: Mon Jun 16 21:05:17 2025 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..baff019 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Ignore packed files +*.erf +*.hak +*.mod +*.ndb +*.nwm + +# Ignore the nasher directory +.nasher/ +/_release diff --git a/README.md b/README.md new file mode 100644 index 0000000..a79ba15 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Default Module Template + +Repository for the development of the PRC8 version of ..... + +[Discussion Thread on Discord](https://discord.gg/ca2ru3KxYd) + +[Original module]() + +## Requirements +1.) [Nasher](https://github.com/squattingmonk/nasher), installed in your system path. + +2.) [PRC8](https://gitea.raptio.us/Jaysyn/PRC8/src/branch/main/Release) + +3.) [CEP3](https://neverwintervault.org/project/nwnee/hakpak/combined/cep-3-community-expansion-pack) \ No newline at end of file diff --git a/nasher.cfg b/nasher.cfg new file mode 100644 index 0000000..cdbdb4d --- /dev/null +++ b/nasher.cfg @@ -0,0 +1,244 @@ +[package] +name = "Default Module" +description = "PRC8 version of ..." +version = "1.01prc8" +url = "https://discord.gg/ca2ru3KxYd" +author = "Original Author" +author = "Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com>" + + [package.sources] + include = "src/module/**/*.{nss.json}" + include = "src/include/**/*.{nss}" + + [package.rules] + "*" = "src/module/$ext" + +[target] +name = "default" +file = "Default Module.mod" +description = "PRC8 version of ..." + [target.sources] + include = "src/module/**/*" + include = "src/include/**/*" + filter = "bnd_inc_bndfunc.nss" + filter = "bnd_vestig_const.nss" + filter = "inc_2dacache.nss" + filter = "inc_abil_damage.nss" + filter = "inc_acp.nss" + filter = "inc_addragebonus.nss" + filter = "inc_area.nss" + filter = "inc_array_sort.nss" + filter = "inc_cache_setup.nss" + filter = "inc_debug.nss" + filter = "inc_dispel.nss" + filter = "inc_draw.nss" + filter = "inc_draw_prc.nss" + filter = "inc_draw_text.nss" + filter = "inc_draw_tools.nss" + filter = "inc_dynconv.nss" + filter = "inc_ecl.nss" + filter = "inc_epicspellai.nss" + filter = "inc_epicspelldef.nss" + filter = "inc_epicspellfnc.nss" + filter = "inc_epicspells.nss" + filter = "inc_eventhook.nss" + filter = "inc_heap.nss" + filter = "inc_item_props.nss" + filter = "inc_logmessage.nss" + filter = "inc_lookups.nss" + filter = "inc_metalocation.nss" + filter = "inc_newspellbook.nss" + filter = "inc_npc.nss" + filter = "inc_nwnx_funcs.nss" + filter = "inc_persistsql.nss" + filter = "inc_persist_loca.nss" + filter = "inc_pers_array.nss" + filter = "inc_poison.nss" + filter = "inc_prc_npc.nss" + filter = "inc_prc_poly.nss" + filter = "inc_rand_equip.nss" + filter = "inc_ravage.nss" + filter = "inc_rend.nss" + filter = "inc_sbr_readme.nss" + filter = "inc_set.nss" + filter = "inc_spirit_weapn.nss" + filter = "inc_sp_gain_mem.nss" + filter = "inc_sql.nss" + filter = "inc_switch_setup.nss" + filter = "inc_target_list.nss" + filter = "inc_threads.nss" + filter = "inc_time.nss" + filter = "inc_timestop.nss" + filter = "inc_uniqueid.nss" + filter = "inc_utility.nss" + filter = "inc_vfx_const.nss" + filter = "inv_inc_blast.nss" + filter = "inv_inc_invfunc.nss" + filter = "inv_inc_invknown.nss" + filter = "inv_inc_invoke.nss" + filter = "inv_invoc_const.nss" + filter = "inv_invokehook.nss" + filter = "lookup_2da_spell.nss" + filter = "moi_inc_moifunc.nss" + filter = "moi_meld_const.nss" + filter = "nw_o2_coninclude.nss" + filter = "pnp_lich_inc.nss" + filter = "pnp_shft_main.nss" + filter = "pnp_shft_poly.nss" + filter = "prcsp_archmaginc.nss" + filter = "prcsp_engine.nss" + filter = "prcsp_reputation.nss" + filter = "prc_add_spell_dc.nss" + filter = "prc_add_spl_pen.nss" + filter = "prc_allow_const.nss" + filter = "prc_alterations.nss" + filter = "prc_ccc_const.nss" + filter = "prc_ccc_readme.nss" + filter = "prc_class_const.nss" + filter = "prc_compan_inc.nss" + filter = "prc_craft_inc.nss" + filter = "prc_effect_inc.nss" + filter = "prc_feat_const.nss" + filter = "prc_getbest_inc.nss" + filter = "prc_inc_actions.nss" + filter = "prc_inc_array.nss" + filter = "prc_inc_assoc.nss" + filter = "prc_inc_breath.nss" + filter = "prc_inc_burn.nss" + filter = "prc_inc_castlvl.nss" + filter = "prc_inc_chat.nss" + filter = "prc_inc_chat_dm.nss" + filter = "prc_inc_chat_pow.nss" + filter = "prc_inc_chat_shf.nss" + filter = "prc_inc_clsfunc.nss" + filter = "prc_inc_combat.nss" + filter = "prc_inc_combmove.nss" + filter = "prc_inc_core.nss" + filter = "prc_inc_damage.nss" + filter = "prc_inc_descrptr.nss" + filter = "prc_inc_domain.nss" + filter = "prc_inc_dragsham.nss" + filter = "prc_inc_drugfunc.nss" + filter = "prc_inc_effect.nss" + filter = "prc_inc_factotum.nss" + filter = "prc_inc_fork.nss" + filter = "prc_inc_function.nss" + filter = "prc_inc_hextor.nss" + filter = "prc_inc_itmrstr.nss" + filter = "prc_inc_leadersh.nss" + filter = "prc_inc_listener.nss" + filter = "prc_inc_material.nss" + filter = "prc_inc_natweap.nss" + filter = "prc_inc_nat_hb.nss" + filter = "prc_inc_newip.nss" + filter = "prc_inc_nwscript.nss" + filter = "prc_inc_onhit.nss" + filter = "prc_inc_racial.nss" + filter = "prc_inc_sbheir.nss" + filter = "prc_inc_sb_const.nss" + filter = "prc_inc_scry.nss" + filter = "prc_inc_shifting.nss" + filter = "prc_inc_skills.nss" + filter = "prc_inc_skin.nss" + filter = "prc_inc_smite.nss" + filter = "prc_inc_sneak.nss" + filter = "prc_inc_spells.nss" + filter = "prc_inc_sp_tch.nss" + filter = "prc_inc_stunfist.nss" + filter = "prc_inc_switch.nss" + filter = "prc_inc_s_det.nss" + filter = "prc_inc_teleport.nss" + filter = "prc_inc_template.nss" + filter = "prc_inc_turning.nss" + filter = "prc_inc_unarmed.nss" + filter = "prc_inc_util.nss" + filter = "prc_inc_wpnrest.nss" + filter = "prc_ipfeat_const.nss" + filter = "prc_ip_srcost.nss" + filter = "prc_misc_const.nss" + filter = "prc_racial_const.nss" + filter = "prc_shifter_info.nss" + filter = "prc_spellf_inc.nss" + filter = "prc_spellhook.nss" + filter = "prc_spell_const.nss" + filter = "prc_sp_func.nss" + filter = "prc_template_con.nss" + filter = "prc_weap_apt.nss" + filter = "prc_x2_craft.nss" + filter = "prc_x2_itemprop.nss" + filter = "prgt_inc.nss" + filter = "prgt_inc_trap.nss" + filter = "psi_inc_ac_const.nss" + filter = "psi_inc_ac_convo.nss" + filter = "psi_inc_ac_manif.nss" + filter = "psi_inc_ac_spawn.nss" + filter = "psi_inc_augment.nss" + filter = "psi_inc_const.nss" + filter = "psi_inc_core.nss" + filter = "psi_inc_enrgypow.nss" + filter = "psi_inc_metapsi.nss" + filter = "psi_inc_onhit.nss" + filter = "psi_inc_powknown.nss" + filter = "psi_inc_ppoints.nss" + filter = "psi_inc_psicraft.nss" + filter = "psi_inc_psifunc.nss" + filter = "psi_inc_pwresist.nss" + filter = "psi_inc_soulkn.nss" + filter = "psi_power_const.nss" + filter = "psi_spellhook.nss" + filter = "sbr_include.nss" + filter = "shd_inc_metashd.nss" + filter = "shd_inc_myst.nss" + filter = "shd_inc_mystknwn.nss" + filter = "shd_inc_shdfunc.nss" + filter = "shd_mysthook.nss" + filter = "shd_myst_const.nss" + filter = "spinc_bolt.nss" + filter = "spinc_burst.nss" + filter = "spinc_cone.nss" + filter = "spinc_dimdoor.nss" + filter = "spinc_engimm.nss" + filter = "spinc_fdisk.nss" + filter = "spinc_greenfire.nss" + filter = "spinc_lessorb.nss" + filter = "spinc_maze.nss" + filter = "spinc_necro_cyst.nss" + filter = "spinc_orb.nss" + filter = "spinc_remeffct.nss" + filter = "spinc_telecircle.nss" + filter = "spinc_teleport.nss" + filter = "spinc_trans.nss" + filter = "tob_inc_martlore.nss" + filter = "tob_inc_move.nss" + filter = "tob_inc_moveknwn.nss" + filter = "tob_inc_recovery.nss" + filter = "tob_inc_tobfunc.nss" + filter = "tob_movehook.nss" + filter = "tob_move_const.nss" + filter = "true_inc_metautr.nss" + filter = "true_inc_truespk.nss" + filter = "true_inc_trufunc.nss" + filter = "true_inc_truknwn.nss" + filter = "true_inc_utter.nss" + filter = "true_utterhook.nss" + filter = "true_utter_const.nss" + filter = "utl_i_sqluuid.nss" + filter = "x0_i0_transport.nss" + filter = "x2_inc_cutscenep.nss" + filter = "x2_inc_spellhook.nss" + filter = "x3_inc_horse.nss" + filter = "prc_inc_string.nss" + filter = "prc_nui_sc_inc.nss" + filter = "prc_nui_scd_inc.nss" + filter = "prc_nui_consts.nss" + filter = "nw_inc_nui" + filter = "xchst_inc.nss" + +[target] +name = "tophak" +file = "HAKNAME.hak" +description = "PRC8 merge hakpak for PRC8 version of ..." + [target.sources] + include = "src/hakpak/HAKNAME/**/*" + [target.rules] + "*" = "src/hakpak/HAKNAME/$ext" \ No newline at end of file diff --git a/pack_haks.cmd b/pack_haks.cmd new file mode 100644 index 0000000..8957f59 --- /dev/null +++ b/pack_haks.cmd @@ -0,0 +1 @@ +nasher pack tophak --verbose \ No newline at end of file diff --git a/pack_module.cmd b/pack_module.cmd new file mode 100644 index 0000000..2308124 --- /dev/null +++ b/pack_module.cmd @@ -0,0 +1 @@ +nasher pack default --verbose \ No newline at end of file diff --git a/src/include/bnd_inc_bndfunc.nss b/src/include/bnd_inc_bndfunc.nss new file mode 100644 index 0000000..ec533de --- /dev/null +++ b/src/include/bnd_inc_bndfunc.nss @@ -0,0 +1,968 @@ +//:://///////////////////////////////////////////// +//:: Binding/Vestiges main include: Miscellaneous +//:: bnd_inc_bndfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Binding. + + @author Stratovarius + @date Created - 2021.02.02 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the given creature's Binder level. + * The vestige is used for Favored Vestige + * + * @param oBinder The creature whose Binder level to determine + * @param nVestige The rowid of the vestige in spells.2da + * + * @return The Binder level + */ +int GetBinderLevel(object oBinder, int nVestige = -1); + +/** + * Determines whether a given class is a Binding-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a Binding-related class, FALSE otherwise + */ +int GetIsBindingClass(int nClass); + +/** + * Calculates how many Binder levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oBinder Creature to calculate added Binder levels for + * @return The number of Binder levels gained + */ +int GetBindingPRCLevels(object oBinder); + +/** + * Returns the master 2da of all vestiges - "vestiges" + */ +string GetVestigeFile(); + +/** + * Returns the master 2da of binds and bind levels - "cls_bind_binder" + */ +string GetBindingClassFile(int nClass); + +/** + * Casts a particular vestige on the binder + * Should only ever be called via Contact Vestige + * + * @param oBinder The binder + * @param nVestige The vestige to attempt binding + */ +void ApplyVestige(object oBinder, int nVestige); + +/** + * Removes a particular vestige from the binder + * Should only ever be called via Contact Vestige + * + * @param oBinder The binder + * @param nVestige The vestige to expel + */ +void ExpelVestige(object oBinder, int nVestige); + +/** + * Rolls the check to see whether it is a good or bad pact + * Sets a local int to mark pact quality + * + * @param oBinder The binder attempting the check + * @param nVestige The rowid of the vestige in vestiges.2da + * + * @return The rowid of the vestige in spells.2da + */ +int DoBindingCheck(object oBinder, int nVestige); + +/** + * Does the animations and count down to bind a particular vestige + * + * @param oBinder The binder + * @param nTime Should always be 66 seconds + * @param nVestige The vestige to attempt binding + * @param nExpel Whether this is a usage of expel vestige or not + */ +void ContactVestige(object oBinder, int nTime, int nVestige, int nExpel = FALSE); + +/** + * Does the animations and count down to bind a particular vestige + * Should only ever be called via Contact Vestige + * + * @param oBinder The binder + * @param nTime Should always be 60 seconds + * @param nVestige The vestige to attempt binding + * @param nExpel Whether this is a usage of expel vestige or not + */ +void BindVestige(object oBinder, int nTime, int nVestige, int nExpel = FALSE); + +/** + * Checks to see whether an ability is off cooldown or not + * Sets the cooldown if the ability is usable + * Informs the player via floating text + * + * @param oBinder The binder + * @param nAbil The vestige ability (such as Amon's Fire Breath SpellId) + * @param nVestige The vestige the ability comes from (such as VESTIGE_AMON) + * + * @return TRUE/FALSE for off cooldown or not + */ +int BindAbilCooldown(object oBinder, int nAbil, int nVestige); + +/** + * How many vestiges can the binder have bound + * + * @param oBinder The binder + * + * @return A number between 1 and 4 + */ +int GetMaxVestigeCount(object oBinder); + +/** + * What is the highest level vestige the binder can bind? + * + * @param oBinder The binder + * + * @return A number between 1 and 8 + */ +int GetMaxVestigeLevel(object oBinder); + +/** + * What is the vestige's level? + * + * @param nVestige The vestige rowid in vestiges.2da + * + * @return A number between 1 and 8 + */ +int GetVestigeLevel(int nVestige); + +/** + * How many total binds are active on the binder? + * Checks via the spellid of the vestiges + * + * @param oBinder The binder + * + * @return 0 or higher + */ +int GetBindCount(object oBinder); + +/** + * How many uses of pact augmentation does the + * binder get when he binds a vestige? + * + * @param oBinder The binder + * + * @return 0 to 5 + */ +int GetPactAugmentCount(object oBinder); + +/** + * Returns the penalty to be applied if the + * binder made a bad pact with the vestige + * Should never be called directly + * + * @param oBinder The binder + * + * @return Linked effect + */ +effect EffectPact(object oBinder); + +/** + * Checks whether the binder has the rapid recovery + * feat for the named vestige + * + * @param oBinder The binder + * @param nVestige The vestige rowid in spells.2da + * + * @return TRUE/FALSE + */ +int RapidRecovery(object oBinder, int nVestige); + +/** + * Checks whether the binder has the favored vestige + * feat for the named vestige + * + * @param oBinder The binder + * @param nVestige The vestige rowid in spells.2da + * + * @return TRUE/FALSE + */ +int FavoredVestige(object oBinder, int nVestige); + +/** + * Checks whether the binder has the favored vestige + * focus feat for the named vestige + * + * @param oBinder The binder + * @param nVestige The vestige rowid in spells.2da + * + * @return TRUE/FALSE + */ +int FavoredVestigeFocus(object oBinder, int nVestige); + +/** + * Returns the DC for saving against a vestige's abilities + * 10 + 1/2 Binding level + Charisma Modifier + bonuses + * + * @param oBinder The binder + * @param nVestige The vestige rowid in spells.2da + * + * @return A number + */ +int GetBinderDC(object oBinder, int nVestige); + +/** + * Checks for the special requirements of each vestige + * or for the presence of the Ignore Special Requirements + * feat. True means passing requirements, False is a fail + * + * @param oBinder The binder + * @param nVestige The vestige rowid in spells.2da + * + * @return TRUE/FALSE + */ +int DoSpecialRequirements(object oBinder, int nVestige); + +/** + * Checks for the special requirements of each vestige + * or for the presence of the Ignore Special Requirements + * feat. True means passing requirements, False is a fail + * + * This is for requirements that apply during the summoning process + * due to having a cost that applies only after the user has selected + * which vestige they wish to bind + * + * @param oBinder The binder + * @param nVestige The vestige rowid in spells.2da + * + * @return TRUE/FALSE + */ +int DoSummonRequirements(object oBinder, int nVestige); + +/** + * Checks for whether the vestige ability was exploited (meaning not + * granted) by the Anima Mage class feature. + * + * @param oBinder The binder + * @param nVestige The vestige ability rowid in vestigeabil.2da + * + * @return TRUE if exploited/FALSE otherwise + */ +int GetIsVestigeExploited(object oBinder, int nVestigeAbil); + +/** + * Marks a vestige ability as exploited (meaning it will not be + * granted) by the Anima Mage class feature. + * + * @param oBinder The binder + * @param nVestige The vestige ability rowid in vestigeabil.2da + */ +void SetIsVestigeExploited(object oBinder, int nVestigeAbil); + +/** + * Checks whether the patron vestige is bound to a Knight of the Sacred Seal + * If not, the class loses all class features + * + * @param oBinder The binder + * + * @return TRUE if bound, FALSE otherwise + */ +int GetIsPatronVestigeBound(object oBinder); + +/** + * Checks who the patron vestige is for a Knight of the Sacred Seal + * Used to ensure they always have a good pact with their patron + * + * @param oBinder The binder + * + * @return The VESTIGE_* const if one exists, -1 otherwise + */ +int GetPatronVestige(object oBinder); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "inc_dynconv" + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int VESTIGE_CONTACT_TIME = 66; +const int VESTIGE_BINDING_TIME = 60; + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetBinderLevel(object oBinder, int nVestige = -1) +{ + int nLevel; + + if(GetLevelByClass(CLASS_TYPE_BINDER, oBinder)) + { + // Binder level is class level + prestige + nLevel = GetLevelByClass(CLASS_TYPE_BINDER, oBinder); + nLevel += GetBindingPRCLevels(oBinder); + } + // If you have no levels in binder, but you have Bind Vestige feat + else if (GetHasFeat(FEAT_BIND_VESTIGE, oBinder)) + { + nLevel = 1; + if (GetHasFeat(FEAT_BIND_VESTIGE_IMPROVED, oBinder)) nLevel += 4; + } + if (FavoredVestige(oBinder, nVestige)) nLevel += 1; + if (GetHasSpellEffect(VESTIGE_IPOS, oBinder) && !GetIsVestigeExploited(oBinder, VESTIGE_IPOS_INFLUENCE) && nVestige >= VESTIGE_AMON) nLevel += 1; + + if(DEBUG) DoDebug("Binder Level: " + IntToString(nLevel)); + + return nLevel; +} + +int GetIsBindingClass(int nClass) +{ + return nClass == CLASS_TYPE_BINDER; +} + +int GetBindingPRCLevels(object oBinder) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oBinder); + nLevel += GetLevelByClass(CLASS_TYPE_KNIGHT_SACRED_SEAL, oBinder); + nLevel += GetLevelByClass(CLASS_TYPE_SCION_DANTALION, oBinder); + + // These don't add at 1st level + if (GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oBinder)) + nLevel += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oBinder) - 1; + + return nLevel; +} + +string GetVestigeFile() +{ + return "vestiges"; +} + +string GetBindingClassFile(int nClass) +{ + string sFile; + if (nClass == CLASS_TYPE_BINDER) sFile = "cls_bind_binder"; + + return sFile; +} + +void ApplyVestige(object oBinder, int nVestige) +{ + PRCRemoveSpellEffects(nVestige, oBinder, oBinder); + GZPRCRemoveSpellEffects(nVestige, oBinder, FALSE); + ActionCastSpellOnSelf(nVestige, METAMAGIC_NONE, oBinder); + if (GetLevelByClass(CLASS_TYPE_BINDER, oBinder) >= 2) + { + PRCRemoveSpellEffects(VESTIGE_PACT_AUGMENTATION, oBinder, oBinder); + GZPRCRemoveSpellEffects(VESTIGE_PACT_AUGMENTATION, oBinder, FALSE); + ActionCastSpellOnSelf(VESTIGE_PACT_AUGMENTATION, METAMAGIC_NONE, oBinder); + } + if (DEBUG) DoDebug("Applying Vestige "+IntToString(nVestige)+" on "+GetName(oBinder)); + + // If you have hosted one of these spirits within the last 24 hours, Amon refuses to answer your call. + if (nVestige == VESTIGE_LERAJE || + nVestige == VESTIGE_CHUPOCLOPS || + nVestige == VESTIGE_KARSUS || + nVestige == VESTIGE_EURYNOME) + { + SetLocalInt(oBinder, "AmonHater", TRUE); + DelayCommand(HoursToSeconds(48), DeleteLocalInt(oBinder, "AmonHater")); + } + + // Only good quality pacts get the bonus spell + if (GetLocalInt(oBinder, "PactQuality"+IntToString(nVestige)) && GetLocalInt(oBinder, "ExploitVestigeConv")) + { + DelayCommand(0.5, StartDynamicConversation("bnd_exploitcnv", oBinder, DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, FALSE, TRUE, oBinder)); + DeleteLocalInt(oBinder, "ExploitVestigeConv"); + } +} + +void ExpelVestige(object oBinder, int nVestige) +{ + SetPersistantLocalInt(oBinder, "ExpelledVestige", TRUE); + SetPersistantLocalInt(oBinder, "ExpelledVestige"+IntToString(nVestige), TRUE); + // Here, making a good pack means we can unbind it + if (GetLocalInt(oBinder, "PactQuality"+IntToString(nVestige))) + { + PRCRemoveSpellEffects(nVestige, oBinder, oBinder); + GZPRCRemoveSpellEffects(nVestige, oBinder, FALSE); + FloatingTextStringOnCreature("Expelled "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nVestige)))+" successfully!", oBinder, FALSE); + } + else + FloatingTextStringOnCreature("Failed to expel "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nVestige))), oBinder, FALSE); +} + +int DoBindingCheck(object oBinder, int nVestige) +{ + int nApply = StringToInt(Get2DACache("vestiges", "SpellID", nVestige)); + + // Knights of the Sacred Seal always have a good pact with their Patron + if (GetPatronVestige(oBinder) == nApply) + { + SetLocalInt(oBinder, "PactQuality"+IntToString(nApply), TRUE); + return nApply; //We don't need the rest of the function here. + } + // Scions of Dantalion always have a good pact with Dantalion + if (GetLevelByClass(CLASS_TYPE_SCION_DANTALION, oBinder) && VESTIGE_DANTALION == nApply) + { + SetLocalInt(oBinder, "PactQuality"+IntToString(nApply), TRUE); + return nApply; //We don't need the rest of the function here. + } + + int nRoll = d20()+ GetBinderLevel(oBinder) + GetAbilityModifier(ABILITY_CHARISMA, oBinder); + if (GetHasFeat(FEAT_SKILLED_PACT_MAKING, oBinder)) nRoll += 4; + // -10 penalty on the check + if (GetLocalInt(oBinder, "RushedBinding")) + { + DeleteLocalInt(oBinder, "RushedBinding"); + nRoll -= 10; + } + // After expelling a vestige, take a -10 penalty on the check + if (GetPersistantLocalInt(oBinder, "ExpelledVestige")) + { + DeletePersistantLocalInt(oBinder, "ExpelledVestige"); + nRoll -= 10; + } + // Next time you rebind an expelled vestige, take a penalty on the check + if (GetPersistantLocalInt(oBinder, "ExpelledVestige"+IntToString(nApply))) + { + DeletePersistantLocalInt(oBinder, "ExpelledVestige"+IntToString(nApply)); + nRoll -= 10; + } + // Exploiting a vestige grants a -5 on the check + if (GetLocalInt(oBinder, "ExploitVestigeTemp")) + { + nRoll -= 5; + FloatingTextStringOnCreature("Exploiting vestige", oBinder); + // This int is only for this Binding check + DeleteLocalInt(oBinder, "ExploitVestigeTemp"); + } + int nDC = StringToInt(Get2DACache("vestiges", "BindDC", nVestige)); + SendMessageToPC(oBinder, "Binding Check: "+IntToString(nRoll)+" vs a DC of "+IntToString(nDC)); + DeleteLocalInt(oBinder, "PactQuality"+IntToString(nApply)); + // Mark a good pact + if (nRoll >= nDC) SetLocalInt(oBinder, "PactQuality"+IntToString(nApply), TRUE); + + return nApply; +} + +void BindVestige(object oBinder, int nTime, int nVestige, int nExpel = FALSE) +{ + if (0 >= nTime) + { + SetCutsceneMode(oBinder, FALSE); + // We're expelling a vestige, not binding one + if (nExpel) + ExpelVestige(oBinder, DoBindingCheck(oBinder, nVestige)); + // Make a check and apply the vestige + else + ApplyVestige(oBinder, DoBindingCheck(oBinder, nVestige)); + } + else if (!GetIsInCombat(oBinder)) // Being in combat causes this to fail + { + FloatingTextStringOnCreature("You must spend " + IntToString(nTime) +" more seconds to complete the binding", oBinder, FALSE); + DelayCommand(6.0, BindVestige(oBinder, nTime - 6, nVestige, nExpel)); + SetCutsceneMode(oBinder, TRUE); + AssignCommand(oBinder, ActionPlayAnimation(ANIMATION_LOOPING_TALK_PLEADING, 1.0, 6.0)); + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_MAZE), GetLocation(oBinder), 6.0); + } + else + SetCutsceneMode(oBinder, FALSE); +} + +void ContactVestige(object oBinder, int nTime, int nVestige, int nExpel = FALSE) +{ + if (0 >= nTime) + { + SetCutsceneMode(oBinder, FALSE); + if (!DoSummonRequirements(oBinder, nVestige)) return; + int nBindTime = VESTIGE_BINDING_TIME; + if(GetPRCSwitch(PRC_BIND_VESTIGE_TIMER) >= 12) nBindTime = GetPRCSwitch(PRC_BIND_VESTIGE_TIMER); + if (GetLocalInt(oBinder, "RushedBinding") || GetLocalInt(oBinder, "RapidPactMaking")) nBindTime = 6; + BindVestige(oBinder, nBindTime, nVestige, nExpel); + } + else if (!GetIsInCombat(oBinder)) // Being in combat causes this to fail + { + FloatingTextStringOnCreature("You must draw the symbol for another " + IntToString(nTime) +" seconds", oBinder, FALSE); + DelayCommand(6.0, ContactVestige(oBinder, nTime - 6, nVestige, nExpel)); + SetCutsceneMode(oBinder, TRUE); + AssignCommand(oBinder, ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0)); + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_SYMB_INSAN), GetLocation(oBinder), 6.0); + } + else + SetCutsceneMode(oBinder, FALSE); +} + +int BindAbilCooldown(object oBinder, int nAbil, int nVestige) +{ + int nCheck = GetLocalInt(oBinder, "Bind"+IntToString(nAbil)); + // On Cooldown + if (nCheck) + { + // Free use + if (GetLocalInt(oBinder, "KotSSSurge")) + { + DeleteLocalInt(oBinder, "KotSSSurge"); + FloatingTextStringOnCreature("Using Vestige's Surge on "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nAbil))), oBinder, FALSE); + return TRUE; + } + FloatingTextStringOnCreature(GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nAbil)))+" is still on cooldown!", oBinder, FALSE); + return FALSE; + } + else + { + SetLocalInt(oBinder, "Bind"+IntToString(nAbil), TRUE); + // Default number of rounds + int nDelay = 5; + // Makes it one round faster + if (RapidRecovery(oBinder, nVestige)) nDelay -= 1; + DelayCommand(RoundsToSeconds(nDelay), DeleteLocalInt(oBinder, "Bind"+IntToString(nAbil))); + DelayCommand(RoundsToSeconds(nDelay), FloatingTextStringOnCreature(GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nAbil)))+" is off cooldown", oBinder, FALSE)); + FloatingTextStringOnCreature("You must wait " + IntToString(nDelay) +" rounds before using "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nAbil)))+" again", oBinder, FALSE); + } + + return TRUE; +} + +int GetMaxVestigeCount(object oBinder) +{ + int nMax = StringToInt(Get2DACache(GetBindingClassFile(CLASS_TYPE_BINDER), "Vestiges", GetBinderLevel(oBinder))); + + if (DEBUG) DoDebug("GetMaxVestigeCount is "+IntToString(nMax)); + return nMax; +} + +int GetMaxVestigeLevel(object oBinder) +{ + int nLevel = GetBinderLevel(oBinder); + if (GetHasFeat(FEAT_IMPROVED_BINDING, oBinder)) nLevel += 2; + // Due to the 2da starting at row 0 + int nMax = StringToInt(Get2DACache(GetBindingClassFile(CLASS_TYPE_BINDER), "VestigeLvl", nLevel - 1)); + + if (DEBUG) DoDebug("GetMaxVestigeLevel is "+IntToString(nMax)); + return nMax; +} + +int GetVestigeLevel(int nVestige) +{ + int nMax = StringToInt(Get2DACache("Vestiges", "Level", nVestige)); + + if (DEBUG) DoDebug("GetVestigeLevel is "+IntToString(nMax)); + return nMax; +} + +int GetBindCount(object oBinder) +{ + int i, nCount; + for(i = VESTIGE_AMON; i <= VESTIGE_ABYSM; i++) + if(GetHasSpellEffect(i, oBinder)) nCount++; + + if (DEBUG) DoDebug("GetBindCount is "+IntToString(nCount)); + return nCount; +} + +int GetPactAugmentCount(object oBinder) +{ + int nClass = GetLevelByClass(CLASS_TYPE_BINDER, oBinder); + int nCount = 0; + + if (nClass >= 20) nCount = 5; + else if (nClass >= 16) nCount = 4; + else if (nClass >= 10) nCount = 3; + else if (nClass >= 5) nCount = 2; + else if (nClass >= 2) nCount = 1; + + if (DEBUG) DoDebug("GetPactAugmentCount is "+IntToString(nCount)); + return nCount; +} + +effect EffectPact(object oBinder) +{ + effect eEffect; + if (!GetLocalInt(oBinder, "PactQuality"+IntToString(GetSpellId()))) + { + // –1 penalty on attack rolls, saving throws, and skill checks + eEffect = EffectLinkEffects(EffectSkillDecrease(SKILL_ALL_SKILLS, 1), EffectSavingThrowDecrease(SAVING_THROW_ALL, 1)); + eEffect = EffectLinkEffects(eEffect, EffectAttackDecrease(1)); + } + else // NWN hates having a blank effect + eEffect = EffectLinkEffects(EffectSkillDecrease(SKILL_DISCIPLINE, 1), EffectSkillIncrease(SKILL_DISCIPLINE, 1)); + + return eEffect; +} + +int RapidRecovery(object oBinder, int nVestige) +{ + int nFavored; + + if (GetHasFeat(FEAT_RAPID_RECOVERY_AMON, oBinder) && nVestige == VESTIGE_AMON) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_AYM , oBinder) && nVestige == VESTIGE_AYM ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_LERAJE , oBinder) && nVestige == VESTIGE_LERAJE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_NABERIUS , oBinder) && nVestige == VESTIGE_NABERIUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_RONOVE , oBinder) && nVestige == VESTIGE_RONOVE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_DAHLVERNAR , oBinder) && nVestige == VESTIGE_DAHLVERNAR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_HAAGENTI , oBinder) && nVestige == VESTIGE_HAAGENTI ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_MALPHAS , oBinder) && nVestige == VESTIGE_MALPHAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_SAVNOK , oBinder) && nVestige == VESTIGE_SAVNOK ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ANDROMALIUS, oBinder) && nVestige == VESTIGE_ANDROMALIUS) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_FOCALOR , oBinder) && nVestige == VESTIGE_FOCALOR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_KARSUS , oBinder) && nVestige == VESTIGE_KARSUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_PAIMON , oBinder) && nVestige == VESTIGE_PAIMON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_AGARES , oBinder) && nVestige == VESTIGE_AGARES ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ANDRAS , oBinder) && nVestige == VESTIGE_ANDRAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_BUER , oBinder) && nVestige == VESTIGE_BUER ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_EURYNOME , oBinder) && nVestige == VESTIGE_EURYNOME ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_TENEBROUS , oBinder) && nVestige == VESTIGE_TENEBROUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ARETE , oBinder) && nVestige == VESTIGE_ARETE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ASTAROTH , oBinder) && nVestige == VESTIGE_ASTAROTH ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ACERERAK , oBinder) && nVestige == VESTIGE_ACERERAK ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_BALAM , oBinder) && nVestige == VESTIGE_BALAM ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_DANTALION , oBinder) && nVestige == VESTIGE_DANTALION ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_GERYON , oBinder) && nVestige == VESTIGE_GERYON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_OTIAX , oBinder) && nVestige == VESTIGE_OTIAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_CHUPOCLOPS , oBinder) && nVestige == VESTIGE_CHUPOCLOPS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_HAURES , oBinder) && nVestige == VESTIGE_HAURES ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_IPOS , oBinder) && nVestige == VESTIGE_IPOS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_SHAX , oBinder) && nVestige == VESTIGE_SHAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ZAGAN , oBinder) && nVestige == VESTIGE_ZAGAN ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_VANUS , oBinder) && nVestige == VESTIGE_VANUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_THETRIAD , oBinder) && nVestige == VESTIGE_THETRIAD ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_DESHARIS , oBinder) && nVestige == VESTIGE_DESHARIS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ZCERYLL , oBinder) && nVestige == VESTIGE_ZCERYLL ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ELIGOR , oBinder) && nVestige == VESTIGE_ELIGOR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_MARCHOSIAS , oBinder) && nVestige == VESTIGE_MARCHOSIAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ASHARDALON , oBinder) && nVestige == VESTIGE_ASHARDALON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_HALPHAX , oBinder) && nVestige == VESTIGE_HALPHAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ORTHOS , oBinder) && nVestige == VESTIGE_ORTHOS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_RAPID_RECOVERY_ABYSM , oBinder) && nVestige == VESTIGE_ABYSM ) nFavored = TRUE; + + if (DEBUG) DoDebug("RapidRecovery return value "+IntToString(nFavored)); + return nFavored; +} + +int FavoredVestige(object oBinder, int nVestige) +{ + int nFavored; + + if (GetHasFeat(FEAT_FAVORED_VESTIGE_AMON, oBinder) && nVestige == VESTIGE_AMON) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_AYM , oBinder) && nVestige == VESTIGE_AYM ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_LERAJE , oBinder) && nVestige == VESTIGE_LERAJE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_NABERIUS , oBinder) && nVestige == VESTIGE_NABERIUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_RONOVE , oBinder) && nVestige == VESTIGE_RONOVE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_DAHLVERNAR , oBinder) && nVestige == VESTIGE_DAHLVERNAR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_HAAGENTI , oBinder) && nVestige == VESTIGE_HAAGENTI ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_MALPHAS , oBinder) && nVestige == VESTIGE_MALPHAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_SAVNOK , oBinder) && nVestige == VESTIGE_SAVNOK ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ANDROMALIUS, oBinder) && nVestige == VESTIGE_ANDROMALIUS) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCALOR , oBinder) && nVestige == VESTIGE_FOCALOR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_KARSUS , oBinder) && nVestige == VESTIGE_KARSUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_PAIMON , oBinder) && nVestige == VESTIGE_PAIMON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_AGARES , oBinder) && nVestige == VESTIGE_AGARES ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ANDRAS , oBinder) && nVestige == VESTIGE_ANDRAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_BUER , oBinder) && nVestige == VESTIGE_BUER ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_EURYNOME , oBinder) && nVestige == VESTIGE_EURYNOME ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_TENEBROUS , oBinder) && nVestige == VESTIGE_TENEBROUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ARETE , oBinder) && nVestige == VESTIGE_ARETE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ASTAROTH , oBinder) && nVestige == VESTIGE_ASTAROTH ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ACERERAK , oBinder) && nVestige == VESTIGE_ACERERAK ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_BALAM , oBinder) && nVestige == VESTIGE_BALAM ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_DANTALION , oBinder) && nVestige == VESTIGE_DANTALION ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_GERYON , oBinder) && nVestige == VESTIGE_GERYON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_OTIAX , oBinder) && nVestige == VESTIGE_OTIAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_CHUPOCLOPS , oBinder) && nVestige == VESTIGE_CHUPOCLOPS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_HAURES , oBinder) && nVestige == VESTIGE_HAURES ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_IPOS , oBinder) && nVestige == VESTIGE_IPOS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_SHAX , oBinder) && nVestige == VESTIGE_SHAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ZAGAN , oBinder) && nVestige == VESTIGE_ZAGAN ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_VANUS , oBinder) && nVestige == VESTIGE_VANUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_THETRIAD , oBinder) && nVestige == VESTIGE_THETRIAD ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_DESHARIS , oBinder) && nVestige == VESTIGE_DESHARIS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ZCERYLL , oBinder) && nVestige == VESTIGE_ZCERYLL ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ELIGOR , oBinder) && nVestige == VESTIGE_ELIGOR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_MARCHOSIAS , oBinder) && nVestige == VESTIGE_MARCHOSIAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ASHARDALON , oBinder) && nVestige == VESTIGE_ASHARDALON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_HALPHAX , oBinder) && nVestige == VESTIGE_HALPHAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ORTHOS , oBinder) && nVestige == VESTIGE_ORTHOS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_ABYSM , oBinder) && nVestige == VESTIGE_ABYSM ) nFavored = TRUE; + + if (DEBUG) DoDebug("FavoredVestige return value "+IntToString(nFavored)); + return nFavored; +} + +int FavoredVestigeFocus(object oBinder, int nVestige) +{ + int nFavored; + + if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_AMON, oBinder) && nVestige == VESTIGE_AMON) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_AYM , oBinder) && nVestige == VESTIGE_AYM ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_LERAJE , oBinder) && nVestige == VESTIGE_LERAJE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_NABERIUS , oBinder) && nVestige == VESTIGE_NABERIUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_RONOVE , oBinder) && nVestige == VESTIGE_RONOVE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_DAHLVERNAR , oBinder) && nVestige == VESTIGE_DAHLVERNAR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_HAAGENTI , oBinder) && nVestige == VESTIGE_HAAGENTI ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_MALPHAS , oBinder) && nVestige == VESTIGE_MALPHAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_SAVNOK , oBinder) && nVestige == VESTIGE_SAVNOK ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ANDROMALIUS, oBinder) && nVestige == VESTIGE_ANDROMALIUS) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_FOCALOR , oBinder) && nVestige == VESTIGE_FOCALOR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_KARSUS , oBinder) && nVestige == VESTIGE_KARSUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_PAIMON , oBinder) && nVestige == VESTIGE_PAIMON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_AGARES , oBinder) && nVestige == VESTIGE_AGARES ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ANDRAS , oBinder) && nVestige == VESTIGE_ANDRAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_BUER , oBinder) && nVestige == VESTIGE_BUER ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_EURYNOME , oBinder) && nVestige == VESTIGE_EURYNOME ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_TENEBROUS , oBinder) && nVestige == VESTIGE_TENEBROUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ARETE , oBinder) && nVestige == VESTIGE_ARETE ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ASTAROTH , oBinder) && nVestige == VESTIGE_ASTAROTH ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ACERERAK , oBinder) && nVestige == VESTIGE_ACERERAK ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_BALAM , oBinder) && nVestige == VESTIGE_BALAM ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_DANTALION , oBinder) && nVestige == VESTIGE_DANTALION ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_GERYON , oBinder) && nVestige == VESTIGE_GERYON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_OTIAX , oBinder) && nVestige == VESTIGE_OTIAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_CHUPOCLOPS , oBinder) && nVestige == VESTIGE_CHUPOCLOPS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_HAURES , oBinder) && nVestige == VESTIGE_HAURES ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_IPOS , oBinder) && nVestige == VESTIGE_IPOS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_SHAX , oBinder) && nVestige == VESTIGE_SHAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ZAGAN , oBinder) && nVestige == VESTIGE_ZAGAN ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_VANUS , oBinder) && nVestige == VESTIGE_VANUS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_THETRIAD , oBinder) && nVestige == VESTIGE_THETRIAD ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_DESHARIS , oBinder) && nVestige == VESTIGE_DESHARIS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ZCERYLL , oBinder) && nVestige == VESTIGE_ZCERYLL ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ELIGOR , oBinder) && nVestige == VESTIGE_ELIGOR ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_MARCHOSIAS , oBinder) && nVestige == VESTIGE_MARCHOSIAS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ASHARDALON , oBinder) && nVestige == VESTIGE_ASHARDALON ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_HALPHAX , oBinder) && nVestige == VESTIGE_HALPHAX ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ORTHOS , oBinder) && nVestige == VESTIGE_ORTHOS ) nFavored = TRUE; + else if (GetHasFeat(FEAT_FAVORED_VESTIGE_FOCUS_ABYSM , oBinder) && nVestige == VESTIGE_ABYSM ) nFavored = TRUE; + + if (DEBUG) DoDebug("FavoredVestigeFocus return value "+IntToString(nFavored)); + return nFavored; +} + +int GetBinderDC(object oBinder, int nVestige) +{ + int nDC = 10 + GetBinderLevel(oBinder, nVestige)/2 + GetAbilityModifier(ABILITY_CHARISMA, oBinder); + if (FavoredVestigeFocus(oBinder, nVestige)) nDC += 1; + if (GetHasSpellEffect(VESTIGE_IPOS, oBinder) && !GetIsVestigeExploited(oBinder, VESTIGE_IPOS_INFLUENCE)) nDC += 1; + + return nDC; +} + +int DoSpecialRequirements(object oBinder, int nVestige) +{ + if (GetHasFeat(FEAT_IGNORE_SPECIAL_REQUIREMENTS, oBinder)) return TRUE; + + if (nVestige == VESTIGE_AMON && (GetLocalInt(oBinder, "AmonHater") || + GetHasSpellEffect(VESTIGE_LERAJE, oBinder) || + GetHasSpellEffect(VESTIGE_EURYNOME, oBinder) || + GetHasSpellEffect(VESTIGE_KARSUS, oBinder) || + GetHasSpellEffect(VESTIGE_CHUPOCLOPS, oBinder))) + return FALSE; + if (nVestige == VESTIGE_LERAJE && GetHasSpellEffect(VESTIGE_AMON, oBinder)) + return FALSE; + if (nVestige == VESTIGE_NABERIUS && 4 > GetSkillRank(SKILL_LORE, oBinder, TRUE) && 4 > GetSkillRank(SKILL_BLUFF, oBinder, TRUE)) + return FALSE; + // Ronove’s seal must be drawn in the soil under the sky. + if (nVestige == VESTIGE_RONOVE && (GetIsAreaNatural(GetArea(oBinder)) != AREA_NATURAL || GetIsAreaAboveGround(GetArea(oBinder)) != AREA_ABOVEGROUND)) + return FALSE; + if (nVestige == VESTIGE_HAAGENTI && (CREATURE_SIZE_LARGE > PRCGetCreatureSize(oBinder) || (GetHasFeat(FEAT_RACE_POWERFUL_BUILD, oBinder) && CREATURE_SIZE_MEDIUM > PRCGetCreatureSize(oBinder)))) + return FALSE; + if (nVestige == VESTIGE_KARSUS && GetHasSpellEffect(VESTIGE_AMON, oBinder)) + return FALSE; + if (nVestige == VESTIGE_KARSUS && 5 > GetSkillRank(SKILL_LORE, oBinder, TRUE) && 5 > GetSkillRank(SKILL_SPELLCRAFT, oBinder, TRUE)) + return FALSE; + if (nVestige == VESTIGE_KARSUS) + { + effect eTest = GetFirstEffect(oBinder); + while(GetIsEffectValid(eTest)) + { + if(GetEffectType(eTest) == EFFECT_TYPE_AREA_OF_EFFECT) + return FALSE; + + eTest = GetNextEffect(oBinder); + } + } + // Agagres’s seal must be drawn in the soil + if (nVestige == VESTIGE_AGARES && GetIsAreaNatural(GetArea(oBinder)) != AREA_NATURAL) + return FALSE; + if (nVestige == VESTIGE_BUER && GetIsAreaInterior(GetArea(oBinder))) + return FALSE; + if (nVestige == VESTIGE_EURYNOME && GetHasSpellEffect(VESTIGE_AMON, oBinder)) + return FALSE; + if (nVestige == VESTIGE_TENEBROUS && !GetIsNight()) + return FALSE; + if (nVestige == VESTIGE_BALAM) + { + int nCur = GetCurrentHitPoints(oBinder); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(1, DAMAGE_TYPE_SLASHING), oBinder); + if (nCur > GetCurrentHitPoints(oBinder)) + return FALSE; + } + if (nVestige == VESTIGE_GERYON && 5 > GetSkillRank(SKILL_LORE, oBinder, TRUE)) + return FALSE; + if (nVestige == VESTIGE_ARETE && (GetHasSpellEffect(VESTIGE_EURYNOME, oBinder) || GetHasSpellEffect(VESTIGE_CHUPOCLOPS, oBinder))) + return FALSE; + if (nVestige == VESTIGE_CHUPOCLOPS && GetHasSpellEffect(VESTIGE_AMON, oBinder)) + return FALSE; + if (nVestige == VESTIGE_IPOS && 5 > GetSkillRank(SKILL_LORE, oBinder, TRUE) && 5 > GetSkillRank(SKILL_SPELLCRAFT, oBinder, TRUE)) + return FALSE; + if (nVestige == VESTIGE_VANUS && GetIsAreaNatural(GetArea(oBinder)) != AREA_NATURAL) + return FALSE; + if (nVestige == VESTIGE_DESHARIS && GetIsAreaNatural(GetArea(oBinder)) == AREA_NATURAL) + return FALSE; + if (nVestige == VESTIGE_HALPHAX && !GetIsAreaInterior(GetArea(oBinder))) + return FALSE; + if (nVestige == VESTIGE_HALPHAX && GetIsAreaInterior(GetArea(oBinder)) && GetIsAreaAboveGround(GetArea(oBinder)) == AREA_UNDERGROUND) + return FALSE; + if (nVestige == VESTIGE_ORTHOS && GetTileMainLight1Color(GetLocation(oBinder)) != TILE_MAIN_LIGHT_COLOR_WHITE && GetTileMainLight1Color(GetLocation(oBinder)) != TILE_MAIN_LIGHT_COLOR_YELLOW) + return FALSE; + + return TRUE; +} + +int DoSummonRequirements(object oBinder, int nVestige) +{ + if (GetHasFeat(FEAT_IGNORE_SPECIAL_REQUIREMENTS, oBinder)) return TRUE; + + int nSpellId = StringToInt(Get2DACache(GetVestigeFile(), "SpellID", nVestige)); + + if (nSpellId == VESTIGE_LERAJE) + { + object oArrow = GetItemInSlot(INVENTORY_SLOT_ARROWS, oBinder); + int nStack = GetItemStackSize(oArrow); + if (nStack) + SetItemStackSize(oArrow, nStack-1); + else + { + FloatingTextStringOnCreature("You have failed to break an arrow for Leraje, and she refuses your call!", oBinder, FALSE); + return FALSE; + } + } + + return TRUE; +} + +int GetIsVestigeExploited(object oBinder, int nVestigeAbil) +{ + if (GetLocalInt(oBinder, "ExploitVestige") == nVestigeAbil) return TRUE; + + return FALSE; +} + +void SetIsVestigeExploited(object oBinder, int nVestigeAbil) +{ + SetLocalInt(oBinder, "ExploitVestige", nVestigeAbil); + SetLocalInt(oBinder, "ExploitVestigeTemp", TRUE); + SetLocalInt(oBinder, "ExploitVestigeConv", TRUE); +} + +int GetIsPatronVestigeBound(object oBinder) +{ + int nPatron; + + if (GetHasFeat(FEAT_PATRON_VESTIGE_AMON , oBinder)) nPatron = VESTIGE_AMON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_AYM , oBinder)) nPatron = VESTIGE_AYM; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_LERAJE , oBinder)) nPatron = VESTIGE_LERAJE; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_NABERIUS , oBinder)) nPatron = VESTIGE_NABERIUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_RONOVE , oBinder)) nPatron = VESTIGE_RONOVE; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_DAHLVERNAR , oBinder)) nPatron = VESTIGE_DAHLVERNAR; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_HAAGENTI , oBinder)) nPatron = VESTIGE_HAAGENTI; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_MALPHAS , oBinder)) nPatron = VESTIGE_MALPHAS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_SAVNOK , oBinder)) nPatron = VESTIGE_SAVNOK; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ANDROMALIUS, oBinder)) nPatron = VESTIGE_ANDROMALIUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_FOCALOR , oBinder)) nPatron = VESTIGE_FOCALOR; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_KARSUS , oBinder)) nPatron = VESTIGE_KARSUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_PAIMON , oBinder)) nPatron = VESTIGE_PAIMON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_AGARES , oBinder)) nPatron = VESTIGE_AGARES; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ANDRAS , oBinder)) nPatron = VESTIGE_ANDRAS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_BUER , oBinder)) nPatron = VESTIGE_BUER; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_EURYNOME , oBinder)) nPatron = VESTIGE_EURYNOME; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_TENEBROUS , oBinder)) nPatron = VESTIGE_TENEBROUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ARETE , oBinder)) nPatron = VESTIGE_ARETE; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ASTAROTH , oBinder)) nPatron = VESTIGE_ASTAROTH; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ACERERAK , oBinder)) nPatron = VESTIGE_ACERERAK; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_BALAM , oBinder)) nPatron = VESTIGE_BALAM; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_DANTALION , oBinder)) nPatron = VESTIGE_DANTALION; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_GERYON , oBinder)) nPatron = VESTIGE_GERYON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_OTIAX , oBinder)) nPatron = VESTIGE_OTIAX; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_CHUPOCLOPS , oBinder)) nPatron = VESTIGE_CHUPOCLOPS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_HAURES , oBinder)) nPatron = VESTIGE_HAURES; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_IPOS , oBinder)) nPatron = VESTIGE_IPOS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_SHAX , oBinder)) nPatron = VESTIGE_SHAX; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ZAGAN , oBinder)) nPatron = VESTIGE_ZAGAN; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_VANUS , oBinder)) nPatron = VESTIGE_VANUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_THETRIAD , oBinder)) nPatron = VESTIGE_THETRIAD; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_DESHARIS , oBinder)) nPatron = VESTIGE_DESHARIS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ZCERYLL , oBinder)) nPatron = VESTIGE_ZCERYLL; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ELIGOR , oBinder)) nPatron = VESTIGE_ELIGOR; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_MARCHOSIAS , oBinder)) nPatron = VESTIGE_MARCHOSIAS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ASHARDALON , oBinder)) nPatron = VESTIGE_ASHARDALON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_HALPHAX , oBinder)) nPatron = VESTIGE_HALPHAX; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ORTHOS , oBinder)) nPatron = VESTIGE_ORTHOS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ABYSM , oBinder)) nPatron = VESTIGE_ABYSM; + + if(GetHasSpellEffect(nPatron, oBinder)) return TRUE; + + return FALSE; +} + +int GetPatronVestige(object oBinder) +{ + int nPatron = -1; + + if (GetHasFeat(FEAT_PATRON_VESTIGE_AMON , oBinder)) nPatron = VESTIGE_AMON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_AYM , oBinder)) nPatron = VESTIGE_AYM; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_LERAJE , oBinder)) nPatron = VESTIGE_LERAJE; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_NABERIUS , oBinder)) nPatron = VESTIGE_NABERIUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_RONOVE , oBinder)) nPatron = VESTIGE_RONOVE; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_DAHLVERNAR , oBinder)) nPatron = VESTIGE_DAHLVERNAR; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_HAAGENTI , oBinder)) nPatron = VESTIGE_HAAGENTI; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_MALPHAS , oBinder)) nPatron = VESTIGE_MALPHAS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_SAVNOK , oBinder)) nPatron = VESTIGE_SAVNOK; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ANDROMALIUS, oBinder)) nPatron = VESTIGE_ANDROMALIUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_FOCALOR , oBinder)) nPatron = VESTIGE_FOCALOR; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_KARSUS , oBinder)) nPatron = VESTIGE_KARSUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_PAIMON , oBinder)) nPatron = VESTIGE_PAIMON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_AGARES , oBinder)) nPatron = VESTIGE_AGARES; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ANDRAS , oBinder)) nPatron = VESTIGE_ANDRAS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_BUER , oBinder)) nPatron = VESTIGE_BUER; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_EURYNOME , oBinder)) nPatron = VESTIGE_EURYNOME; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_TENEBROUS , oBinder)) nPatron = VESTIGE_TENEBROUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ARETE , oBinder)) nPatron = VESTIGE_ARETE; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ASTAROTH , oBinder)) nPatron = VESTIGE_ASTAROTH; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ACERERAK , oBinder)) nPatron = VESTIGE_ACERERAK; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_BALAM , oBinder)) nPatron = VESTIGE_BALAM; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_DANTALION , oBinder)) nPatron = VESTIGE_DANTALION; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_GERYON , oBinder)) nPatron = VESTIGE_GERYON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_OTIAX , oBinder)) nPatron = VESTIGE_OTIAX; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_CHUPOCLOPS , oBinder)) nPatron = VESTIGE_CHUPOCLOPS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_HAURES , oBinder)) nPatron = VESTIGE_HAURES; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_IPOS , oBinder)) nPatron = VESTIGE_IPOS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_SHAX , oBinder)) nPatron = VESTIGE_SHAX; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ZAGAN , oBinder)) nPatron = VESTIGE_ZAGAN; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_VANUS , oBinder)) nPatron = VESTIGE_VANUS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_THETRIAD , oBinder)) nPatron = VESTIGE_THETRIAD; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_DESHARIS , oBinder)) nPatron = VESTIGE_DESHARIS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ZCERYLL , oBinder)) nPatron = VESTIGE_ZCERYLL; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ELIGOR , oBinder)) nPatron = VESTIGE_ELIGOR; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_MARCHOSIAS , oBinder)) nPatron = VESTIGE_MARCHOSIAS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ASHARDALON , oBinder)) nPatron = VESTIGE_ASHARDALON; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_HALPHAX , oBinder)) nPatron = VESTIGE_HALPHAX; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ORTHOS , oBinder)) nPatron = VESTIGE_ORTHOS; + else if (GetHasFeat(FEAT_PATRON_VESTIGE_ABYSM , oBinder)) nPatron = VESTIGE_ABYSM; + + return nPatron; +} \ No newline at end of file diff --git a/src/include/bnd_vestig_const.nss b/src/include/bnd_vestig_const.nss new file mode 100644 index 0000000..4db3502 --- /dev/null +++ b/src/include/bnd_vestig_const.nss @@ -0,0 +1,321 @@ +// Vestige SpellId Constants +const int VESTIGE_AMON = 19020; +const int VESTIGE_AYM = 19021; +const int VESTIGE_LERAJE = 19022; +const int VESTIGE_NABERIUS = 19023; +const int VESTIGE_RONOVE = 19024; +const int VESTIGE_DAHLVERNAR = 19025; +const int VESTIGE_HAAGENTI = 19026; +const int VESTIGE_MALPHAS = 19027; +const int VESTIGE_SAVNOK = 19028; +const int VESTIGE_ANDROMALIUS = 19029; +const int VESTIGE_FOCALOR = 19030; +const int VESTIGE_KARSUS = 19031; +const int VESTIGE_PAIMON = 19032; +const int VESTIGE_AGARES = 19033; +const int VESTIGE_ANDRAS = 19034; +const int VESTIGE_BUER = 19035; +const int VESTIGE_EURYNOME = 19036; +const int VESTIGE_TENEBROUS = 19037; +const int VESTIGE_ARETE = 19038; +const int VESTIGE_ASTAROTH = 19039; +const int VESTIGE_ACERERAK = 19040; +const int VESTIGE_BALAM = 19041; +const int VESTIGE_DANTALION = 19042; +const int VESTIGE_GERYON = 19043; +const int VESTIGE_OTIAX = 19044; +const int VESTIGE_CHUPOCLOPS = 19045; +const int VESTIGE_HAURES = 19046; +const int VESTIGE_IPOS = 19047; +const int VESTIGE_SHAX = 19048; +const int VESTIGE_ZAGAN = 19049; +const int VESTIGE_VANUS = 19050; +const int VESTIGE_THETRIAD = 19051; +const int VESTIGE_DESHARIS = 19052; +const int VESTIGE_ZCERYLL = 19053; +const int VESTIGE_ELIGOR = 19054; +const int VESTIGE_MARCHOSIAS = 19055; +const int VESTIGE_ASHARDALON = 19056; +const int VESTIGE_HALPHAX = 19057; +const int VESTIGE_ORTHOS = 19058; +const int VESTIGE_ABYSM = 19059; + +// Vestige IPFeat Constants +const int IP_CONST_VESTIGE_AMON_BREATH = 13730; +const int IP_CONST_VESTIGE_AYM_HALO = 13731; +const int IP_CONST_VESTIGE_LERAJE_RICOCHET = 13732; +const int IP_CONST_VESTIGE_NABERIUS_DISGUISE = 13733; +const int IP_CONST_VESTIGE_NABERIUS_COMMAND = 13734; +const int IP_CONST_VESTIGE_RONOVE_FARHAND = 13735; +const int IP_CONST_VESTIGE_RONOVE_BULLRUSH = 13736; +const int IP_CONST_VESTIGE_DAHLVER_MOAN = 13737; +const int IP_CONST_VESTIGE_DAHLVER_SHARE = 13738; +const int IP_CONST_VESTIGE_HAAGENTI_TOUCH = 13739; +const int IP_CONST_VESTIGE_MALPHAS_EYE = 13740; +const int IP_CONST_VESTIGE_MALPHAS_INVIS_ST = 13741; +const int IP_CONST_VESTIGE_MALPHAS_INVIS_SW = 13742; +const int IP_CONST_VESTIGE_SAVNOK_CALL = 13743; +const int IP_CONST_VESTIGE_SAVNOK_MOVE_ST = 13744; +const int IP_CONST_VESTIGE_SAVNOK_MOVE_SW = 13745; +const int IP_CONST_VESTIGE_ANDRO_MIRTH = 13746; +const int IP_CONST_VESTIGE_ANDRO_LOCATE = 13747; +const int IP_CONST_VESTIGE_ANDRO_SEE = 13748; +const int IP_CONST_VESTIGE_FOCALOR_AURA = 13749; +const int IP_CONST_VESTIGE_FOCALOR_BOLT = 13750; +const int IP_CONST_VESTIGE_FOCALOR_BREATH = 13751; +const int IP_CONST_VESTIGE_KARSUS_SENSES = 13752; +const int IP_CONST_VESTIGE_KARSUS_TOUCH = 13753; +const int IP_CONST_VESTIGE_PAIMON_DANCE = 13754; +const int IP_CONST_VESTIGE_AGARES_STEP = 13755; +const int IP_CONST_VESTIGE_AGARES_ELEMENTAL = 13756; +const int IP_CONST_VESTIGE_ANDRAS_SMITE = 13757; +const int IP_CONST_VESTIGE_ANDRAS_DISCORD = 13758; +const int IP_CONST_VESTIGE_ANDRAS_MOUNT = 13759; +const int IP_CONST_VESTIGE_BUER_HEAL = 13760; +const int IP_CONST_VESTIGE_TENEBROUS_DARKNESS = 13761; +const int IP_CONST_VESTIGE_TENEBROUS_TOUCH_VOID = 13762; +const int IP_CONST_VESTIGE_TENEBROUS_TURN = 13763; +const int IP_CONST_VESTIGE_TENEBROUS_VESSEL = 13764; +const int IP_CONST_VESTIGE_ACERERAK_DETECT = 13765; +const int IP_CONST_VESTIGE_ACERERAK_HIDE = 13766; +const int IP_CONST_VESTIGE_ACERERAK_TOUCH = 13767; +const int IP_CONST_VESTIGE_BALAM_CUNNING = 13768; +const int IP_CONST_VESTIGE_BALAM_GLARE = 13769; +const int IP_CONST_VESTIGE_DANTALION_AWE = 13770; +const int IP_CONST_VESTIGE_DANTALION_READ = 13771; +const int IP_CONST_VESTIGE_DANTALION_TRAVEL = 13772; +const int IP_CONST_VESTIGE_GERYON_GAZE = 13773; +const int IP_CONST_VESTIGE_GERYON_FLIGHT = 13774; +const int IP_CONST_VESTIGE_OTIAX_AIR_BLAST = 13775; +const int IP_CONST_VESTIGE_OTIAX_OPEN = 13776; +const int IP_CONST_VESTIGE_OTIAX_UNLOCK = 13777; +const int IP_CONST_VESTIGE_ARETE_RESISTANCE = 13778; +const int IP_CONST_VESTIGE_ASTAROTH_BREATH = 13779; +const int IP_CONST_VESTIGE_ASTAROTH_WORD = 13780; +const int IP_CONST_VESTIGE_CHUPOCLOPS_DESPAIR = 13781; +const int IP_CONST_VESTIGE_CHUPOCLOPS_ETHEREAL = 13782; +const int IP_CONST_VESTIGE_HAURES_IMAGE = 13783; +const int IP_CONST_VESTIGE_HAURES_KILLER = 13784; +const int IP_CONST_VESTIGE_IPOS_INSIGHT = 13785; +const int IP_CONST_VESTIGE_SHAX_FREEDOM = 13786; +const int IP_CONST_VESTIGE_SHAX_STRIKE = 13787; +const int IP_CONST_VESTIGE_ZAGAN_AVERSION = 13788; +const int IP_CONST_VESTIGE_VANUS_FREE_ALLY = 13789; +const int IP_CONST_VESTIGE_THE_TRIAD_SMITE = 13790; +const int IP_CONST_VESTIGE_DESHARIS_TELEPORT = 13791; +const int IP_CONST_VESTIGE_DESHARIS_SMITE = 13792; +const int IP_CONST_VESTIGE_DESHARIS_ANIMATE = 13793; +const int IP_CONST_VESTIGE_ZCERYLL_BOLTS = 13794; +const int IP_CONST_VESTIGE_ZCERYLL_SUMMON = 13795; +const int IP_CONST_VESTIGE_ZCERYLL_TRUE_STRIKE = 13796; +const int IP_CONST_VESTIGE_ELIGOR_STRIKE = 13797; +const int IP_CONST_VESTIGE_MARCHOSIAS_SMOKE = 13798; +const int IP_CONST_VESTIGE_ASHARDALON_PRESENCE = 13799; +const int IP_CONST_VESTIGE_ASHARDALON_LOCATE = 13800; +const int IP_CONST_VESTIGE_HALPHAX_IMPRISON = 13801; +const int IP_CONST_VESTIGE_HALPHAX_BARRIER = 13802; +const int IP_CONST_VESTIGE_HALPHAX_SHELTER = 13803; +const int IP_CONST_VESTIGE_ORTHOS_BREATH = 13804; + +// Vestige Ability SpellId Constants +const int VESTIGE_NABERIUS_DISGUISE_SELF_LEARN = 19074; +const int VESTIGE_NABERIUS_DISGUISE_SELF_OPTIONS = 19075; +const int VESTIGE_NABERIUS_DISGUISE_SELF_QS1 = 19076; +const int VESTIGE_NABERIUS_DISGUISE_SELF_QS2 = 19077; +const int VESTIGE_NABERIUS_DISGUISE_SELF_QS3 = 19078; +const int VESTIGE_NABERIUS_COMMAND_APPROACH = 19080; +const int VESTIGE_NABERIUS_COMMAND_DROP = 19081; +const int VESTIGE_NABERIUS_COMMAND_FALL = 19082; +const int VESTIGE_NABERIUS_COMMAND_FLEE = 19083; +const int VESTIGE_NABERIUS_COMMAND_HALT = 19084; +const int VESTIGE_RONOVE_FARHAND = 19085; +const int VESTIGE_RONOVE_BULLRUSH = 19086; +const int VESTIGE_FOCALOR_AURA_SADNESS = 19097; +const int VESTIGE_KARSUS_SENSES = 19100; +const int VESTIGE_KARSUS_DISPEL = 19101; +const int VESTIGE_TENEBROUS_TURN = 19111; +const int VESTIGE_TENEBROUS_FLICKER = 19112; +const int VESTIGE_DANTALION_READ_THOUGHTS = 19119; +const int VESTIGE_DANTALION_TRAVEL_SPELL = 19120; +const int VESTIGE_ARETE_RESIST = 19126; +const int VESTIGE_CHUPOCLOPS_AURA_DESPAIR = 19129; +const int VESTIGE_CHUPOCLOPS_ETHEREAL_WATCHER = 19130; +const int VESTIGE_THE_TRIAD_SMITE = 19138; + +// General Binding SpellId Constants +const int VESTIGE_PACT_AUGMENTATION = 19171; + +// Vestige Ability Constants, matches line # in vestigeabil.2da +const int VESTIGE_AMON_DARKVISION = 1 ; +const int VESTIGE_AMON_FIREBREATH = 2 ; +const int VESTIGE_AMON_RAMATTACK = 3 ; +const int VESTIGE_AYM_DWARVEN_STEP = 4 ; +const int VESTIGE_AYM_HALO_FIRE = 5 ; +const int VESTIGE_AYM_IMP_DISARM = 6 ; +const int VESTIGE_AYM_MEDIUM_ARMOR = 7 ; +const int VESTIGE_AYM_RESIST_FIRE = 8 ; +const int VESTIGE_AYM_RUINOUS_ATTACK = 9 ; +const int VESTIGE_LERAJE_HIDE_BONUS = 10; +const int VESTIGE_LERAJE_LOW_LIGHT_VISION = 11; +const int VESTIGE_LERAJE_PBSHOT = 12; +const int VESTIGE_LERAJE_RICOCHET = 13; +const int VESTIGE_LERAJE_WEAPON_PROF = 14; +const int VESTIGE_NABERIUS_DISGUISE_SELF = 15; +const int VESTIGE_NABERIUS_ABILITY_HEALING = 16; +const int VESTIGE_NABERIUS_SKILLS = 17; +const int VESTIGE_NABERIUS_PERSUASIVE = 18; +const int VESTIGE_NABERIUS_SILVER_TONGUE = 19; +const int VESTIGE_RONOVE_COLD_IRON = 20; +const int VESTIGE_RONOVE_FAR_HAND = 21; +const int VESTIGE_RONOVE_FISTS = 22; +const int VESTIGE_RONOVE_SPRINT = 23; +const int VESTIGE_DAHLVERNAR_MAD_SOUL = 24; +const int VESTIGE_DAHLVERNAR_MADDENING_MOAN = 25; +const int VESTIGE_DAHLVERNAR_NATURAL_ARMOR = 26; +const int VESTIGE_DAHLVERNAR_SHIELD_SELF = 27; +const int VESTIGE_HAAGENTI_CONFUSE = 28; +const int VESTIGE_HAAGENTI_IMMUNE_TRANS = 29; +const int VESTIGE_HAAGENTI_SHIELD_PROF = 30; +const int VESTIGE_HAAGENTI_WEAPON_PROF = 31; +const int VESTIGE_MALPHAS_ARCANE_EYE = 32; +const int VESTIGE_MALPHAS_INVIS = 33; +const int VESTIGE_MALPHAS_POISON_USE = 34; +const int VESTIGE_MALPHAS_SNEAK_ATTACK = 35; +const int VESTIGE_SAVNOK_CALL_ARMOR = 36; +const int VESTIGE_SAVNOK_HEAVY_ARMOR_PROF = 37; +const int VESTIGE_SAVNOK_MOVE_ALLY = 38; +const int VESTIGE_SAVNOK_SAVNOKS_ARMOR = 39; +const int VESTIGE_ANDROMALIUS_JESTER = 40; +const int VESTIGE_ANDROMALIUS_LOCATE = 41; +const int VESTIGE_ANDROMALIUS_SEE = 42; +const int VESTIGE_ANDROMALIUS_SENSE = 43; +const int VESTIGE_ANDROMALIUS_SNEAK = 44; +const int VESTIGE_FOCALOR_AURA = 45; +const int VESTIGE_FOCALOR_BREATH = 46; +const int VESTIGE_FOCALOR_LIGHTNING = 47; +const int VESTIGE_FOCALOR_BREATHING = 48; +const int VESTIGE_KARSUS_HEAVY_MAGIC = 49; +const int VESTIGE_KARSUS_SENSES_ABIL = 50; +const int VESTIGE_KARSUS_TOUCH = 51; +const int VESTIGE_KARSUS_WILL = 52; +const int VESTIGE_PAIMON_BLADES = 53; +const int VESTIGE_PAIMON_DEX = 54; +const int VESTIGE_PAIMON_SKILLS = 55; +const int VESTIGE_PAIMON_DODGE = 56; +const int VESTIGE_PAIMON_WHIRLWIND = 57; +const int VESTIGE_PAIMON_DANCE = 58; +const int VESTIGE_AGARES_EARTH_MASTERY = 59; +const int VESTIGE_AGARES_EARTHSHAKING = 60; +const int VESTIGE_AGARES_COMPANION = 61; +const int VESTIGE_AGARES_FEAR = 62; +const int VESTIGE_ANDRAS_WEAPON_PROF = 63; +const int VESTIGE_ANDRAS_MOUNT = 64; +const int VESTIGE_ANDRAS_SADDLE_SURE = 65; +const int VESTIGE_ANDRAS_SMITE = 66; +const int VESTIGE_ANDRAS_DISCORD = 67; +const int VESTIGE_ANDRAS_SURE_BLOWS = 68; +const int VESTIGE_BUER_KNOWLEDGE = 69; +const int VESTIGE_BUER_PURITY = 70; +const int VESTIGE_BUER_DELAY_DISEASE = 71; +const int VESTIGE_BUER_FAST_HEALING = 72; +const int VESTIGE_BUER_HEALING_GIFT = 73; +const int VESTIGE_BUER_TRACK = 74; +const int VESTIGE_EURYNOME_ANIMAL_FRIEND = 75; +const int VESTIGE_EURYNOME_DR = 76; +const int VESTIGE_EURYNOME_MAUL = 77; +const int VESTIGE_EURYNOME_POISON = 78; +const int VESTIGE_TENEBROUS_DARKNESS = 79; +const int VESTIGE_TENEBROUS_SEE_DARKNESS = 80; +const int VESTIGE_TENEBROUS_TOUCH_VOID = 81; +const int VESTIGE_TENEBROUS_TURN_UNDEAD = 82; +const int VESTIGE_TENEBROUS_EMPTY_VESSEL = 83; +const int VESTIGE_ARETE_PSIONIC_BOON = 84; +const int VESTIGE_ARETE_RESISTANCE = 85; +const int VESTIGE_ARETE_DR = 86; +const int VESTIGE_ARETE_REPLETION = 87; +const int VESTIGE_ASTAROTH_LORE = 88; +const int VESTIGE_ASTAROTH_BREATH = 89; +const int VESTIGE_ASTAROTH_TONGUE = 90; +const int VESTIGE_ASTAROTH_CRAFT = 91; +const int VESTIGE_ASTAROTH_WORD = 92; +const int VESTIGE_ACERERAK_DETECT = 93; +const int VESTIGE_ACERERAK_HIDE = 94; +const int VESTIGE_ACERERAK_LICH = 95; +const int VESTIGE_ACERERAK_TOUCH = 96; +const int VESTIGE_ACERERAK_HEALING = 97; +const int VESTIGE_BALAM_CUNNING = 98; +const int VESTIGE_BALAM_GLARE = 99; +const int VESTIGE_BALAM_PRESCIENCE = 100; +const int VESTIGE_BALAM_FINESSE = 101; +const int VESTIGE_DANTALION_AWE = 102; +const int VESTIGE_DANTALION_KNOWS = 103; +const int VESTIGE_DANTALION_READ = 104; +const int VESTIGE_DANTALION_TRAVEL = 105; +const int VESTIGE_GERYON_GAZE = 106; +const int VESTIGE_GERYON_VISION = 107; +const int VESTIGE_GERYON_DARKNESS = 108; +const int VESTIGE_GERYON_FLIGHT = 109; +const int VESTIGE_OTIAX_AIR_BLAST = 110; +const int VESTIGE_OTIAX_MIST = 111; +const int VESTIGE_OTIAX_OPEN = 112; +const int VESTIGE_OTIAX_UNLOCK = 113; +const int VESTIGE_CHUPOCLOPS_DESPAIR = 114; +const int VESTIGE_CHUPOCLOPS_ETHEREAL = 115; +const int VESTIGE_CHUPOCLOPS_BITE = 116; +const int VESTIGE_CHUPOCLOPS_POUNCE = 117; +const int VESTIGE_CHUPOCLOPS_SOULSENSE = 118; +const int VESTIGE_HAURES_MIND = 119; +const int VESTIGE_HAURES_MOVE = 120; +const int VESTIGE_HAURES_IMAGE = 121; +const int VESTIGE_HAURES_KILLER = 122; +const int VESTIGE_IPOS_CLAWS = 123; +const int VESTIGE_IPOS_INSIGHT = 124; +const int VESTIGE_IPOS_INFLUENCE = 125; +const int VESTIGE_IPOS_REND = 126; +const int VESTIGE_SHAX_FREEDOM = 127; +const int VESTIGE_SHAX_IMMUNITY = 128; +const int VESTIGE_SHAX_STORM_STRIKE = 129; +const int VESTIGE_ZAGAN_AVERSION = 130; +const int VESTIGE_ZAGAN_GRAPPLE = 131; +const int VESTIGE_ZAGAN_SCENT = 132; +const int VESTIGE_ZAGAN_CONSTRICT = 133; +const int VESTIGE_ZAGAN_LIZARD_BANE = 134; +const int VESTIGE_VANUS_FEAR_AURA = 135; +const int VESTIGE_VANUS_FREE_ALLY = 136; +const int VESTIGE_VANUS_DISDAIN = 137; +const int VESTIGE_VANUS_EARS = 138; +const int VESTIGE_THE_TRIAD_PSIONIC_BOON = 139; +const int VESTIGE_THE_TRIAD_GORN = 140; +const int VESTIGE_THE_TRIAD_RUJSHA = 141; +const int VESTIGE_THE_TRIAD_MINTAR = 142; +const int VESTIGE_DESHARIS_CITY_DWELLER = 143; +const int VESTIGE_DESHARIS_TELEPORT = 144; +const int VESTIGE_DESHARIS_SMITE = 145; +const int VESTIGE_DESHARIS_ANIMATE = 146; +const int VESTIGE_ZCERYLL_ALIEN_FORM = 147; +const int VESTIGE_ZCERYLL_ALIEN_MIND = 148; +const int VESTIGE_ZCERYLL_BOLTS = 149; +const int VESTIGE_ZCERYLL_SUMMON = 150; +const int VESTIGE_ELIGOR_STRIKE = 151; +const int VESTIGE_ELIGOR_SADDLE = 152; +const int VESTIGE_ELIGOR_STRENGTH = 153; +const int VESTIGE_ELIGOR_RESILIENCE = 154; +const int VESTIGE_ELIGOR_ARMOR = 155; +const int VESTIGE_MARCHOSIAS_DEATH_ATTACK = 156; +const int VESTIGE_MARCHOSIAS_RETRIBUTION = 157; +const int VESTIGE_MARCHOSIAS_SMOKE = 158; +const int VESTIGE_MARCHOSIAS_SILENT = 159; +const int VESTIGE_ASHARDALON_CREED = 160; +const int VESTIGE_ASHARDALON_PRESENCE = 161; +const int VESTIGE_ASHARDALON_VIGOR = 162; +const int VESTIGE_ASHARDALON_HEART = 163; +const int VESTIGE_HALPHAX_DR = 164; +const int VESTIGE_HALPHAX_KNOWLEDGE = 165; +const int VESTIGE_HALPHAX_IMPRISON = 166; +const int VESTIGE_HALPHAX_BARRIER = 167; +const int VESTIGE_HALPHAX_SHELTER = 168; +const int VESTIGE_ORTHOS_SIGHT = 169; +const int VESTIGE_ORTHOS_DISPLACEMENT = 170; +const int VESTIGE_ORTHOS_BREATH = 171; +const int VESTIGE_ABYSM_PSIONIC_BOON = 172; +const int VESTIGE_ABYSM_OVERPOWER = 173; \ No newline at end of file diff --git a/src/include/inc_2dacache.nss b/src/include/inc_2dacache.nss new file mode 100644 index 0000000..7ef2c61 --- /dev/null +++ b/src/include/inc_2dacache.nss @@ -0,0 +1,190 @@ +/** @file + * Caching 2da read function and related. + * + * SQL/NWNx functions now in inc_sql + * @author Primogenitor + * + * @todo Document the constants and functions + */ + +const int DEBUG_GET2DACACHE = FALSE; + +string Get2DACache(string s2DA, string sColumn, int nRow); + +string GetBiowareDBName(); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "inc_debug" +//#include "prc_inc_switch" + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +/* +This is fugly and long. Should maybe be refactored to multiple functions. But everything inline does give +a few less instructions used. + +Caching behaviour: Tokens in creature inventory +*/ +string Get2DACache(string s2DA, string sColumn, int nRow) +{ + //lower case the 2da and column + s2DA = GetStringLowerCase(s2DA); + sColumn = GetStringLowerCase(sColumn); + + /*//get the chest that contains the cache + object oCacheWP = GetObjectByTag("Bioware2DACache"); + //if no chest, use HEARTOFCHAOS in limbo as a location to make a new one + if (!GetIsObjectValid(oCacheWP)) + { + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Cache container creature does not exist, creating new one"); + //oCacheWP = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_chest2", + // GetLocation(GetObjectByTag("HEARTOFCHAOS")), FALSE, "Bioware2DACache"); + //has to be a creature, placeables cant go through the DB + oCacheWP = CreateObject(OBJECT_TYPE_CREATURE, "prc_2da_cache", + GetLocation(GetObjectByTag("HEARTOFCHAOS")), FALSE, "Bioware2DACache"); + } + + //get the token for this file + string sFileWPName = s2DA + "_" + sColumn + "_" + IntToString(nRow / 1000); + //if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Token tag is " + sFileWPName); + object oFileWP = GetObjectByTag(sFileWPName); + //token doesnt exist make it + if(!GetIsObjectValid(oFileWP)) + { + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Token does not exist, creating new one"); + + // Use containers to avoid running out of inventory space + int nContainer = 0; + string sContainerName = "Bio2DACacheTokenContainer_" + GetSubString(s2DA, 0, 1) + "_"; + object oContainer = GetObjectByTag(sContainerName + IntToString(nContainer)); + + // Find last existing container + if(GetIsObjectValid(oContainer)) + { + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Seeking last container in series: " + sContainerName); + // find the last container + nContainer = GetLocalInt(oContainer, "ContainerCount"); + oContainer = GetObjectByTag(sContainerName + IntToString(nContainer)); + + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Found: " + DebugObject2Str(oContainer)); + + // Make sure it's not full + if(GetLocalInt(oContainer, "NumTokensInside") >= 34) // Container has 35 slots. Attempt to not use them all, just in case + { + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Container full: " + DebugObject2Str(oContainer)); + oContainer = OBJECT_INVALID; + ++nContainer; // new container is 1 higher than last one + } + } + // We need to create a container + if(!GetIsObjectValid(oContainer)) + { + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Creating new container"); + + oContainer = CreateObject(OBJECT_TYPE_ITEM, "nw_it_contain001", GetLocation(oCacheWP), FALSE, sContainerName + IntToString(nContainer)); + DestroyObject(oContainer); + oContainer = CopyObject(oContainer, GetLocation(oCacheWP), oCacheWP, sContainerName + IntToString(nContainer)); + // store the new number of containers in this series + if (nContainer) + SetLocalInt( GetObjectByTag(sContainerName + "0"), "ContainerCount", nContainer); + // else this is the first container - do nothing as this is the same as storing 0 on it. + // Also here we still have 2 objects with the same tag so above code may get + // the object destroyed at the end of the function if this is the first container. + } + + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Using container: " + DebugObject2Str(oContainer)); + + // Create the new token + //oFileWP = CreateObject(OBJECT_TYPE_ITEM, "hidetoken", GetLocation(oCacheWP), FALSE, sFileWPName); + //DestroyObject(oFileWP); + //oFileWP = CopyObject(oFileWP, GetLocation(oCacheWP), oCacheWP, sFileWPName); + oFileWP = CreateItemOnObject("hidetoken", oContainer, 1, sFileWPName); + + //SetName(oFileWP, "2da Cache - " + sFileWPName); + + // Increment token count tracking variable + SetLocalInt(oContainer, "NumTokensInside", GetLocalInt(oContainer, "NumTokensInside") + 1); + } + + // get the value from the cache + string s = GetLocalString(oFileWP, s2DA+"|"+sColumn+"|"+IntToString(nRow)); + //entry didnt exist in the cache + if(s == "") + { + //fetch from the 2da file + s = Get2DAString(s2DA, sColumn, nRow); + + // store it on the waypoint + SetLocalString(oFileWP, s2DA+"|"+sColumn+"|"+IntToString(nRow), + (s == "" ? "****" : s) ); // this sets the stored string to "****" if s is an empty string (else stores s) + if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Missing from cache: " + s2DA + "|" + sColumn + "|" + IntToString(nRow)); + } + //if(DEBUG_GET2DACACHE) PrintString("Get2DACache: Returned value is '" + s + "'");*/ + string s = Get2DAString(s2DA, sColumn, nRow); + return s == "****" ? "" : s; +} + +string GetBiowareDBName() +{ + string sReturn; + sReturn = "prc_data"; + //if(GetPRCSwitch(MARKER_PRC_COMPANION)) + // sReturn += "cp"; + if(GetPRCSwitch(MARKER_CEP1)) + sReturn += "c1"; + if(GetPRCSwitch(MARKER_CEP2)) + sReturn += "c2"; + if(GetPRCSwitch(MARKER_Q)) + sReturn += "q"; + return sReturn; + +} + +/** + * Gets the file end for the given 2da. + * + * @param sTable The 2da to get the file end for + * @return The file end value of the 2da. + */ +int PRCGetFileEnd(string sTable) +{ + sTable = GetStringLowerCase(sTable); + return GetPRCSwitch("PRC_FILE_END_" + sTable); +/* //check fileends.2da first + int nEnd = StringToInt(Get2DACache("fileends", sTable, 0)); + + //still nothing - check + if(!nEnd) nEnd = StringToInt(Get2DACache("fileends", GetStringLeft(sTable, 8)+"*", 0)); + + return nEnd;*/ +} + +/** + * Gets the file end for special PRC files - used in new spellbook system. + * + * cls_psipw_* + * cls_move_* + * cls_true_* + * cls_inv_* + * cls_spell_* + * + * @param sTable The 2da to get the file end for + * @return The file end value of the 2da. + */ +int PRCGetDynamicFileEnd(string sTable) +{ + int nRet = StringToInt(Get2DACache(sTable, "label", 0)); + if(!nRet) + { + if (DEBUG) DoDebug("FileEnd for "+sTable+" not found! Using default value."); + nRet = 2720; + } + return nRet; +} + +//Cache setup functions moved to inc_cache_setup diff --git a/src/include/inc_abil_damage.nss b/src/include/inc_abil_damage.nss new file mode 100644 index 0000000..8539950 --- /dev/null +++ b/src/include/inc_abil_damage.nss @@ -0,0 +1,215 @@ +//:://///////////////////////////////////////////// +//:: Ability Damage special effects include +//:: inc_abil_damage +//::////////////////////////////////////////////// +/** @file + Implements the special effects of an ability + score falling down to 0 as according to PnP. + + Strength: Lies helpless on ground (knockdown) + Dexterity: Paralyzed + Constitution: Death + Intelligence: Coma (knockdown) + Wisdom: Coma (knockdown) + Charisma: Coma (knockdown) + + + This can be turned off with a switch in + prc_inc_switches : PRC_NO_PNP_ABILITY_DAMAGE + + + NOTE: Due to BioOptimization (tm), Dex reaching + 0 from above 3 when any other stat is already + at 0 will result in Dex being considered + restored at the same time the other stat is. + + This might be workable around, but not + efficiently. +*/ +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 09.04.2005 +//:: Modified On: 25.06.2005 +//::////////////////////////////////////////////// + +/* +[00:55] yup +[00:56] well, something to add +[00:56] if KTTS reduces target to 0 (or would, i know NWN goes to 3) +[00:56] drop a cutscene paralyze on em +[00:56] and a long duration knockdown +[01:00] 'k. And spawn a pseudo-hb on them to do recovery if they ever regain the mental stat +[01:01] Also, test result: You lose spellcasting if your casting stat drops below the required, even if the reduction is a magical penalty +[01:03] you do? cool +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +// Internal function. Called by a threadscript. Handles checking if any ability that has reached 0 has been restored +void AbilityDamageMonitor(); + +// Dex needs special handling due to the way CutsceneParalyze works (sets Dex to 3) +void DoDexCheck(object oCreature, int bFirstPart = TRUE); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "prc_inc_spells" +#include "prc_inc_damage" //functions to apply damage + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +void AbilityDamageMonitor() +{ + object oCreature = OBJECT_SELF; + int nMonitoredAbilities = GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR); + int nEffects = GetLocalInt(oCreature, ABILITY_DAMAGE_SPECIALS); + + if (DEBUG) DoDebug("AbilityDamageMonitor running"); + + // Check each of the monitored abilities + if(nMonitoredAbilities & (1 << ABILITY_STRENGTH)) + { + if(GetAbilityScore(oCreature, ABILITY_STRENGTH) > 3) + { + DeleteLocalInt(oCreature, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_STRENGTH)); + SetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR, GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR) ^ (1 << ABILITY_STRENGTH)); + if (DEBUG) DoDebug("AbilityDamageMonitor Strength healed"); + } + } + /*if(nMonitoredAbilities & (1 << ABILITY_DEXTERITY)) + { + if(GetAbilityScore(oCreature, ABILITY_DEXTERITY) > 3) + { + DeleteLocalInt(oCreature, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_DEXTERITY)); + SetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR, GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR) ^ (1 << ABILITY_DEXTERITY)); + } + }*/ + if(nMonitoredAbilities & (1 << ABILITY_INTELLIGENCE)) + { + if(GetAbilityScore(oCreature, ABILITY_INTELLIGENCE) > 3) + { + DeleteLocalInt(oCreature, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE)); + SetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR, GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR) ^ (1 << ABILITY_INTELLIGENCE)); + if (DEBUG) DoDebug("AbilityDamageMonitor Int healed"); + } + } + if(nMonitoredAbilities & (1 << ABILITY_WISDOM)) + { + if(GetAbilityScore(oCreature, ABILITY_WISDOM) > 3) + { + DeleteLocalInt(oCreature, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_WISDOM)); + SetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR, GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR) ^ (1 << ABILITY_WISDOM)); + if (DEBUG) DoDebug("AbilityDamageMonitor Wis healed"); + } + } + if(nMonitoredAbilities & (1 << ABILITY_CHARISMA)) + { + if(GetAbilityScore(oCreature, ABILITY_CHARISMA) > 3) + { + DeleteLocalInt(oCreature, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_CHARISMA)); + SetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR, GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR) ^ (1 << ABILITY_CHARISMA)); + if (DEBUG) DoDebug("AbilityDamageMonitor Cha healed"); + } + } + + // Check which effects, if any, need to be removed + int bRemovePara, bRemoveKnock; + nMonitoredAbilities = GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR); + if(!(nMonitoredAbilities & (1 << ABILITY_STRENGTH) || + nMonitoredAbilities & (1 << ABILITY_INTELLIGENCE) || + nMonitoredAbilities & (1 << ABILITY_WISDOM) || + nMonitoredAbilities & (1 << ABILITY_CHARISMA) + ) ) + { + // Only remove effects if they are present + if(nEffects & ABILITY_DAMAGE_EFFECT_KNOCKDOWN) + { + bRemoveKnock = TRUE; + nEffects ^= ABILITY_DAMAGE_EFFECT_KNOCKDOWN; + } + if(!(nMonitoredAbilities & (1 << ABILITY_DEXTERITY))) + { + if(nEffects & ABILITY_DAMAGE_EFFECT_PARALYZE) + { + bRemovePara = TRUE; + nEffects ^= ABILITY_DAMAGE_EFFECT_KNOCKDOWN; + } + } + // Dex is the only remaining stat keeping CutscenePara on, so run the dexcheck + else + DelayCommand(0.1f, DoDexCheck(oCreature, TRUE)); + + SetLocalInt(oCreature, ABILITY_DAMAGE_SPECIALS, nEffects); + } + + if (DEBUG) DoDebug("AbilityDamageMonitor bRemovePara:" + IntToString(bRemovePara)); + if (DEBUG) DoDebug("AbilityDamageMonitor bRemoveKnock:" + IntToString(bRemoveKnock)); + + // Do effect removal + if(bRemovePara || bRemoveKnock) + { + effect eCheck = GetFirstEffect(oCreature); + while(GetIsEffectValid(eCheck)) + { + if(bRemovePara && GetEffectType(eCheck) == EFFECT_TYPE_CUTSCENE_PARALYZE){ + if (DEBUG) DoDebug("AbilityDamageMonitor Removed para"); + RemoveEffect(oCreature, eCheck); + } + else if(bRemoveKnock && GetEffectType(eCheck) == 0){ + RemoveEffect(oCreature, eCheck); + if (DEBUG) DoDebug("AbilityDamageMonitor Removed knock"); + } + eCheck = GetNextEffect(oCreature); + } + } + if (DEBUG) DoDebug("AbilityDamageMonitor Monitored abilities:" + IntToString(nMonitoredAbilities)); + + // Stop the thread if there is nothing to monitor anymore + if(!nMonitoredAbilities) + TerminateCurrentThread(); +} + +void DoDexCheck(object oCreature, int bFirstPart = TRUE) +{ + // Remove CutscenePara + if(bFirstPart) + { + effect eCheck = GetFirstEffect(oCreature); + while(GetIsEffectValid(eCheck)) + { + if(GetEffectType(eCheck) == EFFECT_TYPE_CUTSCENE_PARALYZE) + RemoveEffect(oCreature, eCheck); + eCheck = GetNextEffect(oCreature); + } + + DelayCommand(0.1f, DoDexCheck(oCreature, FALSE)); + if (DEBUG) DoDebug("First part ran"); + } + // Check if Dex is over 3 when it's gone + else + { + // It is, so remove Dex from the monitored list + if(GetAbilityScore(oCreature, ABILITY_DEXTERITY) > 3) + { + DeleteLocalInt(oCreature, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_DEXTERITY)); + SetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR, GetLocalInt(oCreature, ABILITY_DAMAGE_MONITOR) ^ (1 << ABILITY_DEXTERITY)); + if (DEBUG) DoDebug("Dex check +"); + } + /*else + SendMessageToPC(GetFirstPC(), "Dex check -");*/ + + // Apply CutscenePara back in either case. Next monitor call will remove it if it's supposed to be gone + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oCreature); + } +} + + + +// Test main +//void main(){} diff --git a/src/include/inc_acp.nss b/src/include/inc_acp.nss new file mode 100644 index 0000000..a117a5e --- /dev/null +++ b/src/include/inc_acp.nss @@ -0,0 +1,98 @@ +#include "prc_inc_switch" + +const int PHENOTYPE_KENSAI = 49; +const int PHENOTYPE_ASSASSIN = 50; +const int PHENOTYPE_BARBARIAN = 51; +const int PHENOTYPE_FENCING = 52; +const int PHENOTYPE_ARCANE = 53; +const int PHENOTYPE_DEMONBLADE = 54; +const int PHENOTYPE_WARRIOR = 55; +const int PHENOTYPE_TIGERFANG = 56; +const int PHENOTYPE_SUNFIST = 57; +const int PHENOTYPE_DRAGONPALM = 58; +const int PHENOTYPE_BEARSCLAW = 59; + +string sLock = "acp_fightingstyle_lock"; + +/* This creates a LocalInt - a "lock" - ..we check further down if it exists... + * if it does, we don't allow phenotype changing. To prevent lag spam. */ +void LockThisFeat() +{ + SetLocalInt(OBJECT_SELF, sLock, TRUE); + float fDelay = IntToFloat(GetPRCSwitch(PRC_ACP_DELAY))*60.0; + if(fDelay == 0.0) + fDelay = 90.0; + if(fDelay == -60.0) + fDelay = 0.0; + DelayCommand(fDelay, DeleteLocalInt(OBJECT_SELF, sLock)); //Lock persists 1 min times switchval +} + +void ResetFightingStyle() //Resets the character phenotype to 0 +{ + int nCurrentPheno = GetPhenoType(OBJECT_SELF); + + //If we are at phenotype 0 or 2, we do nothing. Tell the player that. + if(nCurrentPheno == PHENOTYPE_NORMAL + || nCurrentPheno == PHENOTYPE_BIG) + SendMessageToPC(OBJECT_SELF, "You are already using the default combat style."); + + //else if we are at an ACP phenotype we want to reset it to neutral. + else if(nCurrentPheno == PHENOTYPE_KENSAI + || nCurrentPheno == PHENOTYPE_ASSASSIN + || nCurrentPheno == PHENOTYPE_BARBARIAN + || nCurrentPheno == PHENOTYPE_FENCING + || nCurrentPheno == PHENOTYPE_ARCANE + || nCurrentPheno == PHENOTYPE_DEMONBLADE + || nCurrentPheno == PHENOTYPE_WARRIOR + || nCurrentPheno == PHENOTYPE_TIGERFANG + || nCurrentPheno == PHENOTYPE_SUNFIST + || nCurrentPheno == PHENOTYPE_DRAGONPALM + || nCurrentPheno == PHENOTYPE_BEARSCLAW) + { + SetPhenoType(PHENOTYPE_NORMAL); + LockThisFeat(); // Lock use! + } + + //else, warn that the player doesn't have a phenotype which can be reset right now + else + SendMessageToPC(OBJECT_SELF, "Your phenotype is non-standard and cannot be reset this way."); +} + +void SetCustomFightingStyle(int iStyle) //Sets character phenotype to 5,6,7 or 8 +{ + int nCurrentPheno = GetPhenoType(OBJECT_SELF); + + //Maybe we're already using this fighting style? Just warn the player. + if(nCurrentPheno == iStyle) + SendMessageToPC(OBJECT_SELF, "You're already using this fighting style!"); + + //If we are at phenotype 0 or one of the styles themselves, we go ahead + //and set the creature's phenotype accordingly! (safe thanks to previous 'if') + else if(nCurrentPheno == PHENOTYPE_NORMAL + || nCurrentPheno == PHENOTYPE_KENSAI + || nCurrentPheno == PHENOTYPE_ASSASSIN + || nCurrentPheno == PHENOTYPE_FENCING + || nCurrentPheno == PHENOTYPE_ARCANE + || nCurrentPheno == PHENOTYPE_BARBARIAN + || nCurrentPheno == PHENOTYPE_DEMONBLADE + || nCurrentPheno == PHENOTYPE_WARRIOR + || nCurrentPheno == PHENOTYPE_TIGERFANG + || nCurrentPheno == PHENOTYPE_SUNFIST + || nCurrentPheno == PHENOTYPE_DRAGONPALM + || nCurrentPheno == PHENOTYPE_BEARSCLAW) + { + SetPhenoType(iStyle); + LockThisFeat(); // Lock use! + } + + //At phenotype 2? Tell the player they're too fat! + else if (nCurrentPheno == PHENOTYPE_BIG) + SendMessageToPC(OBJECT_SELF, "You're too fat to use a different fighting style!"); + + //...we didn't fulfil the above conditions? Warn the player. + else + SendMessageToPC(OBJECT_SELF, "Your phenotype is non-standard / Unable to change style"); +} + +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/inc_addragebonus.nss b/src/include/inc_addragebonus.nss new file mode 100644 index 0000000..e317590 --- /dev/null +++ b/src/include/inc_addragebonus.nss @@ -0,0 +1,202 @@ +#include "inc_utility" + +//:: Created by Mr. Bumpkin +//:: Include for all rages +//:: This function gets called right after the attribute bonuses are +//:: Applied. + +// Applies all the bonus damage, to hit, and temporary hp to barbarians who would go over their +// +12 attribute caps by raging. +void GiveExtraRageBonuses(int nDuration, int nStrBeforeRaging, int nConBeforeRaging, int strBonus, int conBonus, int nSave, int nDamageBonusType, object oRager = OBJECT_SELF); + +// Returns the damage type of the weapon held in nInventorySlot by oCreature. If they aren't +// holding a weapon, or the weapon they're holding is a x-bow, sling, shuriken, or dart, it returns +// either the damage type of slashing or bludgeoning, depending on whether they have a prc creature +// slashing weapon or not. It's bludgeoning if they don't have a prc creature slashing weapon. +int GetDamageTypeOfWeapon(int nInventorySlot, object oCreature = OBJECT_SELF); + +// Applies all the bonus damage, to hit, and temporary hp to barbarians who would go over their +// +12 attribute caps by raging. +void GiveExtraRageBonuses(int nDuration, int nStrBeforeRaging, int nConBeforeRaging, int strBonus, int conBonus, int nSave, int nDamageBonusType, object oRager = OBJECT_SELF) +{ + float nDelayed = 0.1; + + int nStrSinceRaging = GetAbilityScore(oRager, ABILITY_STRENGTH); + int nConSinceRaging = GetAbilityScore(oRager, ABILITY_CONSTITUTION); + + + int nStrAdded = nStrSinceRaging - nStrBeforeRaging; + // The amount that was added to the str + int nStrWeWouldAdd = strBonus - nStrAdded; + // The amount we would have to theorhetically add if we wanted to give them the full bonus. + effect eDamage; + effect eToHit; + + if(nStrAdded < strBonus) + { + //int nDamageBonusType = GetDamageTypeOfWeapon(INVENTORY_SLOT_RIGHTHAND, oRager); + + if((nStrSinceRaging/2) * 2 != nStrSinceRaging) + // determine if their current Str right now is odd + { + if((nStrWeWouldAdd/2) * 2 != nStrWeWouldAdd) + // determine if the amount we would theorhetically have to add is odd. + // If so, then we're adding 2 odd numbers together to get an even. Add one to the bonuses + { + //::::: in this event we add nStrWeWouldAdd/2 + 1 + //int nAmountToAdd = nStrWeWouldAdd/2 + 1; + + eDamage = EffectDamageIncrease(nStrWeWouldAdd/2 + 1, nDamageBonusType); + eToHit = EffectAttackIncrease(nStrWeWouldAdd/2 +1); + } + else + { + //::::: in this event we add nStrWeWouldAdd/2 + eDamage = EffectDamageIncrease(nStrWeWouldAdd/2, nDamageBonusType); + eToHit = EffectAttackIncrease(nStrWeWouldAdd/2); + } + } + else + { + //::::: in this event we add nStrWeWouldAdd/2 + eDamage = EffectDamageIncrease(nStrWeWouldAdd/2, nDamageBonusType); + eToHit = EffectAttackIncrease(nStrWeWouldAdd/2); + } + + effect eLink2 = ExtraordinaryEffect(EffectLinkEffects(eDamage, eToHit)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink2, oRager, RoundsToSeconds(nDuration) - nDelayed); + // Applies the damage and toHit effects. I couldn't help myself, so I made the damage type be fire. + + } + // If nStrAdded >= nStrBonus, then no need to add any special bonuses. :) + + int nConAdded = nConSinceRaging - nConBeforeRaging; + // The amount that was added to the Con + int nConWeWouldAdd = conBonus - nConAdded; + // The amount we would have to theorhetically add if we wanted to give them the full bonus. + + + if(nConAdded < conBonus) + { + effect eHitPoints; + effect eHPRemoved; + + int nCharacterLevel = GetHitDice(oRager); + + if((nConSinceRaging/2) * 2 != nConSinceRaging) + // determine if their current Con right now is odd + { + if((nConWeWouldAdd/2) * 2 != nConWeWouldAdd) + // determine if the amount we would theorhetically have to add is odd. + // If so, then we're adding 2 odd numbers together to get an even. Add one to the bonuses + { + //::::: in this event we add nConWeWouldAdd/2 + 1 + + eHitPoints = EffectTemporaryHitpoints((nConWeWouldAdd/2 +1) * nCharacterLevel); + eHPRemoved = EffectDamage(((nConWeWouldAdd/2 +1) * nCharacterLevel), DAMAGE_TYPE_MAGICAL); + // We have to remove the temporary HP at the end of the rage via a damage effect, hehe. + // The damage type will be magical, something to keep in mind of magical resistances exist + // on your module. :) If that's a problem, change the damage type. + + + } + else + { + //::::: in this event we add nConWeWouldAdd/2 + + eHitPoints = EffectTemporaryHitpoints((nConWeWouldAdd/2) * nCharacterLevel); + eHPRemoved = EffectDamage(((nConWeWouldAdd/2) * nCharacterLevel), DAMAGE_TYPE_MAGICAL); + // We have to remove the temporary HP at the end of the rage via a damage effect, hehe. + // The damage type will be magical, something to keep in mind of magical resistances exist + // on your module. :) If that's a problem, change the damage type. + + + } + } + else + { + //::::: in this event we add nConWeWouldAdd/2 + + eHitPoints = EffectTemporaryHitpoints((nConWeWouldAdd/2) * nCharacterLevel); + eHPRemoved = EffectDamage(((nConWeWouldAdd/2) * nCharacterLevel), DAMAGE_TYPE_MAGICAL); + // We have to remove the temporary HP at the end of the rage via a damage effect, hehe. + // The damage type will be magical, something to keep in mind of magical resistances exist + // on your module. :) If that's a problem, change the damage type. + + } + + eHitPoints = ExtraordinaryEffect(eHitPoints); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eHitPoints, oRager, RoundsToSeconds(nDuration)- nDelayed); + + + //::: Had to ditch applying the damage effect, cause it would apply it even if they rested during their + //::: rage, and the resting was what ended it. Pretty Ironic. + //::: If you reactivate it, make sure to have the temporary HP effect last longer than the delay on the damage effect. 8j + + //DelayCommand(RoundsToSeconds(nDuration) - nDelayed, ApplyEffectToObject(DURATION_TYPE_INSTANT, eHPRemoved, oRager)); + // This is really how the temporary hp are going to get removed. I just didn't want to take any chances, + // so I gave the temporary hp a temporary duration. + + } + +// If nConAdded >= nConBonus, then no need to add any special bonuses. :) + + + +}// End of the whole function. + + +// Returns the damage type of the weapon held in nInventorySlot by oCreature. If they aren't +// holding a weapon, or the weapon they're holding is a x-bow, sling, shuriken, or dart, it returns +// either the damage type of slashing or bludgeoning, depending on whether they have a prc creature +// slashing weapon or not. It's bludgeoning if they don't have a prc creature slashing weapon. + +int GetDamageTypeOfWeapon(int nInventorySlot, object oCreature = OBJECT_SELF) +{ + // 2da lookup to see what kind of damage it deals, then find the equivalent constant + int iDamageType = StringToInt(Get2DACache("baseitems","WeaponType",GetBaseItemType(GetItemInSlot(nInventorySlot, oCreature)))); + switch(iDamageType) + { + case 1: return DAMAGE_TYPE_PIERCING; + case 2: return DAMAGE_TYPE_BLUDGEONING; + case 3: return DAMAGE_TYPE_SLASHING; + case 4: return DAMAGE_TYPE_SLASHING; // slashing & piercing... slashing bonus. + case 5: return DAMAGE_TYPE_BLUDGEONING; // bludeoning & piercing... bludeoning bonus. + } + +// If none of the above types got a hit, we must assume the character is unarmed. + + if(GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCreature)) == BASE_ITEM_CSLSHPRCWEAP + || GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature)) == BASE_ITEM_CSLSHPRCWEAP + || GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCreature)) == BASE_ITEM_CSLSHPRCWEAP) + { + return DAMAGE_TYPE_SLASHING; + } + // If they're unarmed and have no creature weapons from a prc, we must assume they are just using their fists. + return DAMAGE_TYPE_BLUDGEONING; + +} + +// function I typed out to add duration to the rage, only to realize happily that there is no need. +// the normal rage function calculates duration as being what it naturally should be, even if there +// were no +12 bonus cap. :) +/* + if(nBonusDuration) + { + effect eStr = EffectAbilityIncrease(ABILITY_CONSTITUTION, nBonus); + effect eCon = EffectAbilityIncrease(ABILITY_STRENGTH, nBonus); + effect eSave = EffectSavingThrowIncrease(SAVING_THROW_WILL, nSave); + effect eAC = EffectACDecrease(2, AC_DODGE_BONUS); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE); + + effect eLink = EffectLinkEffects(eCon, eStr); + eLink = EffectLinkEffects(eLink, eSave); + eLink = EffectLinkEffects(eLink, eAC); + eLink = EffectLinkEffects(eLink, eDur); + SignalEvent(OBJECT_SELF, EventSpellCastAt(OBJECT_SELF, SPELLABILITY_BARBARIAN_RAGE, FALSE)); + //Make effect extraordinary + eLink = ExtraordinaryEffect(eLink); + DelayCommand(RoundsToSeconds(nDuration, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oRager, RoundsToSeconds(nBonusDuration))); + } +*/ \ No newline at end of file diff --git a/src/include/inc_area.nss b/src/include/inc_area.nss new file mode 100644 index 0000000..6eb86f4 --- /dev/null +++ b/src/include/inc_area.nss @@ -0,0 +1,83 @@ +// Moved to a seperate inc to prevent a circular dependency error + +/*********************\ +* Function Prototypes * +\*********************/ + +/** + * This function will get the width of the area passed in. + * + * Created By: Zaddix + * Created On: July 17, 2002 + * Optimized: March , 2003 by Knat + * + * @param oArea The area to get the width of. + * @return The width of oArea, as number of tiles. One tile = 10 meters. + */ +int GetAreaWidth(object oArea); + +/** + * This function will get the height of the area passed in. + * + * Created By: Zaddix + * Created On: July 17, 2002 + * Optimized: March , 2003 by Knat + * + * @param oArea The area to get the height of. + * @return The height of oArea, as number of tiles. One tile = 10 meters. + */ +int GetAreaHeight(object oArea); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + + + +/**********************\ +* Function Definitions * +\**********************/ + +int GetAreaWidth(object oArea) +{ + int nX = GetLocalInt(oArea,"#WIDTH"); + if( nX == 0) + { + int nY = 0; int nColor; + for (nX = 0; nX < 32; ++nX) + { + nColor = GetTileMainLight1Color(Location(oArea, Vector(IntToFloat(nX), 0.0, 0.0), 0.0)); + if (nColor < 0 || nColor > 255) + { + SetLocalInt(oArea,"#WIDTH", nX); + return(nX); + } + } + SetLocalInt(oArea,"#WIDTH", 32); + return 32; + } + else + return nX; +} + +int GetAreaHeight(object oArea) +{ + int nY = GetLocalInt(oArea,"#HEIGHT"); + if( nY == 0) + { + int nX = 0; int nColor; + for (nY=0; nY<32; ++nY) + { + nColor = GetTileMainLight1Color(Location(oArea, Vector(0.0, IntToFloat(nY), 0.0),0.0)); + if (nColor < 0 || nColor > 255) + { + SetLocalInt(oArea,"#HEIGHT",nY); + return(nY); + } + } + SetLocalInt(oArea,"#HEIGHT",32); + return 32; + } + else + return nY; +} diff --git a/src/include/inc_array_sort.nss b/src/include/inc_array_sort.nss new file mode 100644 index 0000000..38ffd56 --- /dev/null +++ b/src/include/inc_array_sort.nss @@ -0,0 +1,380 @@ +//::////////////////////////////////////////////// +//:: Array sorting functions +//:: inc_array_sort +//::////////////////////////////////////////////// +/** @file + A bunch of sorting functions for different + data types. + + TMI may occur if attempting to sort too + large arrays. + For the quicksorts, 100 elements should always + be safe. TMI becomes almost certain past 150 elements. + The counting sort can handle 200 elements, with + 250 being near upper limit. + It is not recommended that one directly use the + insertion sort, but 50 elements are probably safe. + + Array implementation is assumed to follow + certain constraints: + - Array elements begin at index 0 + - The value returned by the function to get + array size returns the number of elements + in the array. + - The index of the last element in the array + is the number of elements - 1. + - Array reads change no stored data + - Array writes are allowed to write over + existing entries. + + @author Ornedan + @data Created 2006.05.27 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/// At a certain point when the sort range is small enough, the overhead of quicksort exceeds +/// the higher order of efficiency in comparison to insertion sort, which has minimal overhead, +/// but still decent performance. At that point, switch to insertion sort. +const int QUICKSORT_TO_INSERTIONSORT_TRESHOLD = 6; // Anything betweem 4 - 10 seems to work. 6 was best in testing with randomly created arrays + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Implements a quicksort for integers. The array given for sorting should only contain + * integer elements. Sane results not guaranteed otherwise. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + * + * @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if + * left to default value (-1). + * @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive + * calls if left to default value (-1). + */ +void QuickSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1); + +/** + * Implements a quicksort for floating point numbers. The array given for sorting should only contain + * float elements. Sane results not guaranteed otherwise. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + * + * @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if + * left to default value (-1). + * @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive + * calls if left to default value (-1). + */ +void QuickSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1); + +/** + * Implements an insertion sort for integers. The array given for sorting should only contain + * integer elements. Sane results not guaranteed otherwise. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + * + * @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if + * left to default value (-1). + * @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive + * calls if left to default value (-1). + */ +void InsertionSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1); + +/** + * Implements an insertion sort for floating point numbers. The array given for sorting should only contain + * float elements. Sane results not guaranteed otherwise. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + * + * @param nLower The lower sort bound. Set by the function itself to 0 for recursive calls if + * left to default value (-1). + * @param nUpper The upper sort bound. Set by the function itself to array size - 1 for recursive + * calls if left to default value (-1). + */ +void InsertionSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1); + +/** + * Implements counting sort for integers. The array given for sorting should only contain + * integer elements. Sane results not guaranteed otherwise. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + */ +void CountingSortInt(object oStore, string sArrayName); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_array" +#include "inc_debug" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Shuffles elements around until they are a bit more in order. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + * @param nLower The lower sort bound of the range to sort. + * @param nUpper The upper sort bound of the range to sort. + * @return The array index around which the array has been "sorted". + */ +int _inc_array_sort_PartitionInt(object oStore, string sArrayName, int nLower, int nUpper) +{ + int nDivider, nSwap; + + // Attempt to determine the median of the values in the array positions nLower, nMid and nUpper + int nLowerVal = array_get_int(oStore, sArrayName, nLower), + nMidVal = array_get_int(oStore, sArrayName, (nLower + nUpper) / 2), + nUpperVal = array_get_int(oStore, sArrayName, nUpper); + if(nLowerVal < nMidVal) + { + if(nLowerVal < nUpperVal) nDivider = (nMidVal < nUpperVal) ? nMidVal : nUpperVal; + else nDivider = nLowerVal; + } + else if(nLowerVal < nUpperVal) nDivider = nLowerVal; + else nDivider = (nMidVal > nUpperVal) ? nMidVal : nUpperVal; + + // Loop to sort the array around the median value + nLower -= 1; nUpper += 1; // Hack - The loops below need to be pre-increment, so we need to move the index variables to make the first elements examined be the ones at the original values + while(TRUE) + { + while(array_get_int(oStore, sArrayName, ++nLower) < nDivider) {} // Seek an element in the lower range of the array that is lesser than the divider + while(array_get_int(oStore, sArrayName, --nUpper) > nDivider) {} // Seek an element in the upper range of the array that is greater than the divider + // If the indexes haven't passed each other yet, swap the elements + if(nLower < nUpper) + { + nSwap = array_get_int(oStore, sArrayName, nLower); + array_set_int(oStore, sArrayName, nLower, array_get_int(oStore, sArrayName, nUpper)); + array_set_int(oStore, sArrayName, nUpper, nSwap); + } + // Otherwise, the array is now arranged so that all elements at positions nUpper and higher are greater than or equal to the + // elements lower in the array + else + return nUpper; + } + + // Never going to reach here, but compiler can't figure that out :P + Assert(FALSE, "FALSE", "Execution reached code that shouldn't be reachable", "inc_array_sort", "_inc_arrays_sort_PartitionInt"); + return -1; +} + +/** Internal function. + * Shuffles elements around until they are a bit more in order. + * + * @param oStore The object the array to sort is stored on + * @param sArrayName The name of the array to sort + * @param nLower The lower sort bound of the range to sort. + * @param nUpper The upper sort bound of the range to sort. + * @return The array index around which the array has been "sorted". + */ +int _inc_array_sort_PartitionFloat(object oStore, string sArrayName, int nLower, int nUpper) +{ + float fDivider, fSwap; + + // Attempt to determine the median of the values in the array positions nLower, nMid and nUpper + float fLowerVal = array_get_float(oStore, sArrayName, nLower), + fMidVal = array_get_float(oStore, sArrayName, (nLower + nUpper) / 2), + fUpperVal = array_get_float(oStore, sArrayName, nUpper); + if(fLowerVal < fMidVal) + { + if(fLowerVal < fUpperVal) fDivider = (fMidVal < fUpperVal) ? fMidVal : fUpperVal; + else fDivider = fLowerVal; + } + else if(fLowerVal < fUpperVal) fDivider = fLowerVal; + else fDivider = (fMidVal > fUpperVal) ? fMidVal : fUpperVal; + + // Loop to sort the array around the median value + nLower -= 1; nUpper += 1; // Hack - The loops below need to be pre-increment, so we need to move the index variables to make the first elements examined be the ones at the original values + while(TRUE) + { + while(array_get_float(oStore, sArrayName, ++nLower) < fDivider) {} // Seek an element in the lower range of the array that is lesser than the divider + while(array_get_float(oStore, sArrayName, --nUpper) > fDivider) {} // Seek an element in the upper range of the array that is greater than the divider + // If the indexes haven't passed each other yet, swap the elements + if(nLower < nUpper) + { + fSwap = array_get_float(oStore, sArrayName, nLower); + array_set_float(oStore, sArrayName, nLower, array_get_float(oStore, sArrayName, nUpper)); + array_set_float(oStore, sArrayName, nUpper, fSwap); + } + // Otherwise, the array is now arranged so that all elements at positions nUpper and higher are greater than or equal to the + // elements lower in the array + else + return nUpper; + } + + // Never going to reach here, but compiler can't figure that out :P + Assert(FALSE, "FALSE", "Execution reached code that shouldn't be reachable", "inc_array_sort", "_inc_array_sort_PartitionFloat"); + return -1; +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void QuickSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1) +{ + // If range limits are not given, initialise them to defaults + if(nLower == -1) nLower = 0; + if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1; + + // See if we have reached the point when quicksort becomes less efficient than insertion sort. + if((nUpper - nLower) <= QUICKSORT_TO_INSERTIONSORT_TRESHOLD) + InsertionSortInt(oStore, sArrayName, nLower, nUpper); + else + { + // Move entries into a slightly more sorted order by arranging them around a median value + // nDivider is the position of the beginning of the range where all the elements are + // greater or equal to the median used + int nDivider = _inc_array_sort_PartitionInt(oStore, sArrayName, nLower, nUpper); + + // Recurse into the halves of the array generated by the above sorting + QuickSortInt(oStore, sArrayName, nLower, nDivider); + QuickSortInt(oStore, sArrayName, nDivider + 1, nUpper); + } +} + +void QuickSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1) +{ + // If range limits are not given, initialise them to defaults + if(nLower == -1) nLower = 0; + if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1; + + // See if we have reached the point when quicksort becomes less efficient than insertion sort. + if((nUpper - nLower) <= QUICKSORT_TO_INSERTIONSORT_TRESHOLD) + InsertionSortFloat(oStore, sArrayName, nLower, nUpper); + else + { + // Move entries into a slightly more sorted order by arranging them around a median value + // nDivider is the position of the beginning of the range where all the elements are + // greater or equal to the median used + int nDivider = _inc_array_sort_PartitionFloat(oStore, sArrayName, nLower, nUpper); + + // Recurse into the halves of the array generated by the above sorting + QuickSortFloat(oStore, sArrayName, nLower, nDivider); + QuickSortFloat(oStore, sArrayName, nDivider + 1, nUpper); + } +} + +void InsertionSortInt(object oStore, string sArrayName, int nLower = -1, int nUpper = -1) +{ + // If range limits are not given, initialise them to defaults + if(nLower == -1) nLower = 0; + if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1; + + // Some variables + int i, nSwap; + + // Run the insertion sort loop + for(nLower += 1; nLower <= nUpper; nLower++) + { + // Store current entry in temporary variable + nSwap = array_get_int(oStore, sArrayName, nLower); + + // Move preceding elements forward by one until we encounter index 0 or an element <= nSwap, + // whereupon we insert the swapped out element + i = nLower; + while(i > 0 && array_get_int(oStore, sArrayName, i - 1) > nSwap) + { + array_set_int(oStore, sArrayName, i, + array_get_int(oStore, sArrayName, i - 1) + ); + i--; + } + + // Insert the swapped out element at the position where all elements with index less than it's are lesser than or equal to it + array_set_int(oStore, sArrayName, i, nSwap); + } +} + +void InsertionSortFloat(object oStore, string sArrayName, int nLower = -1, int nUpper = -1) +{ + // If range limits are not given, initialise them to defaults + if(nLower == -1) nLower = 0; + if(nUpper == -1) nUpper = array_get_size(oStore, sArrayName) - 1; + + // Some variables + int i; + float fSwap; + + // Run the insertion sort loop + for(nLower += 1; nLower <= nUpper; nLower++) + { + // Store current entry in temporary variable + fSwap = array_get_float(oStore, sArrayName, nLower); + + // Move preceding elements forward by one until we encounter index 0 or an element <= nSwap, + // whereupon we insert the swapped out element + i = nLower; + while(i > 0 && array_get_float(oStore, sArrayName, i - 1) > fSwap) + { + array_set_float(oStore, sArrayName, i, + array_get_float(oStore, sArrayName, i - 1) + ); + i--; + } + + // Insert the swapped out element at the position where all elements with index less than it's are lesser than or equal to it + array_set_float(oStore, sArrayName, i, fSwap); + } +} + +void CountingSortInt(object oStore, string sArrayName) +{ + int nMin = 0, nMax = 0, + nCount = 0, + i, size = array_get_size(oStore, sArrayName), + nTemp; + + /* Find the least and greatest elements of the array */ + for(i = 0; i < size; i++) + { + nTemp = array_get_int(oStore, sArrayName, i); + if(nTemp < nMin) nMin = nTemp; + if(nTemp > nMax) nMax = nTemp; + } + + // Create temporary array + string sTempArray = "_CSort"; + while(array_exists(oStore, sTempArray)) sTempArray += "_"; + array_create(oStore, sTempArray); + + // Get the amount of each number in the array + for(i = 0; i < size; i++) + { + nTemp = array_get_int(oStore, sArrayName, i) - nMin; + array_set_int(oStore, sTempArray, nTemp, array_get_int(oStore, sTempArray, nTemp) + 1); + } + + // Set the values in the sortable array + size = array_get_size(oStore, sTempArray); + for(i = 0; i < size; i++) + { + while(array_get_int(oStore, sTempArray, i) > 0) + { + array_set_int(oStore, sArrayName, nCount++, i + nMin); + array_set_int(oStore, sTempArray, i, array_get_int(oStore, sTempArray, i) - 1); + } + } + + // Delete the temporary array + array_delete(oStore, sTempArray); +} + + +// Test main +//void main(){} diff --git a/src/include/inc_cache_setup.nss b/src/include/inc_cache_setup.nss new file mode 100644 index 0000000..a061fa2 --- /dev/null +++ b/src/include/inc_cache_setup.nss @@ -0,0 +1,362 @@ +//:://///////////////////////////////////////////// +//:: 2da cache creation include +//:: inc_cache_setup +//:://///////////////////////////////////////////// + +/** @file + * Creation and setting up of 2da caching databases + * Functions moved from inc_2dacache + * Removed SQL caching using the module, removed + * ability to use bioDB or nwnDB as cache (this is + * slow for single gets). + * + * @author Primogenitor + * moved by fluffyamoeba 2008-4-23 + * @todo Document the constants and functions + */ + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "inc_2dacache" + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +void Cache_Done() +{ + WriteTimestampedLogEntry("2da caching complete"); +} + +void Cache_Class_Feat(int nClass, int nRow = 0) +{ + string sFile = Get2DACache("classes", "FeatsTable", nClass); + if(sFile != "" + && sFile != "****" + && nRow < GetPRCSwitch(FILE_END_CLASS_FEAT)) + { + Get2DACache(sFile, "FeatLabel", nRow); + Get2DACache(sFile, "FeatIndex", nRow); + Get2DACache(sFile, "List", nRow); + Get2DACache(sFile, "GrantedOnLevel", nRow); + Get2DACache(sFile, "OnMenu", nRow); + nRow++; + DelayCommand(0.1, Cache_Class_Feat(nClass, nRow)); + } + else + { + if(nClass == 254) + Cache_Done(); + else + { + DelayCommand(0.1, Cache_Class_Feat(nClass+1)); //need to delay to prevent TMI + } + } +} + +void Cache_Classes(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_CLASSES)) + { + Get2DACache("classes", "Label", nRow); + Get2DACache("classes", "Name", nRow); + Get2DACache("classes", "Plural", nRow); + Get2DACache("classes", "Lower", nRow); + Get2DACache("classes", "Description", nRow); + Get2DACache("classes", "Icon", nRow); + Get2DACache("classes", "HitDie", nRow); + Get2DACache("classes", "AttackBonusTable", nRow); + Get2DACache("classes", "FeatsTable", nRow); + Get2DACache("classes", "SavingThrowTable", nRow); + Get2DACache("classes", "SkillsTable", nRow); + Get2DACache("classes", "BonusFeatsTable", nRow); + Get2DACache("classes", "SkillPointBase", nRow); + Get2DACache("classes", "SpellGainTable", nRow); + Get2DACache("classes", "SpellKnownTable", nRow); + Get2DACache("classes", "PlayerClass", nRow); + Get2DACache("classes", "SpellCaster", nRow); + Get2DACache("classes", "Str", nRow); + Get2DACache("classes", "Dex", nRow); + Get2DACache("classes", "Con", nRow); + Get2DACache("classes", "Wis", nRow); + Get2DACache("classes", "Int", nRow); + Get2DACache("classes", "Cha", nRow); + Get2DACache("classes", "PrimaryAbil", nRow); + Get2DACache("classes", "AlignRestrict", nRow); + Get2DACache("classes", "AlignRstrctType", nRow); + Get2DACache("classes", "InvertRestrict", nRow); + Get2DACache("classes", "Constant", nRow); + Get2DACache("classes", "EffCRLvl01", nRow); + Get2DACache("classes", "EffCRLvl02", nRow); + Get2DACache("classes", "EffCRLvl03", nRow); + Get2DACache("classes", "EffCRLvl04", nRow); + Get2DACache("classes", "EffCRLvl05", nRow); + Get2DACache("classes", "EffCRLvl06", nRow); + Get2DACache("classes", "EffCRLvl07", nRow); + Get2DACache("classes", "EffCRLvl08", nRow); + Get2DACache("classes", "EffCRLvl09", nRow); + Get2DACache("classes", "EffCRLvl10", nRow); + Get2DACache("classes", "EffCRLvl12", nRow); + Get2DACache("classes", "EffCRLvl13", nRow); + Get2DACache("classes", "EffCRLvl14", nRow); + Get2DACache("classes", "EffCRLvl15", nRow); + Get2DACache("classes", "EffCRLvl16", nRow); + Get2DACache("classes", "EffCRLvl17", nRow); + Get2DACache("classes", "EffCRLvl18", nRow); + Get2DACache("classes", "EffCRLvl19", nRow); + Get2DACache("classes", "EffCRLvl20", nRow); + Get2DACache("classes", "PreReqTable", nRow); + Get2DACache("classes", "MaxLevel", nRow); + Get2DACache("classes", "XPPenalty", nRow); + Get2DACache("classes", "ArcSpellLvlMod", nRow); + Get2DACache("classes", "DivSpellLvlMod", nRow); + Get2DACache("classes", "EpicLevel", nRow); + Get2DACache("classes", "Package", nRow); + nRow++; + DelayCommand(0.1, Cache_Classes(nRow)); + } + else + DelayCommand(1.0, Cache_Class_Feat(0)); +} + +void Cache_RacialTypes(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_RACIALTYPES)) + { + Get2DACache("racialtypes", "Label", nRow); + Get2DACache("racialtypes", "Abrev", nRow); + Get2DACache("racialtypes", "Name", nRow); + Get2DACache("racialtypes", "ConverName", nRow); + Get2DACache("racialtypes", "ConverNameLower", nRow); + Get2DACache("racialtypes", "NamePlural", nRow); + Get2DACache("racialtypes", "Description", nRow); + Get2DACache("racialtypes", "Appearance", nRow); + Get2DACache("racialtypes", "StrAdjust", nRow); + Get2DACache("racialtypes", "DexAdjust", nRow); + Get2DACache("racialtypes", "IntAdjust", nRow); + Get2DACache("racialtypes", "ChaAdjust", nRow); + Get2DACache("racialtypes", "WisAdjust", nRow); + Get2DACache("racialtypes", "ConAdjust", nRow); + Get2DACache("racialtypes", "Endurance", nRow); + Get2DACache("racialtypes", "Favored", nRow); + Get2DACache("racialtypes", "FeatsTable", nRow); + Get2DACache("racialtypes", "Biography", nRow); + Get2DACache("racialtypes", "PlayerRace", nRow); + Get2DACache("racialtypes", "Constant", nRow); + Get2DACache("racialtypes", "AGE", nRow); + Get2DACache("racialtypes", "ToolsetDefaultClass", nRow); + Get2DACache("racialtypes", "CRModifier", nRow); + + nRow++; + DelayCommand(0.1, Cache_RacialTypes(nRow)); + } + else + DelayCommand(1.0, Cache_Classes(0)); +} + + +void Cache_Feat(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_FEAT)) + { + Get2DACache("feat", "LABEL", nRow); + Get2DACache("feat", "FEAT", nRow); + Get2DACache("feat", "DESCRIPTION", nRow); + Get2DACache("feat", "MINATTACKBONUS", nRow); + Get2DACache("feat", "MINSTR", nRow); + Get2DACache("feat", "MINDEX", nRow); + Get2DACache("feat", "MININT", nRow); + Get2DACache("feat", "MINWIS", nRow); + Get2DACache("feat", "MINCON", nRow); + Get2DACache("feat", "MINCHA", nRow); + Get2DACache("feat", "MINSPELLLVL", nRow); + Get2DACache("feat", "PREREQFEAT1", nRow); + Get2DACache("feat", "PREREQFEAT2", nRow); + Get2DACache("feat", "GAINMULTIPLE", nRow); + Get2DACache("feat", "EFFECTSSTACK", nRow); + Get2DACache("feat", "ALLCLASSESCANUSE", nRow); + Get2DACache("feat", "CATEGORY", nRow); + Get2DACache("feat", "MAXCR", nRow); + Get2DACache("feat", "SPELLID", nRow); + Get2DACache("feat", "SUCCESSOR", nRow); + Get2DACache("feat", "CRValue", nRow); + Get2DACache("feat", "USESPERDAY", nRow); + Get2DACache("feat", "MASTERFEAT", nRow); + Get2DACache("feat", "TARGETSELF", nRow); + Get2DACache("feat", "OrReqFeat0", nRow); + Get2DACache("feat", "OrReqFeat1", nRow); + Get2DACache("feat", "OrReqFeat2", nRow); + Get2DACache("feat", "OrReqFeat3", nRow); + Get2DACache("feat", "OrReqFeat4", nRow); + Get2DACache("feat", "REQSKILL", nRow); + Get2DACache("feat", "ReqSkillMinRanks", nRow); + Get2DACache("feat", "REQSKILL2", nRow); + Get2DACache("feat", "ReqSkillMinRanks2", nRow); + Get2DACache("feat", "Constant", nRow); + Get2DACache("feat", "TOOLSCATEGORIES", nRow); + Get2DACache("feat", "HostileFeat", nRow); + Get2DACache("feat", "MinLevel", nRow); + Get2DACache("feat", "MinLevelClass", nRow); + Get2DACache("feat", "MaxLevel", nRow); + Get2DACache("feat", "MinFortSave", nRow); + Get2DACache("feat", "PreReqEpic", nRow); + Get2DACache("feat", "ReqAction", nRow); + nRow++; + DelayCommand(0.01, Cache_Feat(nRow)); + } + else + DelayCommand(1.0, Cache_RacialTypes()); +} + +void Cache_Spells(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_SPELLS)) + { + Get2DACache("spells", "Label", nRow); + Get2DACache("spells", "Name", nRow); + Get2DACache("spells", "IconResRef", nRow); + Get2DACache("spells", "School", nRow); + Get2DACache("spells", "Range", nRow); + Get2DACache("spells", "VS", nRow); + Get2DACache("spells", "MetaMagic", nRow); + Get2DACache("spells", "TargetType", nRow); + Get2DACache("spells", "ImpactScript", nRow); + Get2DACache("spells", "Bard", nRow); + Get2DACache("spells", "Cleric", nRow); + Get2DACache("spells", "Druid", nRow); + Get2DACache("spells", "Paladin", nRow); + Get2DACache("spells", "Ranger", nRow); + Get2DACache("spells", "Wiz_Sorc", nRow); + Get2DACache("spells", "Innate", nRow); + Get2DACache("spells", "ConjTime", nRow); + Get2DACache("spells", "ConjAnim", nRow); + Get2DACache("spells", "ConjHeadVisual", nRow); + Get2DACache("spells", "ConjHandVisual", nRow); + Get2DACache("spells", "ConjGrndVisual", nRow); + Get2DACache("spells", "ConjSoundVFX", nRow); + Get2DACache("spells", "ConjSoundMale", nRow); + Get2DACache("spells", "ConjSoundFemale", nRow); + Get2DACache("spells", "CastAnim", nRow); + Get2DACache("spells", "CastTime", nRow); + Get2DACache("spells", "CastHeadVisual", nRow); + Get2DACache("spells", "CastHandVisual", nRow); + Get2DACache("spells", "CastGrndVisual", nRow); + Get2DACache("spells", "CastSound", nRow); + Get2DACache("spells", "Proj", nRow); + Get2DACache("spells", "ProjModel", nRow); + Get2DACache("spells", "ProjType", nRow); + Get2DACache("spells", "ProjSpwnPoint", nRow); + Get2DACache("spells", "ProjSound", nRow); + Get2DACache("spells", "ProjOrientation", nRow); + Get2DACache("spells", "ImmunityType", nRow); + Get2DACache("spells", "ItemImmunity", nRow); + Get2DACache("spells", "SubRadSpell1", nRow); + Get2DACache("spells", "SubRadSpell2", nRow); + Get2DACache("spells", "SubRadSpell3", nRow); + Get2DACache("spells", "SubRadSpell4", nRow); + Get2DACache("spells", "SubRadSpell5", nRow); + Get2DACache("spells", "Category", nRow); + Get2DACache("spells", "Master", nRow); + Get2DACache("spells", "UserType", nRow); + Get2DACache("spells", "SpellDesc", nRow); + Get2DACache("spells", "UseConcentration", nRow); + Get2DACache("spells", "SpontaneouslyCast", nRow); + Get2DACache("spells", "AltMessage", nRow); + Get2DACache("spells", "HostileSetting", nRow); + Get2DACache("spells", "FeatID", nRow); + Get2DACache("spells", "Counter1", nRow); + Get2DACache("spells", "Counter2", nRow); + Get2DACache("spells", "HasProjectile", nRow); + nRow++; + DelayCommand(0.01, Cache_Spells(nRow)); + } + else + DelayCommand(0.1, Cache_Feat()); +} + +void Cache_Portraits(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_PORTRAITS)) + { + Get2DACache("portraits", "BaseResRef", nRow); + Get2DACache("portraits", "Sex", nRow); + Get2DACache("portraits", "Race", nRow); + Get2DACache("portraits", "InanimateType", nRow); + Get2DACache("portraits", "Plot", nRow); + Get2DACache("portraits", "LowGore", nRow); + nRow++; + DelayCommand(0.1, Cache_Portraits(nRow)); + } + else + DelayCommand(1.0, Cache_Spells()); +} + +void Cache_Soundset(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_SOUNDSET)) + { + Get2DACache("soundset", "LABEL", nRow); + Get2DACache("soundset", "RESREF", nRow); + Get2DACache("soundset", "STRREF", nRow); + Get2DACache("soundset", "GENDER", nRow); + Get2DACache("soundset", "TYPE", nRow); + nRow++; + DelayCommand(0.1, Cache_Soundset(nRow)); + } + else + DelayCommand(1.0, Cache_Portraits()); +} + +void Cache_Appearance(int nRow = 0) +{ + if(nRow < GetPRCSwitch(FILE_END_APPEARANCE)) + { + Get2DACache("appearance", "LABEL", nRow); + Get2DACache("appearance", "STRING_REF", nRow); + Get2DACache("appearance", "NAME", nRow); + Get2DACache("appearance", "RACE", nRow); + Get2DACache("appearance", "ENVMAP", nRow); + Get2DACache("appearance", "BLOODCOLR", nRow); + Get2DACache("appearance", "MODELTYPE", nRow); + Get2DACache("appearance", "WEAPONSCALE", nRow); + Get2DACache("appearance", "WING_TAIL_SCALE", nRow); + Get2DACache("appearance", "HELMET_SCALE_M", nRow); + Get2DACache("appearance", "HELMET_SCALE_F", nRow); + Get2DACache("appearance", "MOVERATE", nRow); + Get2DACache("appearance", "WALKDIST", nRow); + Get2DACache("appearance", "RUNDIST", nRow); + Get2DACache("appearance", "PERSPACE", nRow); + Get2DACache("appearance", "CREPERSPACE", nRow); + Get2DACache("appearance", "HEIGHT", nRow); + Get2DACache("appearance", "HITDIST", nRow); + Get2DACache("appearance", "PREFATCKDIST", nRow); + Get2DACache("appearance", "TARGETHEIGHT", nRow); + Get2DACache("appearance", "ABORTONPARRY", nRow); + Get2DACache("appearance", "RACIALTYPE", nRow); + Get2DACache("appearance", "HASLEGS", nRow); + Get2DACache("appearance", "HASARMS", nRow); + Get2DACache("appearance", "PORTRAIT", nRow); + Get2DACache("appearance", "SIZECATEGORY", nRow); + Get2DACache("appearance", "PERCEPTIONDIST", nRow); + Get2DACache("appearance", "FOOTSTEPTYPE", nRow); + Get2DACache("appearance", "SOUNDAPPTYPE", nRow); + Get2DACache("appearance", "HEADTRACK", nRow); + Get2DACache("appearance", "HEAD_ARC_H", nRow); + Get2DACache("appearance", "HEAD_ARC_V", nRow); + Get2DACache("appearance", "HEAD_NAME", nRow); + Get2DACache("appearance", "BODY_BAG", nRow); + Get2DACache("appearance", "TARGETABLE", nRow); + nRow++; + DelayCommand(0.1, Cache_Appearance(nRow)); + } + else + DelayCommand(1.0, Cache_Soundset()); +} + +void Cache_2da_data() +{ + Cache_Appearance(); +} + diff --git a/src/include/inc_debug.nss b/src/include/inc_debug.nss new file mode 100644 index 0000000..e7d7de0 --- /dev/null +++ b/src/include/inc_debug.nss @@ -0,0 +1,218 @@ +//::////////////////////////////////////////////// +//:: Debug include +//:: inc_debug +//::////////////////////////////////////////////// +/** @file + This file contains a debug printing function, the + purpose of which is to be leavable in place in code, + so that debug printing can be centrally turned off + by commenting out the contents of the function. + + Also, an assertion function and related function for + killing script execution. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Globals */ +////////////////////////////////////////////////// + +/** + * Prefix all your debug calls with an if(DEBUG) so that they get stripped away + * during compilation as dead code when this is turned off. + */ +//const int DEBUG = FALSE; +#include "prc_inc_switch" +int DEBUG = GetPRCSwitch(PRC_DEBUG); + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * May print the given string, depending on whether debug printing is needed. + * + * Calls to this function should be guarded by an "if(DEBUG)" clause in order + * to be disableable. + * + * @param sString The string to print + */ +void DoDebug(string sString, object oAdditionalRecipient = OBJECT_INVALID); + +/** + * Kills script execution using the Die() function if the given assertion + * is false. If a message has been given, also prints it using DoDebug(). + * An assertion is something that should always be true if the program + * is functioning correctly. An assertion being false indicates a fatal error. + * + * The format of the string printed when an assertion fails is: + * "Assertion failed: sAssertion\nsMessage; At sScriptName: sFunction" + * + * Calls to this function should be guarded by an "if(DEBUG)" clause in order + * to be disableable. + * + * Example use: + * + * if(DEBUG) Assert(1 == 1, "1 == 1", "Oh noes! Arithmetic processing is b0rked.", "fooscript", "Baz()"); + * + * @param bAssertion The result of some evaluation that should always be true. + * @param sAssertion A string containing the statement evalueated for bAssertion. + * @param sMessage The message to print if bAssertion is FALSE. Will be + * prefixed with "Assertion failed: " when printed. + * If left to default (empty), the message printed will simply + * be "Assertion failed!". + * @param sFileName Name of the script file where the call to this function occurs. + * @param sFunction Name of the function where the call to this function occurs. + */ +void Assert(int bAssertion, string sAssertion, string sMessage = "", string sFileName = "", string sFunction = ""); + +/** + * Kills the execution of the current script by forcing a Too Many Instructions + * error. + * Not recommended for use outside of debugging purposes. Scripts should be able + * to handle expectable error conditions gracefully. + */ +void Die(); + +/** + * Converts data about a given object into a string of the following format: + * "'GetName' - 'GetTag' - 'GetResRef' - ObjectToString" + * + * @param o Object to convert into a string + * @return A string containing identifying data about o + */ +string DebugObject2Str(object o); + +/** + * Converts the given location into a string representation. + * + * @param loc Location to convert into a string + * @return A string representation of loc + */ +string DebugLocation2Str(location loc); + +/** + * Converts the given itemproperty into a string representation. + * + * @param iprop Itemproperty to convert into a string + * @return A string representation of iprop + */ +string DebugIProp2Str(itemproperty iprop); + +/** + * Converts a boolean to a string. Quick debug version. + * @see BooleanToString to use the tlkified one + * + * @param bool The boolean value to convert. 0 is considered false + * and everything else is true. + */ +string DebugBool2String(int bool); + +/** + * Converts the given effect into a string representation. + * + * @param eEffect effect to convert into a string + * @return A string representation of effect + */ +string DebugEffect2String(effect eEffect); + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void DoDebug(string sString, object oAdditionalRecipient = OBJECT_INVALID) +{ + SendMessageToPC(GetFirstPC(), "" + sString + ""); + if(oAdditionalRecipient != OBJECT_INVALID) + SendMessageToPC(oAdditionalRecipient, "" + sString + ""); + WriteTimestampedLogEntry(sString); +} + +void Assert(int bAssertion, string sAssertion, string sMessage = "", string sFileName = "", string sFunction = "") +{ + if(bAssertion == FALSE) + { + //SpawnScriptDebugger(); + string sErr = "Assertion failed: " + sAssertion; + + if(sMessage != "" || sFileName != "" || sFunction != "") + { + sErr += "\n"; + + if(sMessage != "") + sErr += sMessage; + + if(sFileName != "" || sFunction != "") + { + if(sMessage != "") + sErr += "\n"; + + sErr += "At " + sFileName; + + if(sFileName != "" && sFunction != "") + sErr += ": "; + + sErr += sFunction; + } + } + + DoDebug(sErr); + Die(); + } +} + +void Die() +{ + while(TRUE) {;} +} + +string DebugObject2Str(object o) +{ + return o == OBJECT_INVALID ? + "OBJECT_INVALID" : // Special case + "'" + GetName(o) + "' - '" + GetTag(o) + "' - '" + GetResRef(o) + "' - " + ObjectToString(o); +} + +string DebugLocation2Str(location loc) +{ + object oArea = GetAreaFromLocation(loc); + vector vPos = GetPositionFromLocation(loc); + string sX, sY, sZ, sF; + // 3 decimal places and no leading whitespace + sX = FloatToString(vPos.x,0,3); + sY = FloatToString(vPos.y,0,3); + sZ = FloatToString(vPos.z,0,3); + sF = FloatToString(GetFacingFromLocation(loc),0,3); + + return "Area: Name = '" + GetName(oArea) + "', Tag = '" + GetTag(oArea) + "'; Position: (" + sX + ", " + sY + ", " + sZ + ",); Facing: " + sF; +} + +string DebugIProp2Str(itemproperty iprop) +{ + return "Type: " + IntToString(GetItemPropertyType(iprop)) + "; " + + "Subtype: " + IntToString(GetItemPropertySubType(iprop)) + "; " + + "Duration type: " + (GetItemPropertyDurationType(iprop) == DURATION_TYPE_INSTANT ? "DURATION_TYPE_INSTANT" : + GetItemPropertyDurationType(iprop) == DURATION_TYPE_TEMPORARY ? "DURATION_TYPE_TEMPORARY" : + GetItemPropertyDurationType(iprop) == DURATION_TYPE_PERMANENT ? "DURATION_TYPE_PERMANENT" : + IntToString(GetItemPropertyDurationType(iprop))) + "; " + + "Param1: " + IntToString(GetItemPropertyParam1(iprop)) + "; " + + "Param1 value: " + IntToString(GetItemPropertyParam1Value(iprop)) + "; " + + "Cost table: " + IntToString(GetItemPropertyCostTable(iprop)) + "; " + + "Cost table value: " + IntToString(GetItemPropertyCostTableValue(iprop)); +} + +string DebugBool2String(int bool) +{ + return bool ? "True" : "False"; +} + +string DebugEffect2String(effect eEffect) +{ + return "Effect; Type = " + IntToString(GetEffectType(eEffect)) + + ", SpellID: " + IntToString(GetEffectSpellId(eEffect)) + + ", Subtype: " + IntToString(GetEffectSubType(eEffect)) + + ", Duration: " + IntToString(GetEffectDurationType(eEffect)) + + ", Creator: " + GetName(GetEffectCreator(eEffect)); +} \ No newline at end of file diff --git a/src/include/inc_dispel.nss b/src/include/inc_dispel.nss new file mode 100644 index 0000000..6d09d8e --- /dev/null +++ b/src/include/inc_dispel.nss @@ -0,0 +1,1108 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +//:: This is the prc_dispel_magic functions declarations. The functions themselves are at the bottom +//:: of the file. I got tired of circular include statement errors and just decided to make +//:: these both be just one file by adding my text to theirs. +/////////////////////////////////////////////////////////////////////////////////////////////// + +// GZ: Number of spells in GetSpellBreachProtections +const int PRC_SPELLS_MAX_BREACH = 33; + +//:: This is my remake of the spellsDispelMagic found in x0_i0_spells. It's pretty much +//:: identical to the old one except instead of calling the EffectDispelMagicBest() and +//:: EffectDispelMagicAll() scripting functions it calls the ones I've specified in this +//:: file to replace them. +void spellsDispelMagicMod(object oTarget, int nCasterLevel, effect eVis, effect eImpac, int bAll = TRUE, int bBreachSpells = FALSE); + +//:: This is my remake of spellsDispelAoE(). It's very different, and very short. It +//:: takes advantage of the way I've reworked AoE's so that it can simply use the caster +//:: level stored in the AoE and do a proper dispel check against it. +void spellsDispelAoEMod(object oTargetAoE, object oCaster, int nCasterLevel); + +//:: This function is to replace EffectDispelMagicBest(). It goes through the references +//:: in the three arrays that store the caster levels and references to effects on oTarget, +//:: chooses the one with the highest caster level, and attempts to dispel it using the +//:: caster level entry in the array that corresponds to the spell itself. +void DispelMagicBestMod(object oTarget, int nCasterLevel); + +//:: This function is to replace EffectDispelMagicAll(). It goest through all the references +//:: in the three arrays that store the caster levels and references to effects on oTarget, and +//:: attempts a dispel on each of them. It only checks to dispel whole spells, not individual +//:: separate effects one spell may place on a person. +void DispelMagicAllMod(object oTarget, int nCasterLevel); + +//:: This function sorts the 3 arrays in descending order of caster level, so entry 0 is the +//:: highest, and the last entry is the lowest. It only gets called from inside DispelMagicBest() +void SortAll3Arrays(object oTarget); + +//:: This function is just a helper function to include Infestation of Maggots among the list +//:: of spells in effect on oTarget, so it can be sorted with the rest. It's only called by +//:: DispelMagicBest() +void HandleInfestationOfMaggots(object oTarget); + +// This is only meant to be called withing SetAllAoEInts() I've heard terrible stories that +// say if an object is destroyed, it's local variables may remain in place eating up memory +// so I decided I'd better mop up after setting all of these. +void DeleteAllAoEInts(object oAoE); + +// Returns the AoE's entire caster level, including any from prc's as stored in the local variable +int AoECasterLevel(object oAoE = OBJECT_SELF); + +// * Performs a spell breach up to nTotal spell are removed and +// * nSR spell resistance is lowered. nSpellId can be used to override +// * the originating spell ID. If not specified, SPELL_GREATER_SPELL_BREACH +// * is used +void PRCDoSpellBreach(object oTarget, int nTotal, int nSR, int nSpellId = -1); + +// * Performs a spell breach up to nTotal spells are removed and nSR spell +// * resistance is lowered. +int PRCGetSpellBreachProtection(int nLastChecked); + +// * Remove all spell protections of a specific type +int PRCRemoveProtections(int nSpell_ID, object oTarget, int nCount); + +// * Handle dispel magic of AoEs +void spellsDispelAoE(object oTargetAoE, object oCaster, int nCasterLevel); + +// * dispel magic on one or multiple targets. +// * if bAll is set to TRUE, all effects are dispelled from a creature +// * else it will only dispel the best effect from each creature (used for AoE) +// * Specify bBreachSpells to add Mord's Disjunction to the dispel +void spellsDispelMagic(object oTarget, int nCasterLevel, effect eVis, effect eImpac, int bAll = TRUE, int bBreachSpells = FALSE); + +// Mysteries have a -4 when dispelling spells and vice versa +int GetIsMystery(int nSpellId); + +#include "prc_effect_inc" +#include "lookup_2da_spell" +#include "spinc_remeffct" +#include "prcsp_engine" + +////////////////////////////////////////////////////////////////////////////////////////////////////// + + +//:: Copy of the original function with 1 minor change: calls DispelMagicAll() and +//:: DispelMagicBest() instead of EffectDispelMagicAll() and EffectDispelMagicBest() +//:: That is the only change. +//------------------------------------------------------------------------------ +// Attempts a dispel on one target, with all safety checks put in. +//------------------------------------------------------------------------------ +void spellsDispelMagicMod(object oTarget, int nCasterLevel, effect eVis, effect eImpac, int bAll = TRUE, int bBreachSpells = FALSE) +{ + //-------------------------------------------------------------------------- + // Don't dispel magic on petrified targets + // this change is in to prevent weird things from happening with 'statue' + // creatures. Also creature can be scripted to be immune to dispel + // magic as well. + //-------------------------------------------------------------------------- + if (PRCGetHasEffect(EFFECT_TYPE_PETRIFY, oTarget) == TRUE + || GetLocalInt(oTarget, "X1_L_IMMUNE_TO_DISPEL") == 10) + { + return; + } + + effect eDispel; + float fDelay = PRCGetRandomDelay(0.1, 0.3); + int nId = PRCGetSpellId(); + if (GetItemPossessedBy(OBJECT_SELF, "WOL_Aradros") == GetItemInSlot(INVENTORY_SLOT_NECK, OBJECT_SELF) && GetIsObjectValid(GetItemPossessedBy(OBJECT_SELF, "WOL_Aradros"))) nCasterLevel += 1; + + //-------------------------------------------------------------------------- + // Fire hostile event only if the target is hostile... + //-------------------------------------------------------------------------- + + if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF)) + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nId)); + else + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nId, FALSE)); + + //-------------------------------------------------------------------------- + // GZ: Bugfix. Was always dispelling all effects, even if used for AoE + //-------------------------------------------------------------------------- + if (bAll == TRUE ) + { + //:: This is the first of 2 changes I made. + DispelMagicAllMod(oTarget, nCasterLevel); + + // The way it used to get done. + //eDispel = EffectDispelMagicAll(nCasterLevel); + + //---------------------------------------------------------------------- + // GZ: Support for Mord's disjunction + //---------------------------------------------------------------------- + if (bBreachSpells) + { + PRCDoSpellBreach(oTarget, 6, 10, nId); + } + } + else + { + //:: This is the second of the 2 changes I made. + //:: There are no other changes. + DispelMagicBestMod(oTarget, nCasterLevel); + + // The way it used to get done + //eDispel = EffectDispelMagicBest(nCasterLevel); + + if (bBreachSpells) + { + PRCDoSpellBreach(oTarget, 2, 10, nId); + } + } + + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDispel, oTarget)); +} + +///////////////////////////////////////////////////////////////////////////////////// +//:: This one's so small I hope not to lose track of it. + +//:: Replaces the normal spellsDispelAoE +//:: I reworked all AoE's to store their caster level as a local int on themselves, +//:: so it's possible to just do a proper caster level check instead of doing +//:: something complicated. + +void spellsDispelAoEMod(object oTargetAoE, object oCaster, int nCasterLevel) +{ + int ModWeave; + int nBonus = 0; + string SchoolWeave = lookup_spell_school(GetLocalInt(oTargetAoE, "X2_AoE_SpellID")); + int Weave = GetHasFeat(FEAT_SHADOWWEAVE,oCaster)+ GetLocalInt(oCaster, "X2_AoE_SpecDispel"); + if (GetLocalInt(oTargetAoE, " X2_Effect_Weave_ID_") && !Weave) ModWeave = 4; + if (SchoolWeave=="V" ||SchoolWeave=="T" ) ModWeave = 0; + if (GetLocalInt(oTargetAoE, "PRC_Power_DispellingBuffer_Active")) nBonus += 5; + if (GetHasFeat(FEAT_SPELL_GIRDING, oTargetAoE)) nBonus += 2; + if (GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oTargetAoE) >= 1) nBonus += 6; + if (GetItemPossessedBy(oCaster, "WOL_Aradros") == GetItemInSlot(INVENTORY_SLOT_NECK, oCaster) && GetIsObjectValid(GetItemPossessedBy(oCaster, "WOL_Aradros"))) nCasterLevel += 1; + + int iDice = d20(1); +// SendMessageToPC(GetFirstPC(), "Spell :"+ IntToString(PRCGetSpellId())+" T "+GetName(oTargetAoE)); +// SendMessageToPC(GetFirstPC(), "Dispell :"+IntToString(iDice + nCasterLevel)+" vs DC :"+IntToString(11 + GetLocalInt(oTargetAoE, "X2_AoE_Caster_Level")+ModWeave)+" Weave :"+IntToString(ModWeave)+" "+SchoolWeave); + + if(iDice + nCasterLevel >= GetLocalInt(oTargetAoE, "X2_AoE_Caster_Level") + ModWeave + nBonus) + { + DestroyObject(oTargetAoE); + } +} + + /////////////////////////////////////////////////////////////////////////////////// + + +//:: Goes through all the references to effects stored in the 3 variable arrays, +//:: picks the one with the highest caster level (breaking ties by just keeping the +//:: first one it comes to) and then attempts a dispel check on it. +//:: It goes by spell, not spell effect, so a successful check removes all spell +//:: affects from that spell itself. + +void DispelMagicBestMod(object oTarget, int nCasterLevel) +{ + // I *really* want to rewrite this one so that it simply dispels the most useful effect + // instead of just the one with the highest caster level. + // Sure hate to dispel mage armor on somebody who's immune to necromancy. Difficult Decision, these. + + + //:: calls a function to determine whether infestation of maggots is in effect + //:: on the target. If so, it adds it to the 3 arrays so it can be sorted with them. + + HandleInfestationOfMaggots(oTarget); + + //:: calls a function to arrange the values in the 3 arrays in order of highest caster level to lowest + //:: Index 0 will be the highest, and nLastEntry will be the lowest. + + SortAll3Arrays(oTarget); + + int nCurrentEntry; + int nLastEntry = GetLocalInt(oTarget, "X2_Effects_Index_Number"); + + int nEffectSpellID, nEffectCastLevel; + object oEffectCaster = OBJECT_SELF; + int ModWeave; + int nBonus = 0; + + string sSelf = "Dispelled: "; + string sCast = "Dispelled on "+GetName(oTarget)+": "; + + int Weave = GetHasFeat(FEAT_SHADOWWEAVE,OBJECT_SELF)+ GetLocalInt(OBJECT_SELF, "X2_AoE_SpecDispel"); + if (GetLocalInt(oTarget, "PRC_Power_DispellingBuffer_Active")) nBonus += 5; + if (GetHasFeat(FEAT_SPELL_GIRDING, oTarget)) nBonus += 2; + if (GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oTarget) >= 1) nBonus += 6; + nCasterLevel += GetEssentiaInvestedFeat(oEffectCaster, FEAT_SOULTOUCHED_SPELLCASTING); + + for(nCurrentEntry = 0; nCurrentEntry <= nLastEntry; nCurrentEntry++) + { + nEffectSpellID = GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrentEntry)); + oEffectCaster = GetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nCurrentEntry)); + //:: Make sure the effect it refers to is still in place before making it + //:: number one. + if(IsStillRealEffect(nEffectSpellID, oEffectCaster, oTarget)) + { + ModWeave = 0; + string SchoolWeave = lookup_spell_school(nEffectSpellID); + string SpellName = GetStringByStrRef(StringToInt(lookup_spell_name(nEffectSpellID))); + nEffectCastLevel = GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrentEntry)); + if (GetLocalInt(oTarget, " X2_Effect_Weave_ID_"+ IntToString(nCurrentEntry)) && !Weave) ModWeave = 4; + if (SchoolWeave=="V" ||SchoolWeave=="T" ) ModWeave = 0; + + // -4 for Mystery vs Spell and Spell vs Mystery + // If you have no Shadowcasting classes, you've got to be using another magic system to dispel + if ((!GetLevelByClass(CLASS_TYPE_SHADOWCASTER, OBJECT_SELF) && !GetLevelByClass(CLASS_TYPE_SHADOWSMITH, OBJECT_SELF)) && GetIsMystery(nEffectSpellID)) nCasterLevel -= 4; + // Likewise, if you're a Shadowcaster, you get a -4 penalty to dispel non-Mysteries. Magic number is MYST_SHADOWS_FADE + if (PRCGetSpellId() == 18386 && !GetIsMystery(nEffectSpellID)) nCasterLevel -= 4; + + int iDice = d20(1); +// SendMessageToPC(GetFirstPC(), "Spell :"+ IntToString(nEffectSpellID)+" T "+GetName(oTarget)+" C "+GetName(oEffectCaster)); +// SendMessageToPC(GetFirstPC(), "Dispell :"+ IntToString(iDice + nCasterLevel)+" vs DC :"+IntToString(11 + nEffectCastLevel+ModWeave)+" Mod Weave"+IntToString(ModWeave)+" "+SchoolWeave); + if(iDice + nCasterLevel >= 11 + nEffectCastLevel + ModWeave + nBonus) + { + if(nEffectSpellID != SPELL_INFESTATION_OF_MAGGOTS) + {// If it isn't infestation of maggots we remove it one way, if it is, we remove it another. + effect eToDispel = GetFirstEffect(oTarget); + while(GetIsEffectValid(eToDispel)) + { + if(GetEffectSpellId(eToDispel) == nEffectSpellID) + { + if(GetEffectCreator(eToDispel) == oEffectCaster) + { + if(GetEffectSpellId(eToDispel) == INVOKE_RETRIBUTIVE_INVISIBILITY && GetLocalInt(oTarget, "DangerousInvis")) + { + location lTarget = GetLocation(oTarget); + effect eRetrVis = EffectVisualEffect(VFX_IMP_SONIC); + effect eRetrPulse = EffectVisualEffect(VFX_IMP_PULSE_WIND); + effect eRetrStun = EffectStunned(); + effect eDam; + int nDamage; + int nDC; + float fDelay; + int RICasterLvl = GetLocalInt(oTarget, "DangerousInvis"); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eRetrPulse, oTarget); + DeleteLocalInt(oTarget, "DangerousInvis"); + + //Declare the spell shape, size and the location. Capture the first target object in the shape. + object oVictim = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(20.0), lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oVictim)) + { + if (spellsIsTarget(oVictim, SPELL_TARGET_STANDARDHOSTILE, oTarget)) + { + //Fire cast spell at event for the specified target + SignalEvent(oVictim, EventSpellCastAt(oTarget, INVOKE_RETRIBUTIVE_INVISIBILITY)); + //Get the distance between the explosion and the target to calculate delay + fDelay = GetDistanceBetweenLocations(lTarget, GetLocation(oVictim))/20; + if (!PRCDoResistSpell(oTarget, oVictim, RICasterLvl, fDelay)) + { + //Roll damage for each target + nDamage = d6(4); + //nDamage += ApplySpellBetrayalStrikeDamage(oVictim, oTarget, FALSE); + nDC = 16 + GetAbilityModifier(ABILITY_CHARISMA, oTarget); + + //save + if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL)) + { + nDamage = nDamage / 2; + if(GetHasMettle(oTarget, SAVING_THROW_FORT)) nDamage = 0; + } + else + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eRetrStun, oVictim, RoundsToSeconds(1))); + + if(nDamage > 0) + { + //Set the damage effect + eDam = PRCEffectDamage(oVictim, nDamage, DAMAGE_TYPE_SONIC); + // Apply effects to the currently selected target. + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oVictim)); + PRCBonusDamage(oVictim); + //This visual effect is applied to the target object not the location as above. This visual effect + //represents the flame that erupts on the target not on the ground. + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eRetrVis, oVictim)); + } + } + + } + //Select the next target within the spell shape. + oVictim = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(20.0), lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + } + } + else if(GetEffectSpellId(eToDispel) == INVOKE_PAINFUL_SLUMBER_OF_AGES && GetLocalInt(oTarget, "PainfulSleep")) + { + effect eSleepDam = EffectDamage(GetLocalInt(oTarget, "PainfulSleep"), DAMAGE_TYPE_MAGICAL); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eSleepDam, oTarget); + DeleteLocalInt(oTarget, "PainfulSleep"); + RemoveEventScript(oTarget, EVENT_VIRTUAL_ONDAMAGED, "inv_painsleep", FALSE, FALSE); + } + + RemoveEffect(oTarget, eToDispel); + + if (GetLevelByClass(CLASS_TYPE_NOCTUMANCER)) + { + int nExist = GetLocalInt(OBJECT_SELF, "CaptureMagic"); + int nSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + SetLocalInt(OBJECT_SELF, "CaptureMagic", PRCMax(nExist, nSpellLevel/2)); + if (GetLevelByClass(CLASS_TYPE_NOCTUMANCER) >= 10) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectSpellImmunity(nEffectSpellID)), OBJECT_SELF, 60.0); + } + + if(GetSpellId() == INVOKE_VORACIOUS_DISPELLING) + { + //Get spell level + int nEffectSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + //Damage = spell level + effect eSlashDam = EffectDamage(DAMAGE_TYPE_MAGICAL, nEffectSpellLevel); + + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eSlashDam, oTarget); + } + + else if(GetSpellId() == INVOKE_DEVOUR_MAGIC) + { + //Get spell level + int nEffectSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + //HP = 5 * spell level + effect eDracHP = EffectTemporaryHitpoints(5 * nEffectSpellLevel); + + //can't stack HP from multiple dispels + if (GetHasSpellEffect(GetSpellId(),OBJECT_SELF)) + { + PRCRemoveSpellEffects(GetSpellId(), OBJECT_SELF, OBJECT_SELF); + } + + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDracHP, OBJECT_SELF, 60.0); + } + + else if(GetSpellId() == SPELL_SLASHING_DISPEL) + { + //Get spell level + int nEffectSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + //Damage = 2 * spell level + effect eSlashDam = EffectDamage(DAMAGE_TYPE_MAGICAL, 2 * nEffectSpellLevel); + + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eSlashDam, oTarget); + } + + }// end if effect comes from this caster + }// end if effect comes from this spell + eToDispel = GetNextEffect(oTarget); + }// end of while loop + }// end if infestation is not the spell + else + { + DeleteLocalInt(oTarget, "XP2_L_SPELL_CASTER_LVL_" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + // Deleting this variable is what actually ends the spell effect's damage. + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + DeleteLocalInt(oTarget,"XP2_L_SPELL_WEAVE" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + effect eToDispel = GetFirstEffect(oTarget); + while(GetIsEffectValid(eToDispel)) + { + //:: We don't worry about who cast it. A person can only really have one infestation + //:: going on at a time, I think. + if(GetEffectSpellId(eToDispel) == nEffectSpellID) + { + RemoveEffect(oTarget, eToDispel); + }// end if effect comes from this spell + eToDispel = GetNextEffect(oTarget); + }// end of while loop + }// end else + + //:: Since the effect has been removed, delete the references to it. + //:: This will help out the sweeping function when it gets called next (not now, though) + // These are stored for one round for Spell Rebirth + SetLocalInt(oTarget, "TrueSpellRebirthSpellId", GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrentEntry))); + SetLocalInt(oTarget, "TrueSpellRebirthCasterLvl", GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrentEntry))); + DelayCommand(6.0, DeleteLocalInt(oTarget, "TrueSpellRebirthSpellId")); + DelayCommand(6.0, DeleteLocalInt(oTarget, "TrueSpellRebirthCasterLvl")); + + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrentEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrentEntry)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nCurrentEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nCurrentEntry)); + + //:: Display a message to all involved. + SendMessageToPC(OBJECT_SELF, sCast+SpellName); + if (oTarget != OBJECT_SELF) SendMessageToPC(oTarget, sSelf+SpellName); + + //:: If the check was successful, then we're done. + return; + }// end if check is successful. + }// end if is still a valid spell. + else + { + // These are stored for one round for Spell Rebirth + SetLocalInt(oTarget, "TrueSpellRebirthSpellId", GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrentEntry))); + SetLocalInt(oTarget, "TrueSpellRebirthCasterLvl", GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrentEntry))); + DelayCommand(6.0, DeleteLocalInt(oTarget, "TrueSpellRebirthSpellId")); + DelayCommand(6.0, DeleteLocalInt(oTarget, "TrueSpellRebirthCasterLvl")); + + // If it's not a real effect anymore, then delete the variables that refer to it. + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrentEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrentEntry)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nCurrentEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nCurrentEntry)); + }// end of else statement. + + }// end of for loop + + // If we got here, the return function above never ran, so nothing got removed: + SendMessageToPC(OBJECT_SELF, sCast+"None"); + if (oTarget != OBJECT_SELF) SendMessageToPC(oTarget, sSelf+"None"); +} // End Of Function + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +//:: Goes through and tries to remove each and every spell effect, doing a +//:: separate caster level check for each spell. It goes by spell, not spell +//:: effect, so a successful check removes all spell affects from that spell +//:: itself. + +void DispelMagicAllMod(object oTarget, int nCasterLevel) +{ + + int nIndex = 0; + int nEffectSpellID; + int nEffectCasterLevel; + object oEffectCaster = OBJECT_SELF; + int ModWeave; + int nBonus = 0; + + int nLastEntry = GetLocalInt(oTarget, "X2_Effects_Index_Number"); + effect eToDispel; + + string sList, SpellName; + string sSelf = "Dispelled: "; + string sCast = "Dispelled on "+GetName(oTarget)+": "; + + int Weave = GetHasFeat(FEAT_SHADOWWEAVE,OBJECT_SELF)+ GetLocalInt(OBJECT_SELF, "X2_AoE_SpecDispel"); + if (GetLocalInt(oTarget, "PRC_Power_DispellingBuffer_Active")) nBonus += 5; + if (GetHasFeat(FEAT_SPELL_GIRDING, oTarget)) nBonus += 2; + if (GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oTarget) >= 1) nBonus += 6; + nCasterLevel += GetEssentiaInvestedFeat(oEffectCaster, FEAT_SOULTOUCHED_SPELLCASTING); + + //:: Do the dispel check for each and every spell in effect on oTarget. + for(nIndex; nIndex <= nLastEntry; nIndex++) + { + nEffectSpellID = GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nIndex)); + if(GetHasSpellEffect(nEffectSpellID, oTarget)) + { + ModWeave = 0; + string SchoolWeave = lookup_spell_school(nEffectSpellID); + SpellName = GetStringByStrRef(StringToInt(lookup_spell_name(nEffectSpellID))); + nEffectCasterLevel = GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nIndex)); + if (GetLocalInt(oTarget, " X2_Effect_Weave_ID_"+ IntToString(nIndex)) && !Weave) ModWeave = 4; + if (SchoolWeave=="V" ||SchoolWeave=="T" ) ModWeave = 0; + + // -4 for Mystery vs Spell and Spell vs Mystery + // If you have no Shadowcasting classes, you've got to be using another magic system to dispel + if ((!GetLevelByClass(CLASS_TYPE_SHADOWCASTER, OBJECT_SELF) && !GetLevelByClass(CLASS_TYPE_SHADOWSMITH, OBJECT_SELF)) && GetIsMystery(nEffectSpellID)) nCasterLevel -= 4; + // Likewise, if you're a Shadowcaster, you get a -4 penalty to dispel non-Mysteries. Magic number is MYST_SHADOWS_FADE + if (PRCGetSpellId() == 18386 && !GetIsMystery(nEffectSpellID)) nCasterLevel -= 4; + + int iDice = d20(1); + // SendMessageToPC(GetFirstPC(), "Spell :"+ IntToString(nEffectSpellID)+" T "+GetName(oTarget)+" C "+GetName(GetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nIndex)))); + // SendMessageToPC(GetFirstPC(), "Dispell :"+IntToString(iDice + nCasterLevel)+" vs DC :"+IntToString(11 + nEffectCasterLevel+ModWeave)+" Weave :"+IntToString(ModWeave)+" "+SchoolWeave); + + if(iDice + nCasterLevel >= 11 + nEffectCasterLevel + ModWeave + nBonus) + { + sList += SpellName+", "; + oEffectCaster = GetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nIndex)); + + //:: Was going to use this function but upon reading it it became apparent it might not remove + //:: all of the given spells effects, but just one instead. + + //PRCRemoveSpellEffects(nEffectSpellID, oEffectCaster, oTarget); + + //:: If the check is successful, go through and remove all effects originating + //:: from that particular spell. + effect eToDispel = GetFirstEffect(oTarget); + while(GetIsEffectValid(eToDispel)) + { + if(GetEffectSpellId(eToDispel) == nEffectSpellID) + { + if(GetEffectCreator(eToDispel) == oEffectCaster) + { + + if(GetEffectSpellId(eToDispel) == INVOKE_RETRIBUTIVE_INVISIBILITY && GetLocalInt(oTarget, "DangerousInvis")) + { + location lTarget = GetLocation(oTarget); + effect eRetrVis = EffectVisualEffect(VFX_IMP_SONIC); + effect eRetrPulse = EffectVisualEffect(VFX_IMP_PULSE_WIND); + effect eRetrStun = EffectStunned(); + int RICasterLvl = GetLocalInt(oTarget, "DangerousInvis"); + effect eDam; + int nDamage; + float fDelay; + int nDC; + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eRetrPulse, oTarget); + DeleteLocalInt(oTarget, "DangerousInvis"); + + //Declare the spell shape, size and the location. Capture the first target object in the shape. + object oVictim = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(20.0), lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oVictim)) + { + if (spellsIsTarget(oVictim, SPELL_TARGET_STANDARDHOSTILE, oTarget)) + { + //Fire cast spell at event for the specified target + SignalEvent(oVictim, EventSpellCastAt(oTarget, INVOKE_RETRIBUTIVE_INVISIBILITY)); + //Get the distance between the explosion and the target to calculate delay + fDelay = GetDistanceBetweenLocations(lTarget, GetLocation(oVictim))/20; + if (!PRCDoResistSpell(oTarget, oVictim, RICasterLvl, fDelay)) + { + //Roll damage for each target + nDamage = d6(4); + nDamage += ApplySpellBetrayalStrikeDamage(oVictim, oTarget, FALSE); + nDC = 16 + GetAbilityModifier(ABILITY_CHARISMA, oTarget); + + //save + if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL)) + { + nDamage = nDamage / 2; + if(GetHasMettle(oTarget, SAVING_THROW_FORT)) nDamage = 0; + } + else + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eRetrStun, oVictim, RoundsToSeconds(1))); + + if(nDamage > 0) + { + //Set the damage effect + eDam = PRCEffectDamage(oVictim, nDamage, DAMAGE_TYPE_SONIC); + // Apply effects to the currently selected target. + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oVictim)); + PRCBonusDamage(oVictim); + //This visual effect is applied to the target object not the location as above. This visual effect + //represents the flame that erupts on the target not on the ground. + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eRetrVis, oVictim)); + } + } + + } + //Select the next target within the spell shape. + oVictim = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(20.0), lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + } + } + + else if(GetEffectSpellId(eToDispel) == INVOKE_PAINFUL_SLUMBER_OF_AGES && GetLocalInt(oTarget, "PainfulSleep")) + { + effect eSleepDam = EffectDamage(GetLocalInt(oTarget, "PainfulSleep"), DAMAGE_TYPE_MAGICAL); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eSleepDam, oTarget); + DeleteLocalInt(oTarget, "PainfulSleep"); + RemoveEventScript(oTarget, EVENT_VIRTUAL_ONDAMAGED, "inv_painsleep", FALSE, FALSE); + } + + RemoveEffect(oTarget, eToDispel); + + if (GetLevelByClass(CLASS_TYPE_NOCTUMANCER)) + { + int nExist = GetLocalInt(OBJECT_SELF, "CaptureMagic"); + int nSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + SetLocalInt(OBJECT_SELF, "CaptureMagic", PRCMax(nExist, nSpellLevel/2)); + if (GetLevelByClass(CLASS_TYPE_NOCTUMANCER) >= 10) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectSpellImmunity(nEffectSpellID)), OBJECT_SELF, 60.0); + } + + if(GetSpellId() == INVOKE_VORACIOUS_DISPELLING) + { + //Get spell level + int nEffectSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + //Damage = spell level + effect eSlashDam = EffectDamage(DAMAGE_TYPE_MAGICAL, nEffectSpellLevel); + + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eSlashDam, oTarget); + } + + else if(GetSpellId() == INVOKE_DEVOUR_MAGIC) + { + //Get spell level + int nEffectSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + //HP = 5 * spell level + effect eDracHP = EffectTemporaryHitpoints(5 * nEffectSpellLevel); + + //can't stack HP from multiple dispels + if (GetHasSpellEffect(GetSpellId(),OBJECT_SELF)) + { + PRCRemoveSpellEffects(GetSpellId(), OBJECT_SELF, OBJECT_SELF); + } + + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDracHP, OBJECT_SELF, 60.0); + } + + else if(GetSpellId() == SPELL_SLASHING_DISPEL) + { + //Get spell level + int nEffectSpellLevel = StringToInt(Get2DACache("spells", "Innate", nEffectSpellID)); + //Damage = 2 * spell level + effect eSlashDam = EffectDamage(DAMAGE_TYPE_MAGICAL, 2 * nEffectSpellLevel); + + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eSlashDam, oTarget); + } + + //Spell Removal Check + SpellRemovalCheck(oEffectCaster, oTarget); + + }// end if effect comes from this caster + }// end if effect comes from this spell + eToDispel = GetNextEffect(oTarget); + }// end of while loop + + + // These are stored for one round for Spell Rebirth + SetLocalInt(oTarget, "TrueSpellRebirthSpellId", GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nIndex))); + SetLocalInt(oTarget, "TrueSpellRebirthCasterLvl", GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nIndex))); + DelayCommand(6.0, DeleteLocalInt(oTarget, "TrueSpellRebirthSpellId")); + DelayCommand(6.0, DeleteLocalInt(oTarget, "TrueSpellRebirthCasterLvl")); + + // Delete the saved references to the spell's effects. + // This will save some time when reordering things a bit. + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nIndex)); + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nIndex)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nIndex)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nIndex)); + + }// end of if caster check is sucessful + }// end of if oTarget has effects from this spell + }// end of for statement + + + // Additional Code to dispel any infestation of maggots effects. + + // If check to take care of infestation of maggots is in effect. + // with the highest caster level on it right now. + // If it is, we remove it instead of the other effect. + int bHasInfestationEffects = GetLocalInt(oTarget,"XP2_L_SPELL_CASTER_LVL_" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + + if(bHasInfestationEffects) + { + ModWeave =0; + if (GetLocalInt(oTarget, " XP2_L_SPELL_WEAVE" +IntToString (SPELL_INFESTATION_OF_MAGGOTS)) && !Weave) ModWeave = 4; + + if(d20(1) + nCasterLevel >= bHasInfestationEffects + 11 + ModWeave + nBonus) + { + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + DeleteLocalInt(oTarget,"XP2_L_SPELL_CASTER_LVL_" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + effect eToDispel = GetFirstEffect(oTarget); + nEffectSpellID = SPELL_INFESTATION_OF_MAGGOTS; + + SpellName = GetStringByStrRef(StringToInt(lookup_spell_name(nEffectSpellID))); + sList += SpellName+", "; + + while(GetIsEffectValid(eToDispel)) + { + if(GetEffectSpellId(eToDispel) == nEffectSpellID) + { + RemoveEffect(oTarget, eToDispel); + }// end if effect comes from this spell + eToDispel = GetNextEffect(oTarget); + }// end of while loop + }// end if caster level check was a success. + }// end if infestation of maggots is in effect on oTarget/ + + // If the loop to rid the target of the effects of infestation of maggots + // runs at all, this next loop won't because eToDispel has to be invalid for this + // loop to terminate and the other to begin - but it won't begin if eToDispel is + // already invalid :) + + if (sList == "") sList = "None "; + sList = GetStringLeft(sList, GetStringLength(sList) - 2); // truncate the last ", " + + SendMessageToPC(OBJECT_SELF, sCast+sList); + if (oTarget != OBJECT_SELF) SendMessageToPC(oTarget, sSelf+sList); + +}// End of function. + +/////////////////////////////////////////////////////////////////////////////////////////// +//:: Sorts the 3 arrays in order of highest at index 0 on up to lowest at index nLastEntry +////////////////////////////////////////////////////////////////////////////////////////// + +void SortAll3Arrays(object oTarget) +{ + + int nLastEntry = GetLocalInt(oTarget, "X2_Effects_Index_Number"); + int nCurrEntry; + int nCurrEntry2; + int nSpellID, nSpellIDHigh, nCasterLvl, nCasterLvl2, nHighCastLvl, nHighestEntry,iWeave,iWeaveHigh; + object oMaker, oMakerHigh; + + for(nCurrEntry = 0; nCurrEntry <= nLastEntry; nCurrEntry++) + { + nCasterLvl = GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrEntry)); + nHighCastLvl = nCasterLvl; + + for(nCurrEntry2 = nCurrEntry + 1; nCurrEntry2 <= nLastEntry; nCurrEntry2++) + { + nCasterLvl2 = GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrEntry2)); + + if(nCasterLvl2 >= nHighCastLvl) + { + nHighestEntry = nCurrEntry2; + nHighCastLvl = nCasterLvl2; + } + }// End of second for statement. + if(nHighCastLvl != nCasterLvl) + // If the entry we're currently looking at already is the highest caster level left, + // we leave it there. Otherwise we swap the highest level entry with this one. + // nHighCastLvl will still be equal to nCasterLvl unless a higher level effect was found. + { + nSpellID = GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrEntry)); + oMaker = GetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nCurrEntry)); + iWeave = GetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nCurrEntry)); + + nSpellIDHigh = GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nHighestEntry)); + oMakerHigh = GetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nHighestEntry)); + iWeaveHigh = GetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nHighestEntry)); + + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrEntry)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nCurrEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nCurrEntry)); + + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nHighestEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nHighestEntry)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nHighestEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nHighestEntry)); + + /////////////////////////////////////////////////////////////////////////////// + SetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nCurrEntry), nHighCastLvl); + SetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nCurrEntry), nSpellIDHigh); + SetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nCurrEntry), oMakerHigh); + SetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nCurrEntry), iWeaveHigh); + + // + SetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nHighestEntry),nCasterLvl); + SetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nHighestEntry), nSpellID); + SetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nHighestEntry), oMaker); + SetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nHighestEntry), iWeave); + + } // End if the caster levels of the 2 entries are actually different. + }// End of first for statement. +} + +////////////////////////////////////////////////////////////////////////////////////////////// +//:: Finished sorting. +///////////////////////////////////////////////////////////////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////////////// +//:: Infestation of maggots is a special case because it won't end until a local +//:: int is deleted. It must be handled specially. +///////////////////////////////////////////////////////////////////////////////// +void HandleInfestationOfMaggots(object oTarget) +{ + //:: Special to trap an infestation of maggots effect. + int nHasInfestationEffect = GetLocalInt(oTarget, "XP2_L_SPELL_CASTER_LVL_" + IntToString (SPELL_INFESTATION_OF_MAGGOTS)); + + int nLastEntry = GetLocalInt(oTarget, "X2_Effects_Index_Number"); + if(nHasInfestationEffect) + { + // If they have infestation of maggots on them, then add it to the end of the list. + // I would add it during the spell script itself but it might get swept up before the spell has + // really ended, I fear. + nLastEntry++; + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nLastEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nLastEntry)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nLastEntry)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nLastEntry)); + DeleteLocalInt(oTarget, "X2_Effects_Index_Number"); + + SetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nLastEntry), SPELL_INFESTATION_OF_MAGGOTS); + SetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nLastEntry), nHasInfestationEffect); + SetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nLastEntry), GetLocalInt(oTarget, " XP2_L_SPELL_WEAVE" +IntToString (SPELL_INFESTATION_OF_MAGGOTS))); + //:: Won't bother with this one. I think a creature can only have one infestation at a time. + //SetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nLastEntry)); + SetLocalInt(oTarget, "X2_Effects_Index_Number", nLastEntry); + } +} +////////////////////////////////////////////////////////////////////////////////////////////// +//:: End of section to trap infestation of maggots. +//////////////////////////////////////////////////////////////////////////////////////////// + +// Just returns the stored value. +int AoECasterLevel(object oAoE = OBJECT_SELF) +{ + int toReturn = GetLocalInt(oAoE, "X2_AoE_Caster_Level"); + return toReturn; +} + +void PRCDoSpellBreach(object oTarget, int nTotal, int nSR, int nSpellId = -1) +{ + if (nSpellId == -1) + { + nSpellId = SPELL_GREATER_SPELL_BREACH; + } + effect eSR = EffectSpellResistanceDecrease(nSR); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + + effect eVis = EffectVisualEffect(VFX_IMP_BREACH); + int nCnt, nIdx; + if(!GetIsReactionTypeFriendly(oTarget)) + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellId )); + //Search through and remove protections. + while(nCnt <= PRC_SPELLS_MAX_BREACH && nIdx < nTotal) + { + nIdx = nIdx + PRCRemoveProtections(PRCGetSpellBreachProtection(nCnt), oTarget, nCnt); + nCnt++; + } + effect eLink = EffectLinkEffects(eDur, eSR); + //-------------------------------------------------------------------------- + // This can not be dispelled + //-------------------------------------------------------------------------- + eLink = ExtraordinaryEffect(eLink); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(10),TRUE); + } + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + +} + +//------------------------------------------------------------------------------ +// Returns the nLastChecked-nth highest spell on the creature for use in +// the spell breach routines +// Please modify the constatn PRC_SPELLS_MAX_BREACH at the top of this file +// if you change the number of spells. +//------------------------------------------------------------------------------ +int PRCGetSpellBreachProtection(int nLastChecked) +{ + //-------------------------------------------------------------------------- + // GZ: Protections are stripped in the order they appear here + //-------------------------------------------------------------------------- + if(nLastChecked == 1) {return SPELL_GREATER_SPELL_MANTLE;} + else if (nLastChecked == 2){return SPELL_PREMONITION;} + else if(nLastChecked == 3) {return SPELL_SPELL_MANTLE;} + else if(nLastChecked == 4) {return SPELL_SHADOW_SHIELD;} + else if(nLastChecked == 5) {return SPELL_GREATER_STONESKIN;} + else if(nLastChecked == 6) {return SPELL_ETHEREAL_VISAGE;} + else if(nLastChecked == 7) {return SPELL_GLOBE_OF_INVULNERABILITY;} + else if(nLastChecked == 8) {return SPELL_ENERGY_BUFFER;} + else if(nLastChecked == 9) {return 443;} // greater sanctuary + else if(nLastChecked == 10) {return SPELL_MINOR_GLOBE_OF_INVULNERABILITY;} + else if(nLastChecked == 11) {return SPELL_SPELL_RESISTANCE;} + else if(nLastChecked == 12) {return SPELL_STONESKIN;} + else if(nLastChecked == 13) {return SPELL_LESSER_SPELL_MANTLE;} + else if(nLastChecked == 14) {return SPELL_MESTILS_ACID_SHEATH;} + else if(nLastChecked == 15) {return SPELL_MIND_BLANK;} + else if(nLastChecked == 16) {return SPELL_ELEMENTAL_SHIELD;} + else if(nLastChecked == 17) {return SPELL_PROTECTION_FROM_SPELLS;} + else if(nLastChecked == 18) {return SPELL_PROTECTION_FROM_ELEMENTS;} + else if(nLastChecked == 19) {return SPELL_RESIST_ELEMENTS;} + else if(nLastChecked == 20) {return SPELL_DEATH_ARMOR;} + else if(nLastChecked == 21) {return SPELL_GHOSTLY_VISAGE;} + else if(nLastChecked == 22) {return SPELL_ENDURE_ELEMENTS;} + else if(nLastChecked == 23) {return SPELL_SHADOW_SHIELD;} + else if(nLastChecked == 24) {return SPELL_SHADOW_CONJURATION_MAGE_ARMOR;} + else if(nLastChecked == 25) {return SPELL_NEGATIVE_ENERGY_PROTECTION;} + else if(nLastChecked == 26) {return SPELL_SANCTUARY;} + else if(nLastChecked == 27) {return SPELL_MAGE_ARMOR;} + else if(nLastChecked == 28) {return SPELL_STONE_BONES;} + else if(nLastChecked == 29) {return SPELL_SHIELD;} + else if(nLastChecked == 30) {return SPELL_SHIELD_OF_FAITH;} + else if(nLastChecked == 31) {return SPELL_LESSER_MIND_BLANK;} + else if(nLastChecked == 32) {return SPELL_IRONGUTS;} + else if(nLastChecked == 33) {return SPELL_RESISTANCE;} + return nLastChecked; +} + +int PRCRemoveProtections(int nSpell_ID, object oTarget, int nCount) +{ + // Declare major variables + effect eProtection; + int nCnt = 0; + + // Check if the target has any effects from the specified spell ID + if (GetHasSpellEffect(nSpell_ID, oTarget)) + { + // Start looping through all effects on the target + eProtection = GetFirstEffect(oTarget); + while (GetIsEffectValid(eProtection)) + { + // Only remove effects that: + // - Match the given spell ID + // - Are Magical (to comply with Breach spell behavior) + if (GetEffectSpellId(eProtection) == nSpell_ID && + GetEffectSubType(eProtection) == SUBTYPE_MAGICAL) + { + RemoveEffect(oTarget, eProtection); + nCnt++; + } + + // Move to the next effect + eProtection = GetNextEffect(oTarget); + } + } + + // Return 1 if any effects were removed, otherwise 0 + if (nCnt > 0) + { + return 1; + } + else + { + return 0; + } +} + +// This was dispelling Extraordinary, Supernatural & Unyielding effects -Jaysyn + +/* int PRCRemoveProtections(int nSpell_ID, object oTarget, int nCount) +{ + //Declare major variables + effect eProtection; + int nCnt = 0; + if(GetHasSpellEffect(nSpell_ID, oTarget)) + { + //Search through the valid effects on the target. + eProtection = GetFirstEffect(oTarget); + while (GetIsEffectValid(eProtection)) + { + //If the effect was created by the spell then remove it + if(GetEffectSpellId(eProtection) == nSpell_ID) + { + RemoveEffect(oTarget, eProtection); + //return 1; + nCnt++; + } + //Get next effect on the target + eProtection = GetNextEffect(oTarget); + } + } + if(nCnt > 0) + { + return 1; + } + else + { + return 0; + } +} */ + +//------------------------------------------------------------------------------ +// Attempts a dispel on one target, with all safety checks put in. +//------------------------------------------------------------------------------ +void spellsDispelMagic(object oTarget, int nCasterLevel, effect eVis, effect eImpac, int bAll = TRUE, int bBreachSpells = FALSE) +{ + //-------------------------------------------------------------------------- + // Don't dispel magic on petrified targets + // this change is in to prevent weird things from happening with 'statue' + // creatures. Also creature can be scripted to be immune to dispel + // magic as well. + //-------------------------------------------------------------------------- + if (PRCGetHasEffect(EFFECT_TYPE_PETRIFY, oTarget) == TRUE || GetLocalInt(oTarget, "X1_L_IMMUNE_TO_DISPEL") == 10) + { + return; + } + + effect eDispel; + float fDelay = PRCGetRandomDelay(0.1, 0.3); + int nId = PRCGetSpellId(); + + //-------------------------------------------------------------------------- + // Fire hostile event only if the target is hostile... + //-------------------------------------------------------------------------- + if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF)) + { + + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nId)); + } + else + { + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nId, FALSE)); + } + + //-------------------------------------------------------------------------- + // GZ: Bugfix. Was always dispelling all effects, even if used for AoE + //-------------------------------------------------------------------------- + if (bAll == TRUE ) + { + eDispel = EffectDispelMagicAll(nCasterLevel); + //---------------------------------------------------------------------- + // GZ: Support for Mord's disjunction + //---------------------------------------------------------------------- + if (bBreachSpells) + { + PRCDoSpellBreach(oTarget, 6, 10, nId); + } + } + else + { + eDispel = EffectDispelMagicBest(nCasterLevel); + if (bBreachSpells) + { + PRCDoSpellBreach(oTarget, 2, 10, nId); + } + } + + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDispel, oTarget)); +} + +//------------------------------------------------------------------------------ +// Handle Dispelling Area of Effects +// Before adding this AoE's got automatically destroyed. Since NWN does not give +// the required information to do proper dispelling on AoEs, we do some simulated +// stuff here: +// - Base chance to dispel is 25, 50, 75 or 100% depending on the spell +// - Chance is modified positive by the caster level of the spellcaster as well +// - as the relevant ability score +// - Chance is modified negative by the highest spellcasting class level of the +// AoE creator and the releavant ability score. +// Its bad, but its not worse than just dispelling the AoE as the game did until +// now +//------------------------------------------------------------------------------ +void spellsDispelAoE(object oTargetAoE, object oCaster, int nCasterLevel) +{ + object oCreator = GetAreaOfEffectCreator(oTargetAoE); + int nChance; + int nId = PRCGetSpellId(); + int nClassCaster = PRCGetLastSpellCastClass(); + if ( nId == SPELL_LESSER_DISPEL ) + { + nChance = 25; + } + else if ( nId == SPELL_DISPEL_MAGIC) + { + nChance = 50; + } + else if ( nId == SPELL_GREATER_DISPELLING ) + { + nChance = 75; + } + else if ( nId == SPELL_MORDENKAINENS_DISJUNCTION ) + { + nChance = 100; + } + + + nChance += ((nCasterLevel + (GetAbilityScoreForClass(nClassCaster, oCaster)-10)/2) - (GetCasterLevel(oCreator))); // yes this is a sucky stupid hack + + //-------------------------------------------------------------------------- + // the AI does cheat here, because it can not react as well as a player to + // AoE effects. Also DMs are always successful + //-------------------------------------------------------------------------- + if (!GetIsPC(oCaster)) + { + nChance +=30; + } + + if (oCaster == oCreator) + { + nChance = 100; + } + + int nRand = Random(100); + + if ((nRand < nChance )|| GetIsDM(oCaster) || GetIsDMPossessed(oCaster)) + { + FloatingTextStrRefOnCreature(100929,oCaster); // "AoE dispelled" + DestroyObject (oTargetAoE); + } + else + { + FloatingTextStrRefOnCreature(100930,oCaster); // "AoE not dispelled" + } + +} + +int GetIsMystery(int nSpellId) +{ + // They're all next to one another in the 2da + if (nSpellId >= 18352 && nSpellId < 18450) + return TRUE; + return FALSE; +} + +// Test main +//void main(){} diff --git a/src/include/inc_draw.nss b/src/include/inc_draw.nss new file mode 100644 index 0000000..3df5982 --- /dev/null +++ b/src/include/inc_draw.nss @@ -0,0 +1,4241 @@ +/** @file + ============================================= + PENTAGRAMS & SUMMONING CIRCLES v1.45 + ============================================= + gaoneng January 17, 2005 + #include "inc_draw" + + last updated on August 8, 2005 + + modified by Ornedan of PRC to add new parameters: + fDirectionXZ and fDirectionYZ + + Draw geometric forms using a variety of media + ============================================= +*/ + +#include "inc_draw_tools" + +/* + ============================================= + DRAW* PLACE* AND BEAM* FUNCTIONS DECLARATIONS + ============================================= +*/ +// Draws a circle around lCenter +// ============================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius of circle in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the circle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the circle. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawCircle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a spiral around lCenter +// ============================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spiral lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a spring around lCenter +// ============================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a line towards lCenter +// ============================ +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fLength = length of line in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fTime = time in seconds taken to draw the line. DEFAULT : 6.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawLineToCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, int nFrequency=90, float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a line from lCenter +// ========================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fLength = length of line in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fTime = time in seconds taken to draw the line. DEFAULT : 6.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawLineFromCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, int nFrequency=90, float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a polygonal spring around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawPolygonalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a polygonal spiral around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spiral lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawPolygonalSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a polygon around lCenter +// ============================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius of polygon in meters. (1 tile = 10.0m X 10.0m) +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the polygon lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the polygon. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawPolygon(int nDurationType, int nVFX, location lCenter, float fRadius, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a pentacle (five-pointed star) around lCenter +// =================================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius of pentacle in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the pentacle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the pentacle. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawPentacle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a pentaclic spiral around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the pentacle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawPentaclicSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a pentaclic spring around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the pentacle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawPentaclicSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a star spring around lCenter +// ================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of spring in meters. +// fRadiusStartInner = starting inner radius of spring in meters. +// fRadiusEndOuter = ending outer radius of spring in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nSides = number of sides (or points) DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the pentacle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawStarSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a star spiral around lCenter +// ================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of spiral in meters. +// fRadiusStartInner = starting inner radius of spiral in meters. +// fRadiusEndOuter = ending outer radius of spring in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of spring in meters. DEFAULT : 0.0 +// nSides = number of sides (or points) DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the pentacle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawStarSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a star around lCenter +// =========================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of star in meters. (1 tile = 10.0m X 10.0m) +// fRadiusInner = inner radius of star in meters. +// nSides = number of sides (or points) DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the pentacle lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawStar(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a hemisphere around lCenter +// ================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of sphere in meters. DEFAULT : 0.0 +// fHeightStart = starting height of sphere in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of sphere in meters. DEFAULT : 5.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the sphere lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +void DrawHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a perfect sphere around lCenter +// ===================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the sphere lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +void DrawSphere(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a polygonal hemisphere around lCenter +// =========================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of sphere in meters. DEFAULT : 0.0 +// fHeightStart = starting height of sphere in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of sphere in meters. DEFAULT : 5.0 +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the sphere lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +void DrawPolygonalHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a toroidal spring around lCenter +// ====================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the torus in meters. +// fRadiusStartInner = starting inner radius of the torus in meters. +// fRadiusEndOuter = ending outer radius of the torus in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the torus in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fLoopsPerRev = number of loops per revolution. DEFAULT : 36.0 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawToroidalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fLoopsPerRev=36.0f, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a toroidal spiral around lCenter +// ====================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the torus in meters. +// fRadiusStartInner = starting inner radius of the torus in meters. +// fRadiusEndOuter = ending outer radius of the torus in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the torus in meters. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fLoopsPerRev = number of loops per revolution. DEFAULT : 36.0 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawToroidalSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fDuration=0.0f, int nFrequency=90, float fLoopsPerRev=36.0f, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a standard torus around lCenter +// ===================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of the torus in meters. +// fRadiusInner = inner radius of the torus in meters. +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fLoopsPerRev = number of loops per revolution. DEFAULT : 36.0 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the torus. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawTorus(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, float fDuration=0.0f, int nFrequency=90, float fLoopsPerRev=36.0f, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a sinusoidal curve from lCenter +// ===================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = amplitude of curve in meters. +// fLength = horizontal length of curve in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the torus. DEFAULT : 6.0 +// fRotate = the shift in phase in degrees. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawSinusoid(int nDurationType, int nVFX, location lCenter, float fRadius, float fLength, float fDirection=0.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws an elliptical spring around lCenter +// ========================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the ellipse in meters. +// fRadiusStartInner = starting inner radius of the ellipse in meters. +// fRadiusEndOuter = ending outer radius of the ellipse in meters.DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the ellipse in meters.DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the VFX lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawEllipticalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws an elliptical spiral around lCenter +// ========================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the ellipse in meters. +// fRadiusStartInner = starting inner radius of the ellipse in meters. +// fRadiusEndOuter = ending outer radius of the ellipse in meters.DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the ellipse in meters.DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the VFX lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawEllipticalSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws an ellipse around lCenter +// =============================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of the ellipse in meters. +// fRadiusInner = inner radius of the ellipse in meters. +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the VFX lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the ellipse. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawEllipse(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a rhodonea helix around lCenter +// ===================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawRhodoneaSpring(int nDurationType, int nVFX, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a rhodonea around lCenter +// =============================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawRhodonea(int nDurationType, int nVFX, location lCenter, float fRadius, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a hypocycloid helix around lCenter +// ======================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawHypocycloidSpring(int nDurationType, int nVFX, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a hypocycloid around lCenter +// ================================== +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawHypocycloid(int nDurationType, int nVFX, location lCenter, float fRadius, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a epicycloid helix around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawEpicycloidSpring(int nDurationType, int nVFX, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Draws a epicycloid around lCenter +// ================================= +// nDurationType = DURATION_TYPE_* constant +// nVFX = the VFX_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the spring lasts before fading. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more effects are +// generated and the closer they are to each other. DEFAULT : 90 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +void DrawEpicycloid(int nDurationType, int nVFX, location lCenter, float fRadius, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f); + +// Places a circle around lCenter +// ============================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius of circle in meters. (1 tile = 10.0m X 10.0m) +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the circle. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceCircle(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a spiral around lCenter +// ============================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceSpiral(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a spring around lCenter +// ============================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a line towards lCenter +// ============================= +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fLength = length of line in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fTime = time in seconds taken to draw the line. DEFAULT : 12.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceLineToCenter(string sTemplate, location lCenter, float fLength, float fDirection=0.0f, int nFrequency=60, float fTime=12.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a line from lCenter +// ========================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fLength = length of line in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fTime = time in seconds taken to draw the line. DEFAULT : 12.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceLineFromCenter(string sTemplate, location lCenter, float fLength, float fDirection=0.0f, int nFrequency=60, float fTime=12.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a polygonal spring around lCenter +// ======================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePolygonalSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a polygonal spiral around lCenter +// ======================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePolygonalSpiral(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nSides=3, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a polygon around lCenter +// =============================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius of polygon in meters. (1 tile = 10.0m X 10.0m) +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// nFrequency = number of points, the higher the frequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the polygon. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePolygon(string sTemplate, location lCenter, float fRadius, int nSides=3, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a pentacle (five-pointed star) around lCenter +// ==================================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius of pentacle in meters. (1 tile = 10.0m X 10.0m) +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the pentacle. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePentacle(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a pentaclic spiral around lCenter +// ======================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePentaclicSpiral(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a pentaclic spring around lCenter +// ======================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePentaclicSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a star spring around lCenter +// =================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of spring in meters. +// fRadiusStartInner = starting inner radius of spring in meters. +// fRadiusEndOuter = ending outer radius of spring in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nSides = number of sides (or points) DEFAULT : 3 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceStarSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a star spiral around lCenter +// =================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of spiral in meters. +// fRadiusStartInner = starting inner radius of spiral in meters. +// fRadiusEndOuter = ending outer radius of spring in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of spring in meters. DEFAULT : 0.0 +// nSides = number of sides (or points) DEFAULT : 3 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceStarSpiral(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a star around lCenter +// ============================ +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of star in meters. (1 tile = 10.0m X 10.0m) +// fRadiusInner = inner radius of star in meters. +// nSides = number of sides (or points) DEFAULT : 3 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceStar(string sTemplate, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a hemisphere around lCenter +// ================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of sphere in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the sphere in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the sphere in meters. DEFAULT : 5.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a perfect sphere around lCenter +// ====================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceSphere(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a polygonal hemisphere around lCenter +// ============================================ +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of sphere in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the sphere in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the sphere in meters. DEFAULT : 5.0 +// nSides = number of sides. nSides < 3 will default to 3. DEFAULT : 3 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlacePolygonalHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a toroidal spring around lCenter +// ======================================= +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the torus in meters. +// fRadiusStartInner = starting inner radius of the torus in meters. +// fRadiusEndOuter = ending outer radius of the torus in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the torus in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fLoopsPerRev = number of loops per revolution. DEFAULT : 36.0 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceToroidalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a toroidal spiral around lCenter +// ======================================= +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the torus in meters. +// fRadiusStartInner = starting inner radius of the torus in meters. +// fRadiusEndOuter = ending outer radius of the torus in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the torus in meters. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fLoopsPerRev = number of loops per revolution. DEFAULT : 36.0 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceToroidalSpiral(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a standard torus around lCenter +// ====================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of the torus in meters. +// fRadiusInner = inner radius of the torus in meters. +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fLoopsPerRev = number of loops per revolution. DEFAULT : 36.0 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the torus. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceTorus(string sTemplate, location lCenter, float fRadiusOuter, float fRadiusInner, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a sinusoidal curve from lCenter +// ====================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = amplitude of curve in meters. +// fLength = horizontal length of curve in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of curve respective to normal. DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the torus. DEFAULT : 12.0 +// fRotate = the shift in phase in degrees. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceSinusoid(string sTemplate, location lCenter, float fRadius, float fLength, float fDirection=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places an elliptical spring around lCenter +// ========================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the ellipse in meters. +// fRadiusStartInner = starting inner radius of the ellipse in meters. +// fRadiusEndOuter = ending outer radius of the ellipse in meters.DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the ellipse in meters.DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceEllipticalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places an elliptical spiral around lCenter +// ========================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of the ellipse in meters. +// fRadiusStartInner = starting inner radius of the ellipse in meters. +// fRadiusEndOuter = ending outer radius of the ellipse in meters.DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of the ellipse in meters.DEFAULT : 0.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceEllipticalSpiral(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places an ellipse around lCenter +// ================================ +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of the ellipse in meters. +// fRadiusInner = inner radius of the ellipse in meters. +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the ellipse. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceEllipse(string sTemplate, location lCenter, float fRadiusOuter, float fRadiusInner, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a stella octangula above lCenter +// ======================================= +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fTime = time in seconds taken to draw the polyhedron. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceStellaOctangula(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a regular icosahedron above lCenter +// ========================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fTime = time in seconds taken to draw the polyhedron. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceIcosahedron(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a rhodonea helix around lCenter +// ====================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceRhodoneaSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a rhodonea around lCenter +// ================================ +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceRhodonea(string sTemplate, location lCenter, float fRadius, float fRoulette=3.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a hypocycloid helix around lCenter +// ========================================= +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceHypocycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a hypocycloid around lCenter +// =================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceHypocycloid(string sTemplate, location lCenter, float fRadius, float fRoulette=3.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a epicycloid helix around lCenter +// ======================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceEpicycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Places a epicycloid around lCenter +// ================================== +// sTemplate = blueprint resref of placeable to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fRoulette = arbitrary constant, affects number of petals. DEFAULT : 3.0 +// nFrequency = number of points, the higher nFrequency, the more placeables +// are created and the closer they are to each other. DEFAULT : 60 +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 12.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeables. Default invalid effect. DEFAULT : -1 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait = time in seconds to wait before applying visual effect. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeables get destroyed. DEFAULT : 0.0 +void PlaceEpicycloid(string sTemplate, location lCenter, float fRadius, float fRoulette=3.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f); + +// Beams a polygonal hemisphere around lCenter +// =========================================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of sphere in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of sphere in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the sphere in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the sphere in meters. DEFAULT : 5.0 +// nSides = number of sides. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the sphere. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the central/normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPolygonalHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a polygonal spring around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nSides = number of sides. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPolygonalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a polygonal spiral around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// nSides = number of sides. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPolygonalSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a polygon around lCenter +// ============================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius of polygon in meters. (1 tile = 10.0m X 10.0m) +// nSides = number of sides. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the polygon. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPolygon(int nDurationType, int nVFX, location lCenter, float fRadius, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a star around lCenter +// =========================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusOuter = outer radius of star in meters. (1 tile = 10.0m X 10.0m) +// fRadiusInner = inner radius of star in meters. +// nSides = number of sides (or points) DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the pentacle. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamStar(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a star spring around lCenter +// ================================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of spring in meters. +// fRadiusStartInner = starting inner radius of spring in meters. +// fRadiusEndOuter = ending outer radius of spring in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// nSides = number of sides (or points) DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the pentacle. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamStarSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a star spiral around lCenter +// ================================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStartOuter = starting outer radius of spring in meters. +// fRadiusStartInner = starting inner radius of spring in meters. +// fRadiusEndOuter = ending outer radius of spiral in meters. DEFAULT : 0.0 +// fRadiusEndInner = ending inner radius of spiral in meters. DEFAULT : 0.0 +// nSides = number of sides (or points) DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the pentacle. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamStarSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a pentacle around lCenter +// =============================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius of pentacle in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the pentacle. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPentacle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a pentaclic spiral around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spiral in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spiral in meters. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 1.0 +// fTime = time in seconds taken to draw the spiral. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPentaclicSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a pentaclic spring around lCenter +// ======================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of spring in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of spring in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the spring in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the spring in meters. DEFAULT : 5.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fRev = number of revolutions. DEFAULT : 5.0 +// fTime = time in seconds taken to draw the spring. DEFAULT : 6.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamPentaclicSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a line from lCenter +// ========================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fLength = length of line in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beam lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use.. DEFAULT : "" +// fTime = time in seconds taken to draw the line. DEFAULT : 6.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamLineFromCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a line to lCenter +// ======================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fLength = length of line in meters. (1 tile = 10.0m X 10.0m) +// fDirection = direction of line respective to normal. DEFAULT : 0.0 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beam lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to draw the line. DEFAULT : 6.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamLineToCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a stella octangula above lCenter +// ====================================== +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to create the placeable nodes. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamStellaOctangula(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a regular icosahedron above lCenter +// ========================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to create the placeable nodes. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamIcosahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a regular dodecahedron above lCenter +// ========================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to create the placeable nodes. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamDodecahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a rhombic triacontahedron above lCenter +// ============================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to create the placeable nodes. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamTriacontahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a cuboctahedron above lCenter +// ============================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to create the placeable nodes. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamCuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a small rhombicuboctahedron above lCenter +// ============================================= +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadius = radius in meters. (1 tile = 10.0m X 10.0m) +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to create the placeable nodes. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamSmallRhombicuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +// Beams a gengon above lCenter +// ============================ +// nDurationType = DURATION_TYPE_* constant. +// nVFX = the VFX_BEAM_* constant to use. +// lCenter = the location of the center. +// fRadiusStart = starting radius of gengon in meters. (1 tile = 10.0m X 10.0m) +// fRadiusEnd = ending radius of gengon in meters. DEFAULT : 0.0 +// fHeightStart = starting height of the gengon in meters. DEFAULT : 0.0 +// fHeightEnd = ending height of the gengon in meters. DEFAULT : 5.0 +// nSides = number of sides. DEFAULT : 3 +// fDuration = if nDurationType is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the beams lasts before fading. DEFAULT : 0.0 +// sTemplate = blueprint resref of placeable to use. DEFAULT : "" +// fTime = time in seconds taken to draw the gengon. DEFAULT : 6.0 +// fWait = time in seconds to wait before applying the beams. DEFAULT : 1.0 +// fRotate = the angle of rotation respective to normal. DEFAULT : 0.0 +// fTwist = rotational displacement of end polygon in degrees. DEFAULT : 0.0 +// sAxis = ("x", "y" or "z") the normal axis. DEFAULT : "z" +// nDurationType2 = DURATION_TYPE_* constant if an additional visual effect is +// to be applied. Default invalid duration. DEFAULT : -1 +// nVFX2 = the VFX_* constant to use if an additional visual effect is to be +// applied to the placeable nodes. Default invalid effect. DEFAULT : -1 +// fDuration2 = if nDurationType2 is DURATION_TYPE_TEMPORARY, this is the number +// of seconds the effect lasts before fading. DEFAULT : 0.0 +// fWait2 = time in seconds to wait before applying nVFX2. DEFAULT : 1.0 +// fLifetime = if fLifetime is not 0.0, then this is time in seconds before the +// placeable nodes get destroyed. DEFAULT : 0.0 +void BeamGengon(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, float fTwist=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f); + +/* + ============================================= + OBJECT-RETURNING FUNCTIONS DECLARATIONS + ============================================= +*/ +// Object-returning equivalent of the void-returning functions +// sTag = tag of oData (the data storage invisible object) + +object ObjectPlaceSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_SPRING"); +object ObjectPlacePolygonalSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_POLYGONALSPRING"); +object ObjectPlacePentaclicSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_PENTACLICSPRING"); +object ObjectPlaceStarSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_STARSPRING"); +object ObjectPlaceHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_HEMISPHERE"); +object ObjectPlacePolygonalHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_POLYGONALHEMISPHERE"); +object ObjectPlaceSinusoid(string sTemplate, location lCenter, float fRadius, float fLength, float fDirection=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_SINUSOID"); +object ObjectPlaceToroidalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_TOROIDALSPRING"); +object ObjectPlaceEllipticalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_ELLIPTICALSPRING"); +object ObjectPlaceStellaOctangula(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_STELLAOCTANGULA"); +object ObjectPlaceIcosahedron(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_ICOSAHEDRON"); +object ObjectPlaceRhodoneaSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_RHODONEASPRING"); +object ObjectPlaceHypocycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_HYPOCYCLOIDSPRING"); +object ObjectPlaceEpicycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_EPICYCLOIDSPRING"); +object ObjectBeamPolygonalHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_POLYGONALHEMISPHERE"); +object ObjectBeamPolygonalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_POLYGONALSPRING"); +object ObjectBeamPolygon(int nDurationType, int nVFX, location lCenter, float fRadius, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_POLYGON"); +object ObjectBeamStar(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_STAR"); +object ObjectBeamStarSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_STARSPRING"); +object ObjectBeamPentacle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_PENTACLE"); +object ObjectBeamPentaclicSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_PENTACLICSPRING"); +object ObjectBeamLineFromCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_LINEFROM"); +object ObjectBeamLineToCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_LINETO"); +object ObjectBeamStellaOctangula(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_STELLAOCTANGULA"); +object ObjectBeamIcosahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_ICOSAHEDRON"); +object ObjectBeamDodecahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_DODECAHEDRON"); +object ObjectBeamTriacontahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_TRIACONTAHEDRON"); +object ObjectBeamCuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_CUBOCTAHEDRON"); +object ObjectBeamSmallRhombicuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_SMALLRHOMBICUBOCTAHEDRON"); +object ObjectBeamGengon(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, float fTwist=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_GENGON"); + +/* + ============================================= + PRIVATE FUNCTIONS DECLARATIONS + ============================================= +*/ +// Draws a straight line from vOne to vTwo +// void DrawLineFromVectorToVector(int nDurationType, int nVFX, object oArea, vector vOne, vector vTwo, float fDuration, int nFrequency, float fTime); + +// Places a straight line from vOne to vTwo, automatically applies VFX if any +// void PlaceLineFromVectorToVector(string sTemplate, object oArea, vector vOne, vector vTwo, int nFrequency, float fTime, int nDurationType, int nVFX, float fDuration, float fWait, float fLifetime, object oData); + +// Delayable CreateObject, automatically applies VFX if any, and sets created object as oData's local object using auto-generated number if fLifetime > 0.0 +// void gao_ActionCreateObject(string sTemplate, location lLocation, int nDurationType, int nVFX, float fDuration, float fWait, float fLifetime, object oData); + +// Delayable CreateObject, automatically sets created object as oData's local object and applies VFX if any +// void gao_ActionCreateLocalObject(string sTemplate, location lLocation, string sNumber, object oData, int nDurationType, int nVFX, float fDuration, float fWait, float fLifetime); + +// Apply EffectBeam between two of oData's local objects +// void gao_ActionApplyLocalBeamEffect(object oData, string sNumber1, string sNumber2, int nDurationType, int nVFX, float fDuration); + +// Return properly rotated vector +//vector gao_RotateVector(vector vCenter, string sAxis, float x, float y, float z, float fRotateXZ, float fRotateYZ); + +/* + ============================================= + FUNCTIONS IMPLEMENTATIONS + ============================================= +*/ + +/* + ============================================= + PRIVATE FUNCTIONS + ============================================= +*/ + +void gao_ActionCreateObject(string sTemplate, location lLocation, int nDurationType, int nVFX, float fDuration, float fWait, float fLifetime, object oData) +{ + object oPlaceable = CreateObject(OBJECT_TYPE_PLACEABLE, sTemplate, lLocation); + if (nDurationType >= 0 && nVFX >= 0) DelayCommand(fWait, ApplyEffectToObject(nDurationType, EffectVisualEffect(nVFX), oPlaceable, fDuration)); + if (fLifetime > 0.0) DestroyObject(oPlaceable, fLifetime); + else // if display is permanent, then start storing the objects as local to ease garbage collection later on. + { // code modified from Ornedan's modification of the original function + int i = GetLocalInt(oData, "storetotal"); + AssignCommand(oPlaceable, ActionDoCommand(SetLocalObject(oData, "store" + IntToString(i), oPlaceable))); + SetLocalInt(oData, "storetotal", i+1); + } +} + +void gao_ActionCreateLocalObject(string sTemplate, location lLocation, string sNumber, object oData, int nDurationType, int nVFX, float fDuration, float fWait, float fLifetime) +{ + object oObject = CreateObject(OBJECT_TYPE_PLACEABLE, sTemplate, lLocation); + AssignCommand(oObject, ActionDoCommand(SetLocalObject(oData, sNumber, oObject))); + if (nDurationType >= 0 && nVFX >= 0) DelayCommand(fWait, ApplyEffectToObject(nDurationType, EffectVisualEffect(nVFX), oObject, fDuration)); + if (fLifetime > 0.0) DestroyObject(oObject, fLifetime); +} + +void gao_ActionApplyLocalBeamEffect(object oData, string sNumber1, string sNumber2, int nDurationType, int nVFX, float fDuration) +{ + object oNode1 = GetLocalObject(oData, sNumber1); + object oNode2 = GetLocalObject(oData, sNumber2); + ApplyEffectToObject(nDurationType, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode2, fDuration); +} + +void DrawLineFromVectorToVector(int nDurationType, int nVFX, object oArea, vector vOne, vector vTwo, float fDuration, int nFrequency, float fTime) +{ + int i; + if (nFrequency < 1) nFrequency = 1; + vector vResultant = vTwo - vOne; + vector vUnit = VectorNormalize(vResultant); + float fDelay = fTime/IntToFloat(nFrequency); + float fLength = VectorMagnitude(vResultant); + float fDelta = fLength/IntToFloat(nFrequency); // distance between each node + float fAngle = VectorToAngle(vUnit); + location lPos; + float f; + for (i=0; i 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDecay = (fRadiusStart - fRadiusEnd)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2; + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fEta*f + fRotate; + fAngle2 = fEta*g + fRotate; + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*f)*cos(fAngle1), (fRadiusStart - fDecay*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*g)*cos(fAngle2), (fRadiusStart - fDecay*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + DelayCommand(f*fDelayPerSide, DrawLineFromVectorToVector(nDurationType, nVFX, oArea, vPos1, vPos2, fDuration, nFrequencyPerSide, fDelayPerSide)); + } +} + +void DrawPolygonalSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawPolygonalSpring(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, nSides, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawPolygon(int nDurationType, int nVFX, location lCenter, float fRadius, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawPolygonalSpring(nDurationType, nVFX, lCenter, fRadius, fRadius, 0.0, 0.0, nSides, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawPentaclicSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + if (fRev == 0.0) fRev = 5.0; + float fSidesToDraw = (fRev > 0.0) ? fRev*5.0 : -fRev*5.0; + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDecay = (fRadiusStart - fRadiusEnd)/fSidesToDraw; // change in radius per node + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per node + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2; + float fStarangle = (fRev > 0.0) ? 144.0 : -144.0; + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fStarangle*f + fRotate; + fAngle2 = fStarangle*g + fRotate; + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*f)*cos(fAngle1), (fRadiusStart - fDecay*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*g)*cos(fAngle2), (fRadiusStart - fDecay*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + DelayCommand(f*fDelayPerSide, DrawLineFromVectorToVector(nDurationType, nVFX, oArea, vPos1, vPos2, fDuration, nFrequencyPerSide, fDelayPerSide)); + } +} + +void DrawPentaclicSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawPentaclicSpring(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawPentacle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawPentaclicSpring(nDurationType, nVFX, lCenter, fRadius, fRadius, 0.0, 0.0, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawStarSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i, toggle; + if (nSides < 2) nSides = 3; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + if (fRev == 0.0) fRev = 5.0; + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides*2) : -fRev*IntToFloat(nSides*2); + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDecayInner = (fRadiusStartInner - fRadiusEndInner)/fSidesToDraw; // change in radius per node + float fDecayOuter = (fRadiusStartOuter - fRadiusEndOuter)/fSidesToDraw; // change in radius per node + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per node + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2; + float fStarangle = (fRev > 0.0) ? 360.0/IntToFloat(nSides*2) : -360.0/IntToFloat(nSides*2); + for (i = 0; i < nSidesToDraw; i++) + { + toggle ^= 1; + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fStarangle*f + fRotate; + fAngle2 = fStarangle*g + fRotate; + if (!toggle) + { + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStartInner - fDecayInner*f)*cos(fAngle1), (fRadiusStartInner - fDecayInner*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStartOuter - fDecayOuter*g)*cos(fAngle2), (fRadiusStartOuter - fDecayOuter*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + } + else + { + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStartOuter - fDecayOuter*f)*cos(fAngle1), (fRadiusStartOuter - fDecayOuter*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStartInner - fDecayInner*g)*cos(fAngle2), (fRadiusStartInner - fDecayInner*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + } + DelayCommand(f*fDelayPerSide, DrawLineFromVectorToVector(nDurationType, nVFX, oArea, vPos1, vPos2, fDuration, nFrequencyPerSide, fDelayPerSide)); + } +} + +void DrawStarSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawStarSpring(nDurationType, nVFX, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, 0.0, 0.0, nSides, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawStar(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawStarSpring(nDurationType, nVFX, lCenter, fRadiusOuter, fRadiusInner, fRadiusOuter, fRadiusInner, 0.0, 0.0, nSides, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + if (fRev == 0.0) fRev = 5.0; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fSphereRadius, fSphereAngle; + float fEffectiveHeight = fHeightEnd - fHeightStart; + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f + fRotate; + fSphereAngle = fTheta*f*0.25/fRev; + fSphereRadius = fRadiusStart*cos(fSphereAngle) + fRadiusEnd*sin(fSphereAngle); + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSphereRadius*cos(fAngle), fSphereRadius*sin(fAngle), fEffectiveHeight*sin(fSphereAngle) + fHeightStart, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, ApplyEffectAtLocation(nDurationType, EffectVisualEffect(nVFX), lPos, fDuration)); + } +} + +void DrawSphere(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + if (nFrequency < 1) nFrequency = 90; + if (fRev == 0.0) fRev = 5.0; + DrawHemisphere(nDurationType, nVFX, lCenter, fRadius, 0.0, fRadius, 0.0, fDuration, nFrequency/2, fRev/2.0, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); + DrawHemisphere(nDurationType, nVFX, lCenter, fRadius, 0.0, fRadius, 2.0*fRadius, fDuration, nFrequency/2, -fRev/2.0, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawPolygonalHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i; + if (nSides < 3) nSides = 3; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + if (fRev == 0.0) fRev = 5.0; + float fEta = (fRev > 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2, fSphereRadius1, fSphereAngle1, fSphereRadius2, fSphereAngle2; + float fEffectiveHeight = fHeightEnd - fHeightStart; + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fEta*f + fRotate; + fSphereAngle1 = fEta*f*0.25/fRev; + fSphereRadius1 = fRadiusStart*cos(fSphereAngle1) + fRadiusEnd*sin(fSphereAngle1); + fAngle2 = fEta*g + fRotate; + fSphereAngle2 = fEta*g*0.25/fRev; + fSphereRadius2 = fRadiusStart*cos(fSphereAngle2) + fRadiusEnd*sin(fSphereAngle2); + vPos1 = gao_RotateVector(vCenter, sAxis, fSphereRadius1*cos(fAngle1), fSphereRadius1*sin(fAngle1), fEffectiveHeight*sin(fSphereAngle1) + fHeightStart, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, fSphereRadius2*cos(fAngle2), fSphereRadius2*sin(fAngle2), fEffectiveHeight*sin(fSphereAngle2) + fHeightStart, fDirectionXZ, fDirectionYZ); + DelayCommand(f*fDelayPerSide, DrawLineFromVectorToVector(nDurationType, nVFX, oArea, vPos1, vPos2, fDuration, nFrequencyPerSide, fDelayPerSide)); + } +} + +void DrawToroidalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, int nFrequency=90, float fLoopsPerRev=36.0f, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + float fRadiusStart = (fRadiusStartOuter + fRadiusStartInner)*0.5; + float fRadiusEnd = (fRadiusEndOuter + fRadiusEndInner)*0.5; + float fToricRadiusStart = (fRadiusStartOuter - fRadiusStartInner)*0.5; + float fToricRadiusEnd = (fRadiusEndOuter - fRadiusEndInner)*0.5; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fDecay = (fRadiusStart - fRadiusEnd)/IntToFloat(nFrequency); // change in radius per node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fToricDecay = (fToricRadiusStart - fToricRadiusEnd)/IntToFloat(nFrequency); // change in radius of torus per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fToricAngle, fToricRadius; + for (i=0; i 0; atan(y/x) -> 90; + fAngle = (x == 0.0 && y < 0.0) ? 270.0 + fRotate : (x==0.0) ? 90.0 + fRotate : (x < 0.0) ? 180.0 + atan(y/x) + fRotate : atan(y/x) + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fDist*cos(fAngle), fDist*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, ApplyEffectAtLocation(nDurationType, EffectVisualEffect(nVFX), lPos, fDuration)); + } +} + +void DrawHypocycloid(int nDurationType, int nVFX, location lCenter, float fRadius, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawHypocycloidSpring(nDurationType, nVFX, lCenter, fRadius, 0.0, 0.0, fRoulette, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawEpicycloidSpring(int nDurationType, int nVFX, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + if (fRoulette == 0.0) fRoulette = 3.0; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float fAlpha = fRadius + fRoulette; + float fBeta = fAlpha/fRoulette; + float f, x, y, fAngle, fDist; + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f; + y = (fAlpha*sin(fAngle) - fRoulette*sin(fBeta*fAngle)); + x = (fAlpha*cos(fAngle) - fRoulette*cos(fBeta*fAngle)); + fDist = sqrt(pow(y, 2.0) + pow(x, 2.0)); + fAngle = (x == 0.0 && y < 0.0) ? 270.0 + fRotate : (x==0.0) ? 90.0 + fRotate : (x < 0.0) ? 180.0 + atan(y/x) + fRotate : atan(y/x) + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fDist*cos(fAngle), fDist*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, ApplyEffectAtLocation(nDurationType, EffectVisualEffect(nVFX), lPos, fDuration)); + } +} + +void DrawEpicycloid(int nDurationType, int nVFX, location lCenter, float fRadius, float fRoulette=3.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + DrawEpicycloidSpring(nDurationType, nVFX, lCenter, fRadius, 0.0, 0.0, fRoulette, fDuration, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ); +} + +void DrawSinusoid(int nDurationType, int nVFX, location lCenter, float fRadius, float fLength, float fDirection=0.0f, float fDuration=0.0f, int nFrequency=90, float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f) +{ + int i; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 6.0; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fEta = fLength/IntToFloat(nFrequency); // horizontal distance between each node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + vector v, vTemp; + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fSine; + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f + fRotate; + fSine = sin(fAngle); + v = Vector(fEta*f, fRadius*fSine, 0.0); + vTemp = VectorMagnitude(v)*AngleToVector(VectorToAngle(v) + fDirection); + fAngle = (fSine > 0.0) ? 360.0 - fAngle : fAngle ; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, vTemp.x, vTemp.y, 0.0, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, ApplyEffectAtLocation(nDurationType, EffectVisualEffect(nVFX), lPos, fDuration)); + } +} + +/* + ============================================= + PLACE* FUNCTIONS + ============================================= +*/ + +object ObjectPlaceEllipticalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_ELLIPTICALSPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fWait < 1.0) fWait = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + fRadiusStartOuter = (fRadiusStartOuter == 0.0) ? 0.01 : (fRadiusStartOuter < 0.0) ? -fRadiusStartOuter : fRadiusStartOuter ; + fRadiusStartInner = (fRadiusStartInner == 0.0) ? 0.01 : (fRadiusStartInner < 0.0) ? -fRadiusStartInner : fRadiusStartInner ; + fRadiusEndOuter = (fRadiusEndOuter == 0.0) ? 0.01 : (fRadiusEndOuter < 0.0) ? -fRadiusEndOuter : fRadiusEndOuter ; + fRadiusEndInner = (fRadiusEndInner == 0.0) ? 0.01 : (fRadiusEndInner < 0.0) ? -fRadiusEndInner : fRadiusEndInner ; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fInnerDecay = (fRadiusStartInner - fRadiusEndInner)/IntToFloat(nFrequency); // change in radius per node + float fOuterDecay = (fRadiusStartOuter - fRadiusEndOuter)/IntToFloat(nFrequency); // change in radius per node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fElliRadius, fElliAngle, fRadiusOuter, fEccentric; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fElliAngle = fTheta*f; + fAngle = fElliAngle + fRotate; + fRadiusOuter = fRadiusStartOuter - fOuterDecay*f; + fEccentric = 1 - (pow(fRadiusStartInner - fInnerDecay*f, 2.0)/pow(fRadiusOuter, 2.0)); + fElliRadius = fRadiusOuter*sqrt((1 - fEccentric)/(1 - fEccentric*pow(cos(fElliAngle), 2.0))); + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fElliRadius*cos(fAngle), fElliRadius*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceEllipticalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceEllipticalSpring(sTemplate, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, fHeightStart, fHeightEnd, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceEllipticalSpiral(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceEllipticalSpring(sTemplate, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, 0.0, 0.0, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_ELLIPTICALSPIRAL"); +} + +void PlaceEllipse(string sTemplate, location lCenter, float fRadiusOuter, float fRadiusInner, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceEllipticalSpring(sTemplate, lCenter, fRadiusOuter, fRadiusInner, fRadiusOuter, fRadiusInner, 0.0, 0.0, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_ELLIPSE"); +} + +object ObjectPlaceSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_SPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fWait < 1.0) fWait = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fDecay = (fRadiusStart - fRadiusEnd)/IntToFloat(nFrequency); // change in radius per node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*f)*cos(fAngle), (fRadiusStart - fDecay*f)*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceSpring(sTemplate, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceSpiral(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceSpring(sTemplate, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_SPIRAL"); +} + +void PlaceCircle(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceSpring(sTemplate, lCenter, fRadius, fRadius, 0.0, 0.0, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_CIRCLE"); +} + +void PlaceLineToCenter(string sTemplate, location lCenter, float fLength, float fDirection=0.0f, int nFrequency=60, float fTime=12.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceSpring(sTemplate, lCenter, fLength, 0.0, 0.0, 0.0, nFrequency, 0.0, fTime, fDirection, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_LINETO"); +} + +void PlaceLineFromCenter(string sTemplate, location lCenter, float fLength, float fDirection=0.0f, int nFrequency=60, float fTime=12.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceSpring(sTemplate, lCenter, 0.0, fLength, 0.0, 0.0, nFrequency, 0.0, fTime, fDirection, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_LINEFROM"); +} + +object ObjectPlacePolygonalSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_POLYGONALSPRING") +{ + int i; + if (nSides < 3) nSides = 3; + if (nFrequency < 1) nFrequency = 60; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fEta = (fRev > 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDecay = (fRadiusStart - fRadiusEnd)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fEta*f + fRotate; + fAngle2 = fEta*g + fRotate; + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*f)*cos(fAngle1), (fRadiusStart - fDecay*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*g)*cos(fAngle2), (fRadiusStart - fDecay*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + DelayCommand(f*fDelayPerSide, PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlacePolygonalSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePolygonalSpring(sTemplate, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlacePolygonalSpiral(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nSides=3, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePolygonalSpring(sTemplate, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_POLYGONALSPIRAL"); +} + +void PlacePolygon(string sTemplate, location lCenter, float fRadius, int nSides=3, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePolygonalSpring(sTemplate, lCenter, fRadius, fRadius, 0.0, 0.0, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_POLYGON"); +} + +object ObjectPlacePentaclicSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_PENTACLICSPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fSidesToDraw = (fRev > 0.0) ? fRev*5.0 : -fRev*5.0; + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDecay = (fRadiusStart - fRadiusEnd)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2; + float fStarangle = (fRev > 0.0) ? 144.0 : -144.0; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fStarangle*f + fRotate; + fAngle2 = fStarangle*g + fRotate; + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStart-fDecay*f)*cos(fAngle1), (fRadiusStart-fDecay*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStart-fDecay*g)*cos(fAngle2), (fRadiusStart-fDecay*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + DelayCommand(f*fDelayPerSide, PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlacePentaclicSpring(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePentaclicSpring(sTemplate, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlacePentaclicSpiral(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePentaclicSpring(sTemplate, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_PENTACLICSPIRAL"); +} + +void PlacePentacle(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePentaclicSpring(sTemplate, lCenter, fRadius, fRadius, 0.0, 0.0, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_PENTACLE"); +} + +object ObjectPlaceStarSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_STARSPRING") +{ + int i, toggle; + if (nSides < 2) nSides = 3; + if (nFrequency < 1) nFrequency = 60; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides*2) : -fRev*IntToFloat(nSides*2); + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDecayInner = (fRadiusStartInner - fRadiusEndInner)/fSidesToDraw; // change in radius per side + float fDecayOuter = (fRadiusStartOuter - fRadiusEndOuter)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2; + float fStarangle = (fRev > 0.0) ? 360.0/IntToFloat(nSides*2) : -360.0/IntToFloat(nSides*2); + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nSidesToDraw; i++) + { + toggle ^= 1; + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fStarangle*f + fRotate; + fAngle2 = fStarangle*g + fRotate; + if (!toggle) + { + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStartInner - fDecayInner*f)*cos(fAngle1), (fRadiusStartInner - fDecayInner*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStartOuter - fDecayOuter*g)*cos(fAngle2), (fRadiusStartOuter - fDecayOuter*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + } + else + { + vPos1 = gao_RotateVector(vCenter, sAxis, (fRadiusStartOuter - fDecayOuter*f)*cos(fAngle1), (fRadiusStartOuter - fDecayOuter*f)*sin(fAngle1), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, (fRadiusStartInner - fDecayInner*g)*cos(fAngle2), (fRadiusStartInner - fDecayInner*g)*sin(fAngle2), fHeightStart - fGrowth*g, fDirectionXZ, fDirectionYZ); + } + DelayCommand(f*fDelayPerSide, PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceStarSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceStarSpring(sTemplate, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, fHeightStart, fHeightEnd, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceStarSpiral(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceStarSpring(sTemplate, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, 0.0, 0.0, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_STARSPIRAL"); +} + +void PlaceStar(string sTemplate, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceStarSpring(sTemplate, lCenter, fRadiusOuter, fRadiusInner, fRadiusOuter, fRadiusInner, 0.0, 0.0, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_STAR"); +} + +object ObjectPlaceHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_HEMISPHERE") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fDecay = (fRadiusStart - fRadiusEnd)/IntToFloat(nFrequency); // change in radius per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fSphereRadius, fSphereAngle; + float fEffectiveHeight = fHeightEnd - fHeightStart; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f + fRotate; + fSphereAngle = fTheta*f*0.25/fRev; + fSphereRadius = fRadiusStart*cos(fSphereAngle) + fRadiusEnd*sin(fSphereAngle); + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSphereRadius*cos(fAngle), fSphereRadius*sin(fAngle), fEffectiveHeight*sin(fSphereAngle) + fHeightStart, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceHemisphere(sTemplate, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceSphere(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + if (nFrequency < 1) nFrequency = 60; + if (fRev == 0.0) fRev = 5.0; + ObjectPlaceHemisphere(sTemplate, lCenter, fRadius, 0.0, fRadius, 0.0, nFrequency/2, fRev/2.0, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); + ObjectPlaceHemisphere(sTemplate, lCenter, fRadius, 0.0, fRadius, 2.0*fRadius, nFrequency/2, -fRev/2.0, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +object ObjectPlacePolygonalHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_POLYGONALHEMISPHERE") +{ + int i; + if (nSides < 3) nSides = 3; + if (nFrequency < 1) nFrequency = 90; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fEta = (fRev > 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + int nFrequencyPerSide = FloatToInt(IntToFloat(nFrequency)/fSidesToDraw); + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos1, vPos2; + object oArea = GetAreaFromLocation(lCenter); + float f, g, fAngle1, fAngle2, fSphereRadius1, fSphereAngle1, fSphereRadius2, fSphereAngle2; + float fEffectiveHeight = fHeightEnd - fHeightStart; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + fAngle1 = fEta*f + fRotate; + fSphereAngle1 = fEta*f*0.25/fRev; + fSphereRadius1 = fRadiusStart*cos(fSphereAngle1) + fRadiusEnd*sin(fSphereAngle1); + fAngle2 = fEta*g + fRotate; + fSphereAngle2 = fEta*g*0.25/fRev; + fSphereRadius2 = fRadiusStart*cos(fSphereAngle2) + fRadiusEnd*sin(fSphereAngle2); + vPos1 = gao_RotateVector(vCenter, sAxis, fSphereRadius1*cos(fAngle1), fSphereRadius1*sin(fAngle1), fEffectiveHeight*sin(fSphereAngle1) + fHeightStart, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, fSphereRadius2*cos(fAngle2), fSphereRadius2*sin(fAngle2), fEffectiveHeight*sin(fSphereAngle2) + fHeightStart, fDirectionXZ, fDirectionYZ); + DelayCommand(f*fDelayPerSide, PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlacePolygonalHemisphere(string sTemplate, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlacePolygonalHemisphere(sTemplate, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nSides, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +object ObjectPlaceSinusoid(string sTemplate, location lCenter, float fRadius, float fLength, float fDirection=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_SINUSOID") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fEta = fLength/IntToFloat(nFrequency); // horizontal distance between each node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + vector v, vTemp; + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fSine; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f + fRotate; + fSine = sin(fAngle); + v = Vector(fEta*f, fRadius*fSine, 0.0); + vTemp = VectorMagnitude(v)*AngleToVector(VectorToAngle(v) + fDirection); + fAngle = (fSine > 0.0) ? 360.0 - fAngle : fAngle ; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, vTemp.x, vTemp.y, 0.0, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceSinusoid(string sTemplate, location lCenter, float fRadius, float fLength, float fDirection=0.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceSinusoid(sTemplate, lCenter, fRadius, fLength, fDirection, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +object ObjectPlaceToroidalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_TOROIDALSPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fRadiusStart = (fRadiusStartOuter + fRadiusStartInner)*0.5; + float fRadiusEnd = (fRadiusEndOuter + fRadiusEndInner)*0.5; + float fToricRadiusStart = (fRadiusStartOuter - fRadiusStartInner)*0.5; + float fToricRadiusEnd = (fRadiusEndOuter - fRadiusEndInner)*0.5; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fDecay = (fRadiusStart - fRadiusEnd)/IntToFloat(nFrequency); // change in radius per node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fToricDecay = (fToricRadiusStart - fToricRadiusEnd)/IntToFloat(nFrequency); // change in radius of torus per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fToricAngle, fToricRadius; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f + fRotate; + fToricAngle = fLoopsPerRev*fAngle; + fToricRadius = fToricRadiusStart - fToricDecay*f; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, (fRadiusStart - fDecay*f)*cos(fAngle) + fToricRadius*cos(fToricAngle)*cos(fAngle), (fRadiusStart - fDecay*f)*sin(fAngle) + fToricRadius*cos(fToricAngle)*sin(fAngle), fHeightStart - fGrowth*f + fToricRadius*sin(fToricAngle), fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceToroidalSpring(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceToroidalSpring(sTemplate, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, fHeightStart, fHeightEnd, nFrequency, fLoopsPerRev, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceToroidalSpiral(string sTemplate, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceToroidalSpring(sTemplate, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, 0.0, 0.0, nFrequency, fLoopsPerRev, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_TOROIDALSPIRAL"); +} + +void PlaceTorus(string sTemplate, location lCenter, float fRadiusOuter, float fRadiusInner, int nFrequency=60, float fLoopsPerRev=36.0f, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceToroidalSpring(sTemplate, lCenter, fRadiusOuter, fRadiusInner, fRadiusOuter, fRadiusInner, 0.0, 0.0, nFrequency, fLoopsPerRev, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_TORUS"); +} + +object ObjectPlaceStellaOctangula(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_STELLAOCTANGULA") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (nFrequency < 1) nFrequency = 60; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma = fRadius*2.0/3.0; + float fEpsilon = fSigma*4.0/3.0/cos(19.47122063449069136924599933997); + int nFrequencyPerSide = nFrequency/12; + float fDelayPerSide = fTime/12.0; + float f, z1, fAngle1, g, z2, fAngle2; + vector vPos1, vPos2, vTop; + vTop = gao_RotateVector(vCenter, sAxis, 0.0, 0.0, 3.0*fSigma, fDirectionXZ, fDirectionYZ); + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < 6; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + if (i < 3) + { + fAngle1 = fRotate + 120.0*f; + fAngle2 = fRotate + 120.0*g; + z1 = 2.0*fSigma; + z2 = 2.0*fSigma; + } + else + { + fAngle1 = fRotate + 120.0*f + 60.0 ; + fAngle2 = fRotate + 120.0*g + 60.0 ; + z1 = fSigma; + z2 = fSigma; + } + vPos1 = gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle1), fEpsilon*sin(fAngle1), z1, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle2), fEpsilon*sin(fAngle2), z2, fDirectionXZ, fDirectionYZ); + + if (i<3) DelayCommand(fDelayPerSide*f, PlaceLineFromVectorToVector(sTemplate, oArea, vCenter, vPos1, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + else DelayCommand(fDelayPerSide*(f+6.0), PlaceLineFromVectorToVector(sTemplate, oArea, vTop, vPos1, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + DelayCommand(fDelayPerSide*(f+3.0), PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceStellaOctangula(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceStellaOctangula(sTemplate, lCenter, fRadius, nFrequency, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +object ObjectPlaceIcosahedron(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_ICOSAHEDRON") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (nFrequency < 1) nFrequency = 60; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma1 = fRadius*0.55278640450004206071816526625413; + float fSigma2 = fRadius*0.89442719099991587856366946749173; + float fEpsilon = fRadius*0.89442719099991587856366946749256; + int nFrequencyPerSide = nFrequency/30; + float fDelayPerSide = fTime/30.0; + float f, z1, fAngle1, g, z2, fAngle2; + vector vPos1, vPos2, vTop; + vTop = gao_RotateVector(vCenter, sAxis, 0.0, 0.0, 2.0*fSigma1 + fSigma2, fDirectionXZ, fDirectionYZ); + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < 20; i++) + { + f = IntToFloat(i); + g = IntToFloat(i+1); + if (i < 5) + { + fAngle1 = fRotate + f*72.0; + fAngle2 = fRotate + g*72.0; + z1 = fSigma1; + z2 = fSigma1; + } + else if (i < 10) + { + fAngle1 = fRotate + f*72.0; + fAngle2 = fRotate + f*72.0 + 36.0; + z1 = fSigma1; + z2 = fSigma1 + fSigma2; + } + else if (i < 15) + { + fAngle1 = fRotate + f*72.0; + fAngle2 = fRotate + f*72.0 - 36.0; + z1 = fSigma1; + z2 = fSigma1 + fSigma2; + } + else + { + fAngle1 = fRotate + f*72.0 + 36.0; + fAngle2 = fRotate + g*72.0 + 36.0; + z1 = fSigma1 + fSigma2; + z2 = fSigma1 + fSigma2; + } + vPos1 = gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle1), fEpsilon*sin(fAngle1), z1, fDirectionXZ, fDirectionYZ); + vPos2 = gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle2), fEpsilon*sin(fAngle2), z2, fDirectionXZ, fDirectionYZ); + + if (i < 5) + { + DelayCommand(fDelayPerSide*f, PlaceLineFromVectorToVector(sTemplate, oArea, vCenter, vPos1, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + DelayCommand(fDelayPerSide*(f+5.0), PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + else if (i < 10) + { + DelayCommand(fDelayPerSide*(f+5.0), PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + else if (i < 15) + { + DelayCommand(fDelayPerSide*(f+5.0), PlaceLineFromVectorToVector(sTemplate, oArea, vPos2, vPos1, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + else + { + DelayCommand(fDelayPerSide*(f+10.0), PlaceLineFromVectorToVector(sTemplate, oArea, vTop, vPos1, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + DelayCommand(fDelayPerSide*(f+5.0), PlaceLineFromVectorToVector(sTemplate, oArea, vPos1, vPos2, nFrequencyPerSide, fDelayPerSide, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + } + + return oData; +} + +void PlaceIcosahedron(string sTemplate, location lCenter, float fRadius, int nFrequency=60, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceIcosahedron(sTemplate, lCenter, fRadius, nFrequency, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +object ObjectPlaceRhodoneaSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_RHODONEASPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fWait < 1.0) fWait = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float f, fAngle, fDist; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f; + fDist = fRadius*sin(fRoulette*fAngle); + fAngle += fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fDist*cos(fAngle), fDist*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceRhodoneaSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceRhodoneaSpring(sTemplate, lCenter, fRadius, fHeightStart, fHeightEnd, fRoulette, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceRhodonea(string sTemplate, location lCenter, float fRadius, float fRoulette=3.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceRhodoneaSpring(sTemplate, lCenter, fRadius, 0.0, 0.0, fRoulette, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_RHODONEA"); +} + +object ObjectPlaceHypocycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_HYPOCYCLOIDSPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fWait < 1.0) fWait = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + if (fRoulette == 0.0) fRoulette = 3.0; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float fAlpha = fRadius - fRoulette; + float fBeta = fAlpha/fRoulette; // DIVIDE BY ZERO + float f, x, y, fAngle, fDist; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f; + y = (fAlpha*sin(fAngle) - fRoulette*sin(fBeta*fAngle)); + x = (fAlpha*cos(fAngle) + fRoulette*cos(fBeta*fAngle)); + fDist = sqrt(pow(y, 2.0) + pow(x, 2.0)); + fAngle = (x == 0.0 && y < 0.0) ? 270.0 + fRotate : (x==0.0) ? 90.0 + fRotate : (x < 0.0) ? 180.0 + atan(y/x) + fRotate : atan(y/x) + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fDist*cos(fAngle), fDist*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceHypocycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceHypocycloidSpring(sTemplate, lCenter, fRadius, fHeightStart, fHeightEnd, fRoulette, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceHypocycloid(string sTemplate, location lCenter, float fRadius, float fRoulette=3.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceHypocycloidSpring(sTemplate, lCenter, fRadius, 0.0, 0.0, fRoulette, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_HYPOCYCLOID"); +} + +object ObjectPlaceEpicycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f, string sTag="PSC_P_EPICYCLOIDSPRING") +{ + int i; + if (nFrequency < 1) nFrequency = 60; + if (fTime < 0.0) fTime = 12.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fWait < 1.0) fWait = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + if (fRoulette == 0.0) fRoulette = 3.0; + float fTheta = 360.0*fRev/IntToFloat(nFrequency); // angle between each node + float fGrowth = (fHeightStart - fHeightEnd)/IntToFloat(nFrequency); // change in height per node + float fDelay = fTime/IntToFloat(nFrequency); + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + location lPos; + float fAlpha = fRadius + fRoulette; + float fBeta = fAlpha/fRoulette; + float f, x, y, fAngle, fDist; + + object oData = (fLifetime > 0.0) ? OBJECT_INVALID : CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + + for (i = 0; i < nFrequency; i++) + { + f = IntToFloat(i); + fAngle = fTheta*f; + y = (fAlpha*sin(fAngle) - fRoulette*sin(fBeta*fAngle)); + x = (fAlpha*cos(fAngle) - fRoulette*cos(fBeta*fAngle)); + fDist = sqrt(pow(y, 2.0) + pow(x, 2.0)); + fAngle = (x == 0.0 && y < 0.0) ? 270.0 + fRotate : (x==0.0) ? 90.0 + fRotate : (x < 0.0) ? 180.0 + atan(y/x) + fRotate : atan(y/x) + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fDist*cos(fAngle), fDist*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelay, gao_ActionCreateObject(sTemplate, lPos, nDurationType, nVFX, fDuration, fWait, fLifetime, oData)); + } + + return oData; +} + +void PlaceEpicycloidSpring(string sTemplate, location lCenter, float fRadius, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fRoulette=3.0f, int nFrequency=60, float fRev=5.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceEpicycloidSpring(sTemplate, lCenter, fRadius, fHeightStart, fHeightEnd, fRoulette, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime); +} + +void PlaceEpicycloid(string sTemplate, location lCenter, float fRadius, float fRoulette=3.0f, int nFrequency=60, float fRev=1.0f, float fTime=12.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType=-1, int nVFX=-1, float fDuration=0.0f, float fWait=1.0f, float fLifetime=0.0f) +{ + ObjectPlaceEpicycloidSpring(sTemplate, lCenter, fRadius, 0.0, 0.0, fRoulette, nFrequency, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType, nVFX, fDuration, fWait, fLifetime, "PSC_P_EPICYCLOID"); +} + +/* + ============================================= + BEAM FUNCTIONS + ============================================= +*/ + +object ObjectBeamPolygonalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_POLYGONALSPRING") +{ + int i; + if (nSides < 3) nSides = 3; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fEta = (fRev > 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + float fDecay = (fRadiusStart - fRadiusEnd)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float f, fAngle; + object oData; + location lPos; + float fWait = 1.0; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw + 1))); + + for (i = 0; i <= nSidesToDraw; i++) + { + f = IntToFloat(i); + fAngle = fEta*f + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, (fRadiusStart-fDecay*f)*cos(fAngle), (fRadiusStart-fDecay*f)*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i > 0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamPolygonalSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPolygonalSpring(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nSides, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +void BeamPolygonalSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPolygonalSpring(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, nSides, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime, "PSC_B_POLYGONALSPIRAL"); +} + +object ObjectBeamPolygon(int nDurationType, int nVFX, location lCenter, float fRadius, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_POLYGON") +{ + int i; + if (nSides < 3) nSides = 3; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fEta = (fRev > 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos; + object oArea = GetAreaFromLocation(lCenter); + float f, x, y, fAngle; + object oData; + location lPos; + float fWait = 1.0; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw))); + + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + fAngle = fEta*f + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fRadius*cos(fAngle), fRadius*sin(fAngle), 0.0, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i>0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + if (fRev == 1.0 && i == nSidesToDraw-1) DelayCommand(fSidesToDraw*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i), "store0", nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamPolygon(int nDurationType, int nVFX, location lCenter, float fRadius, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPolygon(nDurationType, nVFX, lCenter, fRadius, nSides, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamPentaclicSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_PENTACLICSPRING") +{ + int i; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fSidesToDraw = (fRev > 0.0) ? fRev*5.0 : -fRev*5.0; + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + float fDecay = (fRadiusStart - fRadiusEnd)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float f, fAngle; + float fStarangle = (fRev > 0.0) ? 144.0 : -144.0; + object oData; + location lPos; + float fWait = 1.0; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw+1))); + + for (i = 0; i <= nSidesToDraw; i++) + { + f = IntToFloat(i); + fAngle = fStarangle*f + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, (fRadiusStart-fDecay*f)*cos(fAngle), (fRadiusStart-fDecay*f)*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i > 0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamPentaclicSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPentaclicSpring(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +void BeamPentaclicSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPentaclicSpring(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, 0.0, 0.0, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime, "PSC_B_PENTACLICSPIRAL"); +} + +object ObjectBeamPentacle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_PENTACLE") +{ + int i; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fSidesToDraw = (fRev > 0.0) ? fRev*5.0 : -fRev*5.0; + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos; + object oArea = GetAreaFromLocation(lCenter); + float f, x, y, fAngle; + float fStarangle = (fRev > 0.0) ? 144.0 : -144.0; + object oData; + location lPos; + float fWait = 1.0; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw))); + + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + fAngle = fStarangle*f + fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fRadius*cos(fAngle), fRadius*sin(fAngle), 0.0, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i>0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + if (fRev == 1.0 && i == nSidesToDraw-1) DelayCommand(fSidesToDraw*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i), "store0", nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamPentacle(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPentacle(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamStarSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_STARSPRING") +{ + int i, toggle; + if (nSides < 2) nSides = 3; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides*2) : -fRev*IntToFloat(nSides*2); + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + float fDecayOuter = (fRadiusStartOuter - fRadiusEndOuter)/fSidesToDraw; // change in radius per side + float fDecayInner = (fRadiusStartInner - fRadiusEndInner)/fSidesToDraw; // change in radius per side + float fGrowth = (fHeightStart - fHeightEnd)/fSidesToDraw; // change in height per side + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float f, fAngle; + float fStarangle = (fRev > 0.0) ? 360.0/IntToFloat(2*nSides) : -360.0/IntToFloat(2*nSides); + object oData; + location lPos; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw+1))); + + for (i = 0; i <= nSidesToDraw; i++) + { + toggle ^= 1; + f = IntToFloat(i); + fAngle = fStarangle*f + fRotate; + if (!toggle) lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, (fRadiusStartInner - fDecayInner*f)*cos(fAngle), (fRadiusStartInner - fDecayInner*f)*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + else lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, (fRadiusStartOuter - fDecayOuter*f)*cos(fAngle), (fRadiusStartOuter - fDecayOuter*f)*sin(fAngle), fHeightStart - fGrowth*f, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i>0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamStarSpring(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamStarSpring(nDurationType, nVFX, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, fHeightStart, fHeightEnd, nSides, fDuration, sTemplate, fRev, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +void BeamStarSpiral(int nDurationType, int nVFX, location lCenter, float fRadiusStartOuter, float fRadiusStartInner, float fRadiusEndOuter=0.0f, float fRadiusEndInner=0.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamStarSpring(nDurationType, nVFX, lCenter, fRadiusStartOuter, fRadiusStartInner, fRadiusEndOuter, fRadiusEndInner, 0.0, 0.0, nSides, fDuration, sTemplate, fRev, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime, "PSC_B_STARSPIRAL"); +} + +object ObjectBeamStar(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_STAR") +{ + int i, toggle; + if (nSides < 2) nSides = 3; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 1.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(2*nSides) : -fRev*IntToFloat(2*nSides); + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos; + object oArea = GetAreaFromLocation(lCenter); + float f, x, y, fAngle; + float fStarangle = (fRev > 0.0) ? 360.0/IntToFloat(2*nSides) : -360.0/IntToFloat(2*nSides); + object oData; + location lPos; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw))); + + for (i = 0; i < nSidesToDraw; i++) + { + f = IntToFloat(i); + toggle ^= 1; + fAngle = fStarangle*f + fRotate; + if (!toggle) lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fRadiusInner*cos(fAngle), fRadiusInner*sin(fAngle), 0.0, fDirectionXZ, fDirectionYZ), fAngle); + else lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fRadiusOuter*cos(fAngle), fRadiusOuter*sin(fAngle), 0.0, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i > 0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + if (fRev == 1.0 && i == nSidesToDraw-1) DelayCommand(fSidesToDraw*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i), "store0", nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamStar(int nDurationType, int nVFX, location lCenter, float fRadiusOuter, float fRadiusInner, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=1.0f, float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamStar(nDurationType, nVFX, lCenter, fRadiusOuter, fRadiusInner, nSides, fDuration, sTemplate, fRev, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamLineFromCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_LINEFROM") +{ + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + object oArea = GetAreaFromLocation(lCenter); + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos = fLength*AngleToVector(fDirection); + vector vPos2; + float fWait = 1.0; + + vPos2 = gao_RotateVector(vCenter, sAxis, vPos.x, vPos.y, vPos.z, fDirectionXZ, fDirectionYZ); + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 2))); + + gao_ActionCreateLocalObject(sTemplate, lCenter, "store0", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); + DelayCommand(fTime, gao_ActionCreateLocalObject(sTemplate, Location(oArea, vPos2, fDirection), "store1", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + + DelayCommand(fTime+1.0, gao_ActionApplyLocalBeamEffect(oData, "store0", "store1", nDurationType, nVFX, fDuration)); + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamLineFromCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamLineFromCenter(nDurationType, nVFX, lCenter, fLength, fDirection, fDuration, sTemplate, fTime, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamLineToCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_LINETO") +{ + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + object oArea = GetAreaFromLocation(lCenter); + vector vCenter = GetPositionFromLocation(lCenter); + vector vPos = fLength*AngleToVector(fDirection); + vector vPos2; + float fWait = 1.0; + + vPos2 = gao_RotateVector(vCenter, sAxis, vPos.x, vPos.y, vPos.z, fDirectionXZ, fDirectionYZ); + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 2))); + + DelayCommand(fTime, gao_ActionCreateLocalObject(sTemplate, lCenter, "store0", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + gao_ActionCreateLocalObject(sTemplate, Location(oArea, vPos2, fDirection), "store1", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); + + DelayCommand(fTime+1.0, gao_ActionApplyLocalBeamEffect(oData, "store1", "store0", nDurationType, nVFX, fDuration)); + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamLineToCenter(int nDurationType, int nVFX, location lCenter, float fLength, float fDirection=0.0f, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamLineToCenter(nDurationType, nVFX, lCenter, fLength, fDirection, fDuration, sTemplate, fTime, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamPolygonalHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_POLYGONALHEMISPHERE") +{ + int i; + if (nSides < 3) nSides = 3; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (fRev == 0.0) fRev = 5.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fEta = (fRev > 0.0) ? 360.0/IntToFloat(nSides) : -360.0/IntToFloat(nSides); // angle of segment + float fSidesToDraw = (fRev > 0.0) ? fRev*IntToFloat(nSides) : -fRev*IntToFloat(nSides); // total number of sides to draw including revolutions as float value + int nSidesToDraw = FloatToInt(fSidesToDraw); // total number of sides to draw including revolutions as int value + float fDelayPerSide = fTime/fSidesToDraw; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float f, fAngle, fSphereAngle, fSphereRadius; + float fEffectiveHeight = fHeightEnd - fHeightStart; + object oData; + location lPos; + float fWait = 1.0; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", nSidesToDraw+1))); + + for (i = 0; i <= nSidesToDraw; i++) + { + f = IntToFloat(i); + fAngle = fEta*f + fRotate; + fSphereAngle = fEta*f*0.25/fRev; + fSphereRadius = fRadiusStart*cos(fSphereAngle) + fRadiusEnd*sin(fSphereAngle); + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSphereRadius*cos(fAngle), fSphereRadius*sin(fAngle), fEffectiveHeight*sin(fSphereAngle) + fHeightStart, fDirectionXZ, fDirectionYZ), fAngle); + DelayCommand(f*fDelayPerSide, gao_ActionCreateLocalObject(sTemplate, lPos, "store" + IntToString(i), oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + if (i > 0) DelayCommand(f*fDelayPerSide+fWait, gao_ActionApplyLocalBeamEffect(oData, "store" + IntToString(i-1), "store" + IntToString(i), nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamPolygonalHemisphere(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fRev=5.0f, float fTime=6.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamPolygonalHemisphere(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nSides, fDuration, sTemplate, fRev, fTime, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamStellaOctangula(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_STELLAOCTANGULA") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma = fRadius*2.0/3.0; + float fEpsilon = fSigma*4.0/3.0/cos(19.47122063449069136924599933997); + float fDelay = fTime/8.0; + fWait += fDelay; + float f, z, fAngle; + location lPos; + string sNumber, sNumber1; + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 8))); + + gao_ActionCreateLocalObject(sTemplate, lCenter, "store7", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); + + for (i = 0; i < 7; i++) + { + f = IntToFloat(i); + if (i < 3) + { + z = 2.0*fSigma; + fAngle = fRotate + 120.0*f; + } + else if (i < 6) + { + z = fSigma; + fAngle = fRotate + 120.0*f + 60.0; + } + else + { + z = 3.0*fSigma; + fEpsilon = 0.0; + } + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle), fEpsilon*sin(fAngle), z, fDirectionXZ, fDirectionYZ), fAngle); + sNumber = "store"+IntToString(i); + DelayCommand(fDelay*(f+1.0), gao_ActionCreateLocalObject(sTemplate, lPos, sNumber, oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + } + + for (i=0; i<3; i++) + { + f = IntToFloat(i); + sNumber = "store"+IntToString(i); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, "store7", sNumber, nDurationType, nVFX, fDuration)); + } + for (i=0; i<6; i++) + { + f = IntToFloat(i+3); + sNumber = "store"+IntToString(i); + if (i==2) sNumber1 = "store0"; + else if (i==5) sNumber1 = "store3"; + else sNumber1 = "store"+IntToString(i+1); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber, sNumber1, nDurationType, nVFX, fDuration)); + } + for (i=3; i<6; i++) + { + f = IntToFloat(i+6); + sNumber = "store"+IntToString(i); + sNumber1 = "store6"; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber, sNumber1, nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamStellaOctangula(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamStellaOctangula(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamIcosahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_ICOSAHEDRON") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma1 = fRadius*0.55278640450004206071816526625413; + float fSigma2 = fRadius*0.89442719099991587856366946749173; + float fEpsilon = fRadius*0.89442719099991587856366946749256; + float fDelay = fTime/30.0; + fWait += fDelay; + float f, z, fAngle; + location lPos; + string sNumber, sNumber1; + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 12))); + + gao_ActionCreateLocalObject(sTemplate, lCenter, "store11", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); + + for (i = 0; i < 11; i++) + { + f = IntToFloat(i); + if (i < 5) + { + fAngle = fRotate + f*72.0; + z = fSigma1; + } + else if (i < 10) + { + fAngle = fRotate + f*72.0 + 36.0; + z = fSigma1 + fSigma2; + } + else + { + fAngle = fRotate; + z = 2.0*fSigma1 + fSigma2; + fEpsilon = 0.0; + } + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle), fEpsilon*sin(fAngle), z, fDirectionXZ, fDirectionYZ), fAngle); + sNumber = "store"+IntToString(i); + DelayCommand(fDelay*(f+1.0), gao_ActionCreateLocalObject(sTemplate, lPos, sNumber, oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + } + + for (i=0; i<5; i++) + { + f = IntToFloat(i); + sNumber = "store"+IntToString(i); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, "store11", sNumber, nDurationType, nVFX, fDuration)); + } + for (i=0; i<10; i++) + { + f = IntToFloat(i+5); + sNumber = "store"+IntToString(i); + if (i==4) sNumber1 = "store0"; + else if (i==9) sNumber1 = "store5"; + else sNumber1 = "store"+IntToString(i+1); + + if (i<5) DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber, sNumber1, nDurationType, nVFX, fDuration)); + else DelayCommand(fDelay*(f+10.0)+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber, sNumber1, nDurationType, nVFX, fDuration)); + } + for (i=0; i<10; i++) + { + f = IntToFloat(i+10); + sNumber = "store"+IntToString(i); + if (i<5) sNumber1 = "store"+IntToString(i+5); + else if (i==9) sNumber1 = "store0"; + else sNumber1 = "store"+IntToString(i-4); + + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber, sNumber1, nDurationType, nVFX, fDuration)); + } + for (i=5; i<10; i++) + { + f = IntToFloat(i+20); + sNumber = "store10"; + sNumber1 = "store"+IntToString(i); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber, sNumber1, nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamIcosahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamIcosahedron(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamDodecahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_DODECAHEDRON") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma1 = fRadius*0.205345527708233877044469071674; // Rd - rd + float fSigma2 = fRadius*0.79465447229176612295553092832696; // rd + float fSigma3 = fRadius*0.60706199820668622309539158142001; // Rp + float fEpsilon = fRadius*0.98224694637684602281567027523513; //Rdisplace ~ Zdisplace, golden + float fDelay = fTime/30.0; + fWait += fDelay; + float f, fAngle; + location lPos; + string sNumber1, sNumber2; + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 20))); + + for (i = 0; i < 20; i++) + { + f = IntToFloat(i); + if (i<5) + { + fAngle = fRotate + f*72.0 - 36.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma3*cos(fAngle), fSigma3*sin(fAngle), fSigma1, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<10) + { + fAngle = fRotate + f*72.0 - 36.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle), fEpsilon*sin(fAngle), fSigma1 + fSigma3, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<15) + { + fAngle = fRotate + f*72.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon*cos(fAngle), fEpsilon*sin(fAngle), fSigma1 + fEpsilon, fDirectionXZ, fDirectionYZ), fAngle); + } + else + { + fAngle = fRotate + f*72.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma3*cos(fAngle), fSigma3*sin(fAngle), fSigma1 + 2.0*fSigma2, fDirectionXZ, fDirectionYZ), fAngle); + } + sNumber1 = "store"+IntToString(i); + DelayCommand(fDelay*f, gao_ActionCreateLocalObject(sTemplate, lPos, sNumber1, oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + } + + for (i=0; i<5; i++) + { + f = IntToFloat(i); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<4) ? "store" + IntToString(i+1) : "store0"; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=0; i<15; i++) + { + f = (i<10) ? IntToFloat(i+5) : IntToFloat(i+10); + sNumber1 = "store" + IntToString(i); + sNumber2 = "store" + IntToString(i+5); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=10; i<15; i++) + { + f = IntToFloat(i+5); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<14) ? "store" + IntToString(i-4) : "store5"; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=15; i<20; i++) + { + f = IntToFloat(i+10); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<19) ? "store" + IntToString(i+1) : "store15"; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamDodecahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamDodecahedron(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamTriacontahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_TRIACONTAHEDRON") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma1 = fRadius*0.55278640450004206071816526625413; + float fSigma2 = fRadius*0.89442719099991587856366946749173; + float fSigma3 = fRadius*0.205345527708233877044469071674; //1 Rd - rd + float fSigma4 = fRadius*0.79465447229176612295553092832696; //2 rd + float fSigma5 = fRadius*0.60706199820668622309539158142001; //3 Rp + float fEpsilon1 = fRadius*0.89442719099991587856366946749256; + float fEpsilon2 = fRadius*0.98224694637684602281567027523513; //Rdisplace ~ Zdisplace, golden + float fDelay = fTime/60.0; + fWait += fDelay; + float f, fAngle; + location lPos; + string sNumber1, sNumber2; + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 32))); + + gao_ActionCreateLocalObject(sTemplate, lCenter, "store31", oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); + + for (i = 0; i < 31; i++) + { + f = IntToFloat(i); + if (i<5) + { + fAngle = fRotate + f*72.0 + 36.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma5*cos(fAngle), fSigma5*sin(fAngle), fSigma3, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<10) + { + fAngle = fRotate + f*72.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon1*cos(fAngle), fEpsilon1*sin(fAngle), fSigma1, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<15) + { + fAngle = fRotate + f*72.0 + 36.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon2*cos(fAngle), fEpsilon2*sin(fAngle), fSigma3 + fSigma5, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<20) + { + fAngle = fRotate + f*72.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon2*cos(fAngle), fEpsilon2*sin(fAngle), fSigma3 + fEpsilon2, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<25) + { + fAngle = fRotate + f*72.0 + 36.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fEpsilon1*cos(fAngle), fEpsilon1*sin(fAngle), fSigma1 + fSigma2, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<30) + { + fAngle = fRotate + f*72.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma5*cos(fAngle), fSigma5*sin(fAngle), fSigma3 + 2.0*fSigma4, fDirectionXZ, fDirectionYZ), fAngle); + } + else + { + fAngle = fRotate; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, 0.0, 0.0, 2.0*fSigma1 + fSigma2, fDirectionXZ, fDirectionYZ), fAngle); + } + sNumber1 = "store"+IntToString(i); + DelayCommand(fDelay*(f+1.0), gao_ActionCreateLocalObject(sTemplate, lPos, sNumber1, oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + } + + for (i=0; i<15; i++) + { + f = (i<10) ? IntToFloat(i) : IntToFloat(i+5); + sNumber1 = (i<5) ? "store31" : "store" + IntToString(i-5); + sNumber2 = "store" + IntToString(i); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=5; i<15; i++) + { + f = (i<10) ? IntToFloat(i+5) : IntToFloat(i+10) ; + sNumber1 = "store" + IntToString(i); + sNumber2 = (i==5) ? "store4" : (i==14) ? "store5" : (i<10) ? "store" + IntToString(i-6) : "store" + IntToString(i-4); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=5; i<15; i++) + { + f = IntToFloat(i+20); + sNumber1 = "store" + IntToString(i); + sNumber2 = "store" + IntToString(i+10); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=15; i<30; i++) + { + f = (i<20) ? IntToFloat(i+20) : (i<25) ? IntToFloat(i+25) : IntToFloat(i+30); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<25) ? "store" + IntToString(i+5) : "store30" ; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=20; i<30; i++) + { + f = (i<20) ? IntToFloat(i+20) : IntToFloat(i+25) ; + sNumber1 = "store" + IntToString(i); + sNumber2 = (i==24) ? "store15" : (i==25) ? "store24" : (i<25) ? "store" + IntToString(i-4) : "store" + IntToString(i-6); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamTriacontahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamTriacontahedron(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamCuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_CUBOCTAHEDRON") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma = fRadius*0.70710678118654752440084436210485; + float fEpsilon = fRadius - fSigma ; + float fDelay = fTime/24.0; + fWait += fDelay; + float f, fAngle; + location lPos; + string sNumber1, sNumber2; + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 12))); + + for (i = 0; i < 12; i++) + { + f = IntToFloat(i); + if (i<4) + { + fAngle = fRotate + f*90.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma*cos(fAngle), fSigma*sin(fAngle), fEpsilon, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<8) + { + fAngle = fRotate + f*90.0 + 45.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fRadius*cos(fAngle), fRadius*sin(fAngle), fRadius, fDirectionXZ, fDirectionYZ), fAngle); + } + else + { + fAngle = fRotate + f*90.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma*cos(fAngle), fSigma*sin(fAngle), fRadius + fSigma, fDirectionXZ, fDirectionYZ), fAngle); + } + sNumber1 = "store"+IntToString(i); + DelayCommand(fDelay*f, gao_ActionCreateLocalObject(sTemplate, lPos, sNumber1, oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + } + + for (i=0; i<4; i++) + { + f = IntToFloat(i); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<3) ? "store" + IntToString(i+1) : "store0"; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=0; i<8; i++) + { + f = (i<4) ? IntToFloat(i+4) : IntToFloat(i+8) ; + sNumber1 = "store" + IntToString(i); + sNumber2 = "store" + IntToString(i+4); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=0; i<8; i++) + { + f = (i<4) ? IntToFloat(i+8) : IntToFloat(i+12); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i==0) ? "store7" : (i<4) ? "store" + IntToString(i+3) : (i==7) ? "store8" : "store" + IntToString(i+5); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=8; i<12; i++) + { + f = IntToFloat(i+12); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<11) ? "store" + IntToString(i+1) : "store8"; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamCuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamCuboctahedron(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamSmallRhombicuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_SMALLRHOMBICUBOCTAHEDRON") +{ + int i; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float fSigma1 = fRadius*0.50544946512442356216037311029756; + float fSigma2 = fRadius*0.93394883109446475957738040414503; + float fEpsilon1 = fRadius*0.13714379053898318189115991804927; + float fEpsilon2 = fRadius*0.71481348867318651189693394330755; + float fDelay = fTime/48.0; + fWait += fDelay; + float f, z, fAngle; + location lPos; + string sNumber1, sNumber2; + + object oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 24))); + + for (i = 0; i < 24; i++) + { + f = IntToFloat(i); + if (i<4) + { + fAngle = fRotate + f*90.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma1*cos(fAngle), fSigma1*sin(fAngle), fEpsilon1, fDirectionXZ, fDirectionYZ), fAngle); + } + else if (i<20) + { + fAngle = fRotate + 27.5 + (f-4.0)*45.0; + z = (i<12) ? fEpsilon1 + fSigma1 : fEpsilon1 + fEpsilon2 + fSigma1; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma2*cos(fAngle), fSigma2*sin(fAngle), z, fDirectionXZ, fDirectionYZ), fAngle); + } + else + { + fAngle = fRotate + f*90.0; + lPos = Location(oArea, gao_RotateVector(vCenter, sAxis, fSigma1*cos(fAngle), fSigma1*sin(fAngle), fEpsilon1 + 2.0*fSigma1 + fEpsilon2, fDirectionXZ, fDirectionYZ), fAngle); + } + sNumber1 = "store"+IntToString(i); + DelayCommand(fDelay*f, gao_ActionCreateLocalObject(sTemplate, lPos, sNumber1, oData, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime)); + } + + for (i=0; i<24; i++) + { + f = (i<4) ? IntToFloat(i) : (i<12) ? IntToFloat(i+8) : (i<20) ? IntToFloat(i+16) : IntToFloat(i+24) ; + sNumber1 = "store" + IntToString(i); + sNumber2 = (i==3) ? "store0" : (i==11) ? "store4" : (i==19) ? "store12" : (i==23) ? "store20" : "store" + IntToString(i+1) ; + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=0; i<20; i++) + { + f = (i<4) ? IntToFloat(i*2+4) : (i<12) ? IntToFloat(i+16) : IntToFloat(i+24); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i<4) ? "store" + IntToString(i*2+4) : (i<12) ? "store" + IntToString(i+8) : (i==13||i== 15||i==17) ? "store" + IntToString(21+(i-13)/2) : (i==19) ? "store20" : (i==18) ? "store23" : "store" + IntToString(19+(i-10)/2); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + for (i=0; i<4; i++) + { + f = (i==0) ? 11.0 : IntToFloat((i-1)*2+5); + sNumber1 = "store" + IntToString(i); + sNumber2 = (i==0) ? "store11" : "store" + IntToString(i*2+3); + DelayCommand(fDelay*f+fWait, gao_ActionApplyLocalBeamEffect(oData, sNumber1, sNumber2, nDurationType, nVFX, fDuration)); + } + + if (fLifetime > 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamSmallRhombicuboctahedron(int nDurationType, int nVFX, location lCenter, float fRadius, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamSmallRhombicuboctahedron(nDurationType, nVFX, lCenter, fRadius, fDuration, sTemplate, fTime, fWait, fRotate, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} + +object ObjectBeamGengon(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, float fTwist=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f, string sTag="PSC_B_GENGON") +{ + int i; + if (nSides < 3) nSides = 3; + if (fWait < 1.0) fWait = 1.0; + if (fWait2 < 1.0) fWait2 = 1.0; + if (fTime < 0.0) fTime = 6.0; + if (fLifetime < 0.0) fLifetime = 0.0; + if (sTemplate == "") sTemplate = "prc_invisobj"; + float fEta = 360.0/IntToFloat(nSides); // angle of segment + float fDelay = fTime/IntToFloat(3*nSides); // delay per edge + vector vCenter = GetPositionFromLocation(lCenter); + object oArea = GetAreaFromLocation(lCenter); + float f, fAngle; + object oData; + location lPos; + fTwist += fRotate; + fWait += fDelay; + string sNumber1, sNumber2; + + oData = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lCenter, FALSE, sTag); + AssignCommand(oData, ActionDoCommand(SetLocalInt(oData, "storetotal", 2*nSides))); + + for (i = 0; i < 2*nSides; i++) + { + f = IntToFloat(i); + if (i 0.0) + { + DestroyObject(oData, fLifetime + fTime + fWait + fWait2 + 5.0); + return OBJECT_INVALID; + } + return oData; +} + +void BeamGengon(int nDurationType, int nVFX, location lCenter, float fRadiusStart, float fRadiusEnd=0.0f, float fHeightStart=0.0f, float fHeightEnd=5.0f, int nSides=3, float fDuration=0.0f, string sTemplate="", float fTime=6.0f, float fWait=1.0f, float fRotate=0.0f, float fTwist=0.0f, string sAxis="z", float fDirectionXZ = 0.0f, float fDirectionYZ = 0.0f, int nDurationType2=-1, int nVFX2=-1, float fDuration2=0.0f, float fWait2=1.0f, float fLifetime=0.0f) +{ + ObjectBeamGengon(nDurationType, nVFX, lCenter, fRadiusStart, fRadiusEnd, fHeightStart, fHeightEnd, nSides, fDuration, sTemplate, fTime, fWait, fRotate, fTwist, sAxis, fDirectionXZ, fDirectionYZ, nDurationType2, nVFX2, fDuration2, fWait2, fLifetime); +} diff --git a/src/include/inc_draw_prc.nss b/src/include/inc_draw_prc.nss new file mode 100644 index 0000000..b1823c1 --- /dev/null +++ b/src/include/inc_draw_prc.nss @@ -0,0 +1,65 @@ +//:://///////////////////////////////////////////// +//:: Drawing include - PRC-created functions +//:: inc_draw_prc +//:://///////////////////////////////////////////// +/** @file + PRC extensions and additions to gaoneng's + Pentagrams & Summoning Circles system. + + @author Ornedan + @date Created - 2005.12.05 +*/ +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * An attempt at conical VFX. Draws a bunch from the center towards the edge + * of a quarter-circle. + * + * @param nLines Number of lines to draw + * @param fLength Length of the cone + * @param lOrigin The origin of the cone + * @param fDirection The direction of the cone + * + * @param nDurationType The duration type of the applied VFX + * @param nVFX Visual effect to use + * @param fDuration Duration of the visualeffects, if temporary + * @param nFrequency How many VFX per line + * @param fTime How long it takes to draw the whole thing + */ +void DrawLinesInACone(int nLines, float fLength, location lOrigin, float fDirection, + int nDurationType, int nVFX, float fDuration, int nFrequency, float fTime); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_draw" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void DrawLinesInACone(int nLines, float fLength, location lOrigin, float fDirection, + int nDurationType, int nVFX, float fDuration, int nFrequency, float fTime) +{ + float fTheta = 90.0f / nLines; + vector vCenter = GetPositionFromLocation(lOrigin); + object oArea = GetAreaFromLocation(lOrigin); + + int i; + float f, fAngle; + vector vTarget; + for(i = 0; i < nLines; i++) + { + f = IntToFloat(i); + fAngle = fTheta * f + (fDirection - 45.0); + vTarget = vCenter + Vector(cos(fAngle), sin(fAngle), 0.0f); + DrawLineFromVectorToVector(nDurationType, nVFX, oArea, vCenter, vTarget, fDuration, nFrequency, fTime); + } +} diff --git a/src/include/inc_draw_text.nss b/src/include/inc_draw_text.nss new file mode 100644 index 0000000..6464d78 --- /dev/null +++ b/src/include/inc_draw_text.nss @@ -0,0 +1,366 @@ +/* + ============================================= + PENTAGRAMS & SUMMONING CIRCLES - + BEAM-STYLE TEXT + ============================================= + gaoneng January 17, 2005 + #include "inc_draw_text" + + last updated on April 25, 2005 + + Extension library for PENTAGRAMS & SUMMONING + CIRCLES. Used for creating text display. + ============================================= +*/ + +/* + ================================== + FUNCTIONS DECLARATIONS + ================================== +*/ +// Assigns oData to display sMessage +// ================================= +// sMessage = message to display +// oData = target group +// fSpeed = second per letter +// fLifetime = seconds text lasts +// fFontHeight = height of font in meters +// fFontWidth = width of font in meters +// nVFX = VFX_BEAM_* constant +void TextMessage(string sMessage, object oData, float fSpeed=2.0f, float fLifetime=0.0f, float fFontHeight=0.5f, float fFontWidth=0.25f, int nVFX=VFX_BEAM_FIRE_W_SILENT); + +/* + ================================== + PRIVATE FUNCTIONS + ================================== +*/ + +// void gao_BeamLetter(object oNode, string sAlphabet, int nVFX, int nDurationType=2, float fFlashRate=0.0f); +// void gao_CreateTextGrid(object oNode, float fFontHeight, float fFontWidth); +// void gao_AlphabetBlink(object oNode, string sAlphabet, int nVFX, float fFlashRate, float fLifetime); +// void gao_AlphabetPermanent(object oNode, string sAlphabet, int nVFX, float fLifetime); +// void gao_AlphabetScroll(object oNode, string sMessage, int nVFX, float fFlashRate, float fLifetime=0.0f); +// string gao_ReverseMessage(string sMessage); + + +/* + ================================== + FUNCTIONS IMPLEMENTATIONS + ================================== +*/ +void gao_CreateTextGrid(object oNode, float fFontHeight, float fFontWidth, object oData, float fLifetime) +{ + object oArea = GetArea(oNode); + vector vPos = GetPosition(oNode); + float fFacing = GetFacing(oNode); + vector vFacing = AngleToVector(fFacing + 90.0); + vector vLedNode; + object oLedNode; + fFontWidth /= 2.0; + fFontHeight /= 2.0; + + int i, j, nTotal; + float f, g; + + for (i=0; i<3; i++) + { + f = IntToFloat(i); + + for (j=0; j<3; j++) + { + g = IntToFloat(j-1); + vLedNode = vPos - fFontWidth*g*vFacing + f*Vector(0.0, 0.0, fFontHeight); + oLedNode = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", Location(oArea, vLedNode, fFacing), FALSE, "PSC_X_TEXTMESSAGE"); + AssignCommand(oLedNode, ActionDoCommand(SetLocalObject(oNode, "led" + IntToString(j) + IntToString(i), oLedNode))); + if (fLifetime == 0.0) + { + nTotal = GetLocalInt(oData, "storetotal"); + AssignCommand(oLedNode, ActionDoCommand(SetLocalObject(oData, "store" + IntToString(nTotal), oLedNode))); + SetLocalInt(oData, "storetotal", nTotal + 1); + } + } + } +} + +void gao_DestroyTextGrid(object oNode) +{ + DestroyObject(GetLocalObject(oNode, "led00")); + DestroyObject(GetLocalObject(oNode, "led10")); + DestroyObject(GetLocalObject(oNode, "led20")); + DestroyObject(GetLocalObject(oNode, "led01")); + DestroyObject(GetLocalObject(oNode, "led11")); + DestroyObject(GetLocalObject(oNode, "led21")); + DestroyObject(GetLocalObject(oNode, "led02")); + DestroyObject(GetLocalObject(oNode, "led12")); + DestroyObject(GetLocalObject(oNode, "led22")); +} + +void gao_BeamLetter(object oNode, string sAlphabet, int nVFX) +{ + object oNode1 = GetLocalObject(oNode, "led00"); + object oNode2 = GetLocalObject(oNode, "led10"); + object oNode3 = GetLocalObject(oNode, "led20"); + object oNode4 = GetLocalObject(oNode, "led01"); + object oNode5 = GetLocalObject(oNode, "led11"); + object oNode6 = GetLocalObject(oNode, "led21"); + object oNode7 = GetLocalObject(oNode, "led02"); + object oNode8 = GetLocalObject(oNode, "led12"); + object oNode9 = GetLocalObject(oNode, "led22"); + + if (sAlphabet == "a") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "b") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode8); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode8); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode6); + } + else if (sAlphabet == "c") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "d") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode2); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode8, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode8); + } + else if (sAlphabet == "e") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode5); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "f") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode5); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "g") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "h") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + } + else if (sAlphabet == "i") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode8); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "j") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode4); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "k") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode5); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode5); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "l") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + } + else if (sAlphabet == "m") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode8); + } + else if (sAlphabet == "n") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "o" || sAlphabet == "0") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "p") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode6, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "q") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode3); + } + else if (sAlphabet == "r") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode6, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode5); + } + else if (sAlphabet == "s" || sAlphabet == "5") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode6); + } + else if (sAlphabet == "t") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode8); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "u") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "v") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "w") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode5); + } + else if (sAlphabet == "x") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode7); + } + else if (sAlphabet == "y") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode5, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode2, BODY_NODE_CHEST), oNode5); + } + else if (sAlphabet == "z") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "1") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "2") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode4); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode6, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "3") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "4") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "6") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode6); + } + else if (sAlphabet == "7") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "8") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + } + else if (sAlphabet == "9") + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode1, BODY_NODE_CHEST), oNode3); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode6); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode7, BODY_NODE_CHEST), oNode9); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode4, BODY_NODE_CHEST), oNode7); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectBeam(nVFX, oNode3, BODY_NODE_CHEST), oNode9); + } + else gao_DestroyTextGrid(oNode); +} + +void gao_AlphabetPermanent(object oNode, string sAlphabet, int nVFX, float fLifetime) +{ + gao_BeamLetter(oNode, sAlphabet, nVFX); + if (fLifetime > 0.0) DelayCommand(fLifetime, gao_DestroyTextGrid(oNode)); +} + +void TextMessage(string sMessage, object oData=OBJECT_SELF, float fSpeed=2.0f, float fLifetime=0.0f, float fFontHeight=0.5f, float fFontWidth=0.25f, int nVFX=VFX_BEAM_FIRE_W_SILENT) +{ + int i; + object oNode; + + sMessage = GetStringLowerCase(sMessage); + int nLength = GetStringLength(sMessage); + int nData = GetLocalInt(oData, "storetotal"); + + if (nLength > nData) nLength = nData; + + for (i=0; i-1; i--) + { + DelayCommand(fBreak*IntToFloat(j), ExecuteScript(sScript, GetLocalObject(oData, "store" + IntToString(i)))); + j++; + } + } + else + { + for (i=0; i-1; i--) + { + DelayCommand(fDelay + fBreak*IntToFloat(j), DestroyObject(GetLocalObject(oData, "store" + IntToString(i)))); + j++; + } + } + else + { + for (i=0; i-1; i--) + { + DelayCommand(fBreak*IntToFloat(j), ApplyEffectToObject(nDurationType, eEffect, GetLocalObject(oData, "store" + IntToString(i)), fDuration)); + j++; + } + } + else + { + for (i=0; i-1; i--) + { + oNode = GetLocalObject(oData, "store" + IntToString(i)); + DelayCommand(fBreak*IntToFloat(j), AssignCommand(oNode, SetFacing(GetFacing(oNode) + fDirection))); + j++; + } + } + else + { + for (i=nTotal-1; i>-1; i--) + { + DelayCommand(fBreak*IntToFloat(j), AssignCommand(GetLocalObject(oData, "store" + IntToString(i)), SetFacing(fDirection))); + j++; + } + } + } + else + { + if (bRelative) + { + object oNode; + for (i=0; i-1; i--) + { + DelayCommand(fBreak*IntToFloat(j), AssignCommand(GetLocalObject(oData, "store" + IntToString(i)), SetFacingPoint(vTarget))); + j++; + } + } + else + { + for (i=0; i-1; i--) + { + DelayCommand(fBreak*IntToFloat(j), AssignCommand(GetLocalObject(oData, "store" + IntToString(i)), PlayAnimation(nAnimation, fSpeed))); + j++; + } + } + else + { + for (i=0; i 0) SetLocalInt(oPC, "DynConv_Stack", nStack - 1); + else DeleteLocalInt(oPC, "DynConv_Stack"); + + // Store the date in the conversation variables + SetLocalInt(oPC, DYNCONV_STAGE, nStage); + SetLocalInt(oPC, "DynConv_AllowExit", nAllowExit); + SetLocalInt(oPC, "DynConv_AllowAbort", nAllowAbort); + SetLocalString(oPC, DYNCONV_SCRIPT, sScript); + + // Restart the conversation + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, ActionStartConversation(oPC, "dyncov_base", TRUE, FALSE)); + } + // Fully exited the conversation. Clean up + else + { + if(DEBUG) DoDebug("_DynConvInternal_ExitedConvo(): Fully exited conversation"); + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + array_delete(oPC, "StagesSetup"); + + DeleteLocalInt(oPC, "ChoiceOffset"); + DeleteLocalInt(oPC, "DynConv_AllowExit"); + DeleteLocalInt(oPC, "DynConv_AllowAbort"); + + DeleteLocalInt(oPC, DYNCONV_VARIABLE); + DeleteLocalInt(oPC, DYNCONV_STAGE); + DeleteLocalString(oPC, DYNCONV_SCRIPT); + DeleteLocalString(oPC, "DynConv_HeaderText"); + int i; + for(i = DYNCONV_MIN_TOKEN; i <= DYNCONV_MAX_TOKEN; i++) + DeleteLocalString(oPC, GetTokenIDString(i)); + } + } +} + +void _DynConvInternal_RunScript(object oPC, int nDynConvVar) +{ + if(!GetLocalInt(oPC, "DynConv_RestartMarker")) + { + _DynConvInternal_PreScript(oPC); + string sScript = GetLocalString(oPC, DYNCONV_SCRIPT); + SetLocalInt(oPC, DYNCONV_VARIABLE, nDynConvVar); + ExecuteScript(sScript, OBJECT_SELF); + _DynConvInternal_PostScript(oPC); + } + else + { + SetupTokens(oPC); + DeleteLocalInt(oPC, "DynConv_RestartMarker"); + } +} + +void _DynConvInternal_PreScript(object oPC) +{ + // Create the choice arrays + array_create(oPC, "ChoiceTokens"); + array_create(oPC, "ChoiceValues"); +} + +void _DynConvInternal_PostScript(object oPC) +{ + // If debugging is active, check that the conversations have at least one response node + // when exiting is off + if(DEBUG) + { + if(GetLocalInt(oPC, DYNCONV_VARIABLE) == DYNCONV_SETUP_STAGE && + GetLocalInt(oPC, "DynConv_AllowExit") == DYNCONV_EXIT_NOT_ALLOWED && + array_get_size(oPC, "ChoiceTokens") == 0 + ) + { + DoDebug("Dynconvo ERROR: No response tokens set up and exiting not allowed!"); + } + } +} + +object _DynConvInternal_ResolvePC(object oPC) +{ + return oPC == OBJECT_INVALID ? GetPCSpeaker() : oPC; // If no valid PC reference was passed, get it via GetPCSpeaker +} + +void SetStage(int nNewStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + // No need to act if the stage wasn't changed + if(nNewStage != GetStage(oPC)) + { + + SetLocalInt(oPC, DYNCONV_STAGE, nNewStage); + + // Clear the choice data + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + DeleteLocalInt(oPC, "ChoiceOffset"); + } +} + +int GetStage(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + return GetLocalInt(oPC, DYNCONV_STAGE); +} + +int GetChoice(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + return array_get_int(oPC, "ChoiceValues", GetLocalInt(oPC, DYNCONV_VARIABLE) // Number of choice + - 1 // Which begins at index 1 instead of the index 0 we need here + + GetLocalInt(oPC, "ChoiceOffset")); +} + +string GetChoiceText(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + return array_get_string(oPC, "ChoiceTokens", GetLocalInt(oPC, DYNCONV_VARIABLE) // Number of choice + - 1 // Which begins at index 1 instead of the index 0 we need here + + GetLocalInt(oPC, "ChoiceOffset")); +} + +void StartDynamicConversation(string sConversationScript, object oPC, + int nAllowExit = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bAllowAbort = FALSE, + int bForceStart = FALSE, object oConverseWith = OBJECT_INVALID) +{ + if(IsInConversation(oPC)) + { + if(DEBUG) DoDebug("StartDynamicConversation(): Aborting--already in conversation"); + return; + } + + if(DEBUG) DoDebug("StartDynamicConversation(): Starting new dynamic conversation, parameters:\n" + + "sConversationScript = '" + sConversationScript + "'\n" + + "oPC = " + DebugObject2Str(oPC) + "\n" + + "nAllowExit = " + (nAllowExit == DYNCONV_EXIT_NOT_ALLOWED ? "DYNCONV_EXIT_NOT_ALLOWED" : + nAllowExit == DYNCONV_EXIT_FORCE_EXIT ? "DYNCONV_EXIT_FORCE_EXIT" : + nAllowExit == DYNCONV_EXIT_ALLOWED_SHOW_CHOICE ? "DYNCONV_EXIT_ALLOWED_SHOW_CHOICE" : + "ERROR: Unsupported value: " + IntToString(nAllowExit) + ) + "\n" + + "bAllowAbort = " + DebugBool2String(bAllowAbort) + "\n" + + "bForceStart = " + DebugBool2String(bForceStart) + "\n" + + "oConverseWith = " + DebugObject2Str(oConverseWith) + "\n" + ); + // By default, the PC converses with itself + oConverseWith = oConverseWith == OBJECT_INVALID ? oPC : oConverseWith; + if(DEBUG) if(!GetIsObjectValid(oConverseWith)) DoDebug("StartDynamicConversation(): ERROR: oConverseWith is not valid!"); + + // Store the exit control variables + SetLocalInt(oPC, "DynConv_AllowExit", nAllowExit); + SetLocalInt(oPC, "DynConv_AllowAbort", bAllowAbort); + + // Initiate conversation + if(bForceStart) AssignCommand(oPC, ClearAllActions(TRUE)); + SetLocalString(oPC, DYNCONV_SCRIPT, sConversationScript); + AssignCommand(oPC, ActionStartConversation(oConverseWith, "dyncov_base", TRUE, FALSE)); +} + +void BranchDynamicConversation(string sConversationToEnter, int nStageToReturnTo, + int nAllowExit = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bAllowAbort = FALSE, + object oPC = OBJECT_INVALID) +{ + if(DEBUG) DoDebug("BranchDynamicConversation(): Entering another dynamic conversation, parameters:\n" + + "sConversationToEnter = '" + sConversationToEnter + "'\n" + + "nStageToReturnTo = " + IntToString(nStageToReturnTo) + "\n" + + "nAllowExit = " + (nAllowExit == DYNCONV_EXIT_NOT_ALLOWED ? "DYNCONV_EXIT_NOT_ALLOWED" : + nAllowExit == DYNCONV_EXIT_FORCE_EXIT ? "DYNCONV_EXIT_FORCE_EXIT" : + nAllowExit == DYNCONV_EXIT_ALLOWED_SHOW_CHOICE ? "DYNCONV_EXIT_ALLOWED_SHOW_CHOICE" : + "ERROR: Unsupported value: " + IntToString(nAllowExit) + ) + "\n" + + "bAllowAbort = " + DebugBool2String(bAllowAbort) + "\n" + + "oPC = " + DebugObject2Str(oPC) + "\n " + ); + oPC = _DynConvInternal_ResolvePC(oPC); + // Get current stack level + int nStack = GetLocalInt(oPC, "DynConv_Stack") + 1; + + // Push the return data onto the stack + SetLocalInt(oPC, "DynConv_Stack_ReturnToStage_" + IntToString(nStack), nStageToReturnTo); + SetLocalInt(oPC, "DynConv_Stack_AllowExit_" + IntToString(nStack), + GetLocalInt(oPC, "DynConv_AllowExit")); + SetLocalInt(oPC, "DynConv_Stack_AllowAbort_" + IntToString(nStack), + GetLocalInt(oPC, "DynConv_AllowAbort")); + SetLocalString(oPC, "DynConv_Stack_Script_" + IntToString(nStack), + GetLocalString(oPC, DYNCONV_SCRIPT)); + SetLocalInt(oPC, "DynConv_Stack", nStack); + + // Clean the current conversation data + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + array_delete(oPC, "StagesSetup"); + DeleteLocalInt(oPC, "ChoiceOffset"); + DeleteLocalInt(oPC, DYNCONV_STAGE); + + // Set the new conversation as active + SetLocalString(oPC, DYNCONV_SCRIPT, sConversationToEnter); + SetLocalInt(oPC, "DynConv_AllowExit", nAllowExit); + SetLocalInt(oPC, "DynConv_AllowAbort", bAllowAbort); +} + +/// @todo Rename to SetExitable +void AllowExit(int nNewValue = DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, int bChangeExitTokenText = TRUE, object oPC = OBJECT_INVALID) +{ + if(DEBUG) DoDebug("AllowExit():\n" + + "nNewValue = " + (nNewValue == DYNCONV_EXIT_NOT_ALLOWED ? "DYNCONV_EXIT_NOT_ALLOWED" : + nNewValue == DYNCONV_EXIT_FORCE_EXIT ? "DYNCONV_EXIT_FORCE_EXIT" : + nNewValue == DYNCONV_EXIT_ALLOWED_SHOW_CHOICE ? "DYNCONV_EXIT_ALLOWED_SHOW_CHOICE" : + "ERROR: Unsupported value: " + IntToString(nNewValue) + ) + "\n" + + "bChangeExitTokenText = " + DebugBool2String(bChangeExitTokenText) + "\n" + + "oPC = " + DebugObject2Str(_DynConvInternal_ResolvePC(oPC)) + "\n" + ); + + SetLocalInt(_DynConvInternal_ResolvePC(oPC), "DynConv_AllowExit", nNewValue); + if(bChangeExitTokenText) + SetCustomToken(DYNCONV_TOKEN_EXIT, GetStringByStrRef(DYNCONV_STRREF_EXIT_CONVO)); +} + +/// @todo Replace with SetAbortable(int bAllow, object oPC = OBJECT_INVALID) +void AllowAbort(object oPC = OBJECT_INVALID) +{ + SetLocalInt(_DynConvInternal_ResolvePC(oPC), "DynConv_AllowAbort", TRUE); +} + +int GetIsStageSetUp(int nStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + if(!array_exists(oPC, "StagesSetup")) + return FALSE; + return array_get_int(oPC, "StagesSetup", nStage); +} + +void MarkStageSetUp(int nStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + if(!array_exists(oPC, "StagesSetup")) + array_create(oPC, "StagesSetup"); + array_set_int(oPC, "StagesSetup", nStage, TRUE); +} + +void MarkStageNotSetUp(int nStage, object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + if(!array_exists(oPC, "StagesSetup")) + return; + array_set_int(oPC, "StagesSetup", nStage, FALSE); +} + +void ClearCurrentStage(object oPC = OBJECT_INVALID) +{ + oPC = _DynConvInternal_ResolvePC(oPC); + + // Clear the choice data + array_delete(oPC, "ChoiceTokens"); + array_delete(oPC, "ChoiceValues"); + DeleteLocalInt(oPC, "ChoiceOffset"); + + MarkStageNotSetUp(GetStage(oPC), oPC); +} diff --git a/src/include/inc_ecl.nss b/src/include/inc_ecl.nss new file mode 100644 index 0000000..541f9b9 --- /dev/null +++ b/src/include/inc_ecl.nss @@ -0,0 +1,371 @@ +/** @file + * ECL handling. + * + * @author Primogenitor + * + * @todo Primo, could you document this one? More details to header and comment function prototypes + */ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +// returns oTarget's LA value, including their race and template(s) LA +int GetTotalLA(object oTarget); + +// returns oTarget's level adjusted by their LA +int GetECL(object oTarget); +void GiveXPReward(object oCreature, int nXP, int bIsPC = TRUE); +void GiveXPRewardToParty(object oKiller, object oDead, int nCR = 0); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "inc_utility" +//#include "prc_inc_template" +#include "inc_npc" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetTotalLA(object oTarget) +{ + int nLA; + int nRace = GetRacialType(oTarget); + if(GetPRCSwitch(PRC_XP_USE_SIMPLE_LA)) + nLA += StringToInt(Get2DACache("ECL", "LA", nRace)); + if(GetPRCSwitch(PRC_XP_INCLUDE_RACIAL_HIT_DIE_IN_LA)) + nLA += StringToInt(Get2DACache("ECL", "RaceHD", nRace)); + nLA += GetPersistantLocalInt(oTarget, "template_LA"); + nLA -= GetPersistantLocalInt(oTarget, "LA_Buyoff"); + return nLA; +} + +int GetECL(object oTarget) +{ + int nLevel; + // we need to use a derivation of the base xp formular to compute the + // pc level based on total XP. + // + // base XP formula (x = pc level, t = total xp): + // + // t = x * (x-1) * 500 + // + // need to use some base math.. + // transform for pq formula use (remove brackets with x inside and zero right side) + // + // x^2 - x - (t / 500) = 0 + // + // use pq formula to solve it [ x^2 + px + q = 0, p = -1, q = -(t/500) ]... + // + // that's our new formula to get the level based on total xp: + // level = 0.5 + sqrt(0.25 + (t/500)) + // + if(GetPRCSwitch(PRC_ECL_USES_XP_NOT_HD) && GetIsPC(oTarget)) + nLevel = FloatToInt(0.5 + sqrt(0.25 + ( IntToFloat(GetXP(oTarget)) / 500 ))); + else + nLevel = GetHitDice(oTarget); + nLevel += GetTotalLA(oTarget); + return nLevel; +} + +int CheckDistance(object oDead, object oTest) +{ + if(GetPRCSwitch(PRC_XP_MUST_BE_IN_AREA)) + { + if(GetArea(oDead) != GetArea(oTest)) + return FALSE; + if(GetDistanceBetween(oDead, oTest) > IntToFloat(GetPRCSwitch(PRC_XP_MAX_PHYSICAL_DISTANCE))) + return FALSE; + } + return TRUE; +} + +float GetGroupBonusModifier(int nPartySize) +{ + return 1 + ((nPartySize-1) * IntToFloat(GetPRCSwitch(PRC_XP_GROUP_BONUS))/100.0); +} + +void GiveXPRewardToParty(object oKiller, object oDead, int nCR = 0) +{ + //get prc switches + int bSPAM = GetPRCSwitch(PRC_XP_DISABLE_SPAM); + float fPRC_XP_DIVISOR_PC = IntToFloat(GetPRCSwitch(PRC_XP_PC_PARTY_COUNT_x100))/100.0; + float fPRC_XP_DIVISOR_HENCHMAN = IntToFloat(GetPRCSwitch(PRC_XP_HENCHMAN_PARTY_COUNT_x100))/100.0; + float fPRC_XP_DIVISOR_ANIMALCOMPANION = IntToFloat(GetPRCSwitch(PRC_XP_ANIMALCOMPANION_PARTY_COUNT_x100))/100.0; + float fPRC_XP_DIVISOR_FAMILIAR = IntToFloat(GetPRCSwitch(PRC_XP_FAMILIAR_PARTY_COUNT_x100))/100.0; + float fPRC_XP_DIVISOR_DOMINATED = IntToFloat(GetPRCSwitch(PRC_XP_DOMINATED_PARTY_COUNT_x100))/100.0; + float fPRC_XP_DIVISOR_SUMMONED = IntToFloat(GetPRCSwitch(PRC_XP_SUMMONED_PARTY_COUNT_x100))/100.0; + float fPRC_XP_DIVISOR_UNKNOWN = IntToFloat(GetPRCSwitch(PRC_XP_UNKNOWN_PARTY_COUNT_x100))/100.0; + + //number of PCs in the party, average party level + int nPartySize, nAvgLevel; + //xp divisor + float fDivisor; + + //get some basic group data like average PC level , PC group size, and XP divisor + object oTest = GetFirstFactionMember(oKiller, FALSE); + while(GetIsObjectValid(oTest)) + { + if(CheckDistance(oDead, oTest)) + { + if(GetIsPC(oTest)) + { + nPartySize++; + nAvgLevel += GetECL(oTest); + fDivisor += fPRC_XP_DIVISOR_PC; + } + else + { + switch(GetAssociateTypeNPC(oTest)) + { + case ASSOCIATE_TYPE_HENCHMAN: fDivisor += fPRC_XP_DIVISOR_HENCHMAN; break; + case ASSOCIATE_TYPE_ANIMALCOMPANION: fDivisor += fPRC_XP_DIVISOR_ANIMALCOMPANION; break; + case ASSOCIATE_TYPE_FAMILIAR: fDivisor += fPRC_XP_DIVISOR_FAMILIAR; break; + case ASSOCIATE_TYPE_DOMINATED: fDivisor += fPRC_XP_DIVISOR_DOMINATED; break; + case ASSOCIATE_TYPE_SUMMONED: fDivisor += fPRC_XP_DIVISOR_SUMMONED; break; + default: fDivisor += fPRC_XP_DIVISOR_UNKNOWN; break; + } + } + } + else if(!bSPAM && GetIsPC(oTest)) + SendMessageToPC(oTest, "You are too far away from the combat to gain any experience."); + + oTest = GetNextFactionMember(oKiller, FALSE); + } + + //in case something weird is happenening + if(fDivisor == 0.0f) + return; + + //calculate average partylevel + nAvgLevel /= nPartySize; + + int nBaseXP; + if(!nCR) nCR = GetPRCSwitch(PRC_XP_USE_ECL_NOT_CR) ? GetECL(oDead) : FloatToInt(GetChallengeRating(oDead)); + if(nCR < 1) nCR = 1; + + if(GetPRCSwitch(PRC_XP_USE_BIOWARE_XPTABLE)) + { + if(nCR > 40) nCR = 40; + if(nAvgLevel > 40) nAvgLevel = 40; + nBaseXP = StringToInt(Get2DACache("xptable", "C"+IntToString(nCR), nAvgLevel-1)); + } + else + { + if(nCR > 70) nCR = 70; + if(nAvgLevel > 60) nAvgLevel = 60; + nBaseXP = StringToInt(Get2DACache("dmgxp", IntToString(nCR), nAvgLevel-1)); + } + + //average xp per party member + int nXPAward = FloatToInt(IntToFloat(nBaseXP)/fDivisor); + + //now the module slider + nXPAward = FloatToInt(IntToFloat(nXPAward) * IntToFloat(GetPRCSwitch(PRC_XP_SLIDER_x100))/100.0); + + //xp = 0, quit + if(!nXPAward) + return; + + //group bonus + nXPAward = FloatToInt(IntToFloat(nXPAward) * GetGroupBonusModifier(nPartySize)); + + int nKillerLevel = GetECL(oKiller); + //calculate xp for each party member individually + oTest = GetFirstFactionMember(oKiller, FALSE); + float fPCAdjust; + int nMbrLevel; + while(GetIsObjectValid(oTest)) + { + if(CheckDistance(oDead, oTest)) + { + if(GetIsPC(oTest)) + { + nMbrLevel = GetECL(oTest); + if(abs(nMbrLevel - nKillerLevel) <= GetPRCSwitch(PRC_XP_MAX_LEVEL_DIFF)) + { + //now the individual slider + fPCAdjust = IntToFloat(GetLocalInt(oTest, PRC_XP_SLIDER_x100))/100.0; + if(fPCAdjust == 0.0) fPCAdjust = 1.0; + nXPAward = FloatToInt(IntToFloat(nXPAward) * fPCAdjust); + + GiveXPReward(oTest, nXPAward); + } + else if(!bSPAM) + SendMessageToPC(oTest, "You are too high level to gain any experience."); + } + else + GiveXPReward(oTest, nXPAward, FALSE); + } + oTest = GetNextFactionMember(oKiller, FALSE); + } +} + +void GiveXPReward(object oCreature, int nXP, int bIsPC = TRUE) +{ + //actually give the XP + if(bIsPC) + { + if(GetPRCSwitch(PRC_XP_USE_SETXP)) + SetXP(oCreature, GetXP(oCreature)+nXP); + else + GiveXPToCreature(oCreature, nXP); + } + else if(GetPRCSwitch(PRC_XP_GIVE_XP_TO_NPCS)) + SetLocalInt(oCreature, "NPC_XP", GetLocalInt(oCreature, "NPC_XP")+nXP); +} + +//:://///////////////////////////////////////////// +//:: Effective Character Level Experience Script +//:: ecl_exp +//:: Copyright (c) 2004 Theo Brinkman +//::////////////////////////////////////////////// +/* +Call ApplyECLToXP() from applicable heartbeat script(s) +to cause experience to be adjusted according to ECL. +*/ +//::////////////////////////////////////////////// +//:: Created By: Theo Brinkman +//:: Last Updated On: 2004-07-28 +//::////////////////////////////////////////////// +// CONSTANTS +const string sLEVEL_ADJUSTMENT = "ecl_LevelAdjustment"; +const string sXP_AT_LAST_HEARTBEAT = "ecl_LastExperience"; + +int GetXPForLevel(int nLevel) +{ + return nLevel*(nLevel-1)*500; +} + +void ApplyECLToXP(object oPC) +{ + //abort if simple LA is disabled + if(!GetPRCSwitch(PRC_XP_USE_SIMPLE_LA)) + return; + + //abort if it's not valid, still loading, or a PC + if(!GetIsObjectValid(oPC) || GetLocalInt(oPC, "PRC_ECL_Delay") || !GetIsPC(oPC)) + return; + + // Abort if they are registering as a cohort + if(GetLocalInt(oPC, "OriginalXP") || GetPersistantLocalInt(oPC, "RegisteringAsCohort")) + return; + + // Let them make it to level 3 in peace + if(GetTag(GetModule()) == "Prelude") + return; + + // And start HotU in peace + if(GetTag(GetArea(oPC)) == "q2a_yprooms") + return; + + // Abort if they were just relevelled + if(GetLocalInt(oPC, "RelevelXP")) + { + DeleteLocalInt(oPC, "RelevelXP"); + return; + } + + //this is done first because leadership uses it too + int iCurXP = GetXP(oPC); + //if (DEBUG) DoDebug("ApplyECLToXP - iCurXP "+IntToString(iCurXP)); + + int iLastXP = GetPersistantLocalInt(oPC, sXP_AT_LAST_HEARTBEAT); + //if (DEBUG) DoDebug("ApplyECLToXP - iLastXP "+IntToString(iLastXP)); + if(iCurXP > iLastXP) + { + //if (DEBUG) DoDebug("ApplyECLToXP - gained XP"); + int iLvlAdj = GetTotalLA(oPC); + if(iLvlAdj) + { + //if (DEBUG) DoDebug("ApplyECLToXP - have LA"); + int iPCLvl = GetHitDice(oPC); + // Get XP Ratio (multiply new XP by this to see what to subtract) + float fRealXPToLevel = IntToFloat(GetXPForLevel(iPCLvl+1)); + float fECLXPToLevel = IntToFloat(GetXPForLevel(iPCLvl+1+iLvlAdj)); + float fXPRatio = 1.0 - (fRealXPToLevel/fECLXPToLevel); + //At this point the ratio is based on total XP + //This is not correct, it should be based on the XP required to reach + //the next level. + //fRealXPToLevel = IntToFloat(iPCLvl*1000); + //fECLXPToLevel = IntToFloat((iPCLvl+iLvlAdj)*1000); + //fXPRatio = 1.0 - (fRealXPToLevel/fECLXPToLevel); + + float fXPDif = IntToFloat(iCurXP - iLastXP); + int iXPDif = FloatToInt(fXPDif * fXPRatio); + int newXP = iCurXP - iXPDif; + SendMessageToPC(oPC, "XP gained since last heartbeat "+IntToString(FloatToInt(fXPDif))); + SendMessageToPC(oPC, "Real XP to level: "+IntToString(FloatToInt(fRealXPToLevel))); + SendMessageToPC(oPC, "ECL XP to level: "+IntToString(FloatToInt(fECLXPToLevel))); + SendMessageToPC(oPC, "Level Adjustment +"+IntToString(iLvlAdj)+". Reducing XP by " + IntToString(iXPDif)); + SetXP(oPC, newXP); + } + } + iCurXP = GetXP(oPC); + SetPersistantLocalInt(oPC, sXP_AT_LAST_HEARTBEAT, iCurXP); +} + +int GetBuyoffCost(object oPC) +{ + int nECL = GetECL(oPC); + int nXP = (nECL-1) * 1000; + return nXP; +} + +void BuyoffLevel(object oPC) +{ + int nECL = GetECL(oPC); + int nXP = (nECL-1) * 1000; + SetXP(oPC, GetXP(oPC)-nXP); + int nBuyoff = GetPersistantLocalInt(oPC, "LA_Buyoff"); + SetPersistantLocalInt(oPC, "LA_Buyoff", nBuyoff+1); +} + +int GetCanBuyoffLA(object oPC) +{ + int nReturn = FALSE; + int nBuyoff = GetPersistantLocalInt(oPC, "LA_Buyoff"); + int nChar = GetHitDice(oPC); + int nLA = StringToInt(Get2DACache("ECL", "LA", GetRacialType(oPC))) + GetPersistantLocalInt(oPC, "template_LA"); + int nCheck = nLA - nBuyoff; + if (DEBUG) DoDebug("PRE-LA nBuyoff "+IntToString(nBuyoff)+" nChar "+IntToString(nChar)+" nLA "+IntToString(nLA)+" nCheck "+IntToString(nCheck)); + if (0 >= nCheck) // no LA + return FALSE; + + if (!nBuyoff) // Not purchased anything yet + { + if (nChar >= StringToInt(Get2DACache("la_buyoff", "1st", nLA))) + nReturn = TRUE; + } + if (nBuyoff == 1) // Purchased first already + { + if (nChar >= StringToInt(Get2DACache("la_buyoff", "2nd", nLA))) + nReturn = TRUE; + } + if (nBuyoff == 2) // Purchased second already + { + if (nChar >= StringToInt(Get2DACache("la_buyoff", "3rd", nLA))) + nReturn = TRUE; + } + if (nBuyoff == 3) // Purchased third already + { + if (nChar >= StringToInt(Get2DACache("la_buyoff", "4th", nLA))) + nReturn = TRUE; + } + if (nBuyoff == 4) // Purchased fourth already + { + if (nChar >= StringToInt(Get2DACache("la_buyoff", "5th", nLA))) + nReturn = TRUE; + } + if (nBuyoff == 5) // Purchased fifth already + { + if (nChar >= StringToInt(Get2DACache("la_buyoff", "6th", nLA))) + nReturn = TRUE; + } + if (DEBUG) DoDebug("nReturn "+IntToString(nReturn)+" nBuyoff "+IntToString(nBuyoff)+" nChar "+IntToString(nChar)+" nLA "+IntToString(nLA)); + + return nReturn; +} \ No newline at end of file diff --git a/src/include/inc_epicspellai.nss b/src/include/inc_epicspellai.nss new file mode 100644 index 0000000..f2a72a0 --- /dev/null +++ b/src/include/inc_epicspellai.nss @@ -0,0 +1,708 @@ + +/* +for reference +SpellRngPers 0 +SpellRngTouch 2.25 +SpellRngShrt 8 +SpellRngMed 20 +SpellRngLng 40 +*/ + +void DoEpicSpellcasterSpawn(); +int DoEpicSpells(); +int TestConditions(int nSpellID); +object GetSuitableTaget(int nSpellID); +void MakeEpicSpellsKnownAIList(); + +#include "inc_epicspells" +//#include "inc_epicspelldef" +//#include "inc_epicspellfnc" +#include "inc_utility" + +//returns True if it casts something +int DoEpicSpells() +{ + //checks for able to cast anything epic + if(!GetIsEpicSpellcaster(OBJECT_SELF)) + return FALSE; + if(GetSpellSlots(OBJECT_SELF) < 1) + return FALSE; + +// DoDebug("Checking for EpicSpells"); + int nSpellID; + int bTest; + int i; + object oTarget; + + //sanity test + if(!array_exists(OBJECT_SELF,"AI_KnownEpicSpells")) + { + if(DEBUG) DoDebug("ERROR: DoEpicSpells: AI_KnownEpicSpells array does not exist, creating"); + MakeEpicSpellsKnownAIList(); + } + //do specific conditon tests first + //non implemented at moment + //test all spells in known spell array setup on spawn + for(i=0; i 25.0) + return TRUE; + else + return FALSE; + //Order Restored is alignment sensitive. Only castable by lawful + case SPELL_EPIC_ORDER_R: + if(GetAlignmentLawChaos(OBJECT_SELF) == ALIGNMENT_LAWFUL + && GetAlignmentLawChaos(GetNearestCreature( + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY)) == ALIGNMENT_CHAOTIC) + return TRUE; + else + return FALSE; + + //Anarchy's Call is alignment sensitive. Only castable by Chaotic + case SPELL_EPIC_ANARCHY: + if(GetAlignmentLawChaos(OBJECT_SELF) == ALIGNMENT_CHAOTIC + && GetAlignmentLawChaos(GetNearestCreature( + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY)) == ALIGNMENT_LAWFUL) + return TRUE; + else + return FALSE; + //touchbuffs pass automatically because of target selection checks + case SPELL_EPIC_HERCALL: + case SPELL_EPIC_CHAMP_V: + case SPELL_EPIC_DEADEYE: + case SPELL_EPIC_DULBLAD: + case SPELL_EPIC_EP_M_AR: + case SPELL_EPIC_EP_SP_R: + case SPELL_EPIC_ET_FREE: + case SPELL_EPIC_FLEETNS: + case SPELL_EPIC_GR_SP_RE: + case SPELL_EPIC_HERCEMP: + case SPELL_EPIC_IMPENET: + case SPELL_EPIC_TRANVIT: + case SPELL_EPIC_UNIMPIN: + case SPELL_EPIC_AL_MART: + return TRUE; + break; + //single hostile spells + case SPELL_EPIC_GR_RUIN: + case SPELL_EPIC_RUINN: + case SPELL_EPIC_GODSMIT: + case SPELL_EPIC_NAILSKY: + case SPELL_EPIC_ENSLAVE: + case SPELL_EPIC_MORI: + case SPELL_EPIC_THEWITH: + case SPELL_EPIC_PSION_S: + case SPELL_EPIC_DWEO_TH: + case SPELL_EPIC_SP_WORM: + case SPELL_EPIC_SINGSUN: + return TRUE; + break; + + //aoe hostile spells + case SPELL_EPIC_ANBLAST: + case SPELL_EPIC_ANBLIZZ: + case SPELL_EPIC_A_STONE: + case SPELL_EPIC_MASSPEN: + case SPELL_EPIC_HELBALL: + case SPELL_EPIC_MAGMA_B: + case SPELL_EPIC_TOLO_KW: + return TRUE; + break; + + //fail spells that tha AI cant work with anyway + case SPELL_EPIC_CELCOUN: + case SPELL_EPIC_CON_REU: + case SPELL_EPIC_DREAMSC: + case SPELL_EPIC_EP_RPLS: + case SPELL_EPIC_FIEND_W: + case SPELL_EPIC_HELSEND: + case SPELL_EPIC_LEG_ART: + case SPELL_EPIC_PIOUS_P: + case SPELL_EPIC_PLANCEL: + case SPELL_EPIC_RISEN_R: + case SPELL_EPIC_UNSEENW: + return FALSE; + + //fail spells that dont work at the moment + case SPELL_EPIC_BATTLEB: + case SPELL_EPIC_DTHMARK: +// case SPELL_EPIC_HELSEND: +// case SPELL_EPIC_EP_RPLS: +// case SPELL_EPIC_LEG_ART: + case SPELL_EPIC_LIFE_FT: + case SPELL_EPIC_NIGHTSU: + case SPELL_EPIC_PEERPEN: +// case SPELL_EPIC_RISEN_R: + case SPELL_EPIC_SYMRUST: + return FALSE; + } + return FALSE; +} +object GetSuitableTaget(int nSpellID) +{ + object oTarget; + object oTest; + int i; + float fDist; + int nRealSpellID = StringToInt(Get2DACache("feats", "SpellID", + StringToInt(Get2DACache("EpicSpells", "SpellFeatID", nSpellID)))); + switch(nSpellID) + { + //personal spells always target self + case SPELL_EPIC_ACHHEEL: + case SPELL_EPIC_ALLHOPE: + case SPELL_EPIC_ANARCHY: + case SPELL_EPIC_ARMY_UN: + case SPELL_EPIC_BATTLEB: + case SPELL_EPIC_CELCOUN: + case SPELL_EPIC_DIREWIN: + case SPELL_EPIC_DREAMSC: + case SPELL_EPIC_EP_WARD: + case SPELL_EPIC_FIEND_W: + case SPELL_EPIC_GR_TIME: + case SPELL_EPIC_HELSEND: + case SPELL_EPIC_LEG_ART: + case SPELL_EPIC_ORDER_R: + case SPELL_EPIC_PATHS_B: + case SPELL_EPIC_PEERPEN: + case SPELL_EPIC_PESTIL: + case SPELL_EPIC_PIOUS_P: + case SPELL_EPIC_RAINFIR: + case SPELL_EPIC_RISEN_R: + case SPELL_EPIC_WHIP_SH: + return OBJECT_SELF; + break; + //summons target nearest enemy, or self if enemies over short range + case SPELL_EPIC_UNHOLYD: + case SPELL_EPIC_SUMABER: + case SPELL_EPIC_TWINF: + case SPELL_EPIC_MUMDUST: + case SPELL_EPIC_DRG_KNI: + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + if(GetDistanceToObject(oTarget) > RADIUS_SIZE_SMALL)//assuming radius size is the same as range + return OBJECT_SELF; + else + return oTarget; + break; + //touchbuffs target self, or nearest ally without the effect + //maximum 5m distance, dont want to wander too far + //will separate those best cast on others laters + case SPELL_EPIC_HERCALL: + case SPELL_EPIC_CHAMP_V: + case SPELL_EPIC_DEADEYE: + case SPELL_EPIC_DULBLAD: + case SPELL_EPIC_EP_M_AR: + case SPELL_EPIC_EP_RPLS: + case SPELL_EPIC_EP_SP_R: + case SPELL_EPIC_ET_FREE: + case SPELL_EPIC_FLEETNS: + case SPELL_EPIC_GR_SP_RE: + case SPELL_EPIC_HERCEMP: + case SPELL_EPIC_IMPENET: + case SPELL_EPIC_TRANVIT: + case SPELL_EPIC_UNIMPIN: + case SPELL_EPIC_UNSEENW: + case SPELL_EPIC_CON_RES: + fDist = 5.0; + if(!GetHasSpellEffect(nRealSpellID)) + return OBJECT_SELF; + else + { + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_FRIEND,OBJECT_SELF, i); + while(!GetHasSpellEffect(nRealSpellID, oTarget) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_FRIEND,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + } + break; + + case SPELL_EPIC_AL_MART: + fDist = 5.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_FRIEND,OBJECT_SELF, i); + while(!GetHasSpellEffect(nRealSpellID, oTarget) + && GetCurrentHitPoints(oTarget) > GetCurrentHitPoints(OBJECT_SELF) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_FRIEND,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + { + oTest = GetLocalObject(OBJECT_SELF, "oAILastMart"); + if(GetIsObjectValid(oTest) + && !GetIsDead(oTest) + && GetCurrentHitPoints(oTest) > GetMaxHitPoints(oTest)/10) + return OBJECT_INVALID; + return oTarget; + } + break; + //hostile spells + //area effect descriminants + case SPELL_EPIC_ANBLAST: + fDist = 40.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetCurrentHitPoints(oTarget) > 35 + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + break; + case SPELL_EPIC_ANBLIZZ: + fDist = 40.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetCurrentHitPoints(oTarget) > 70 + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + break; + case SPELL_EPIC_A_STONE: + fDist = 20.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetFortitudeSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + break; + + case SPELL_EPIC_MASSPEN: + fDist = 40.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetFortitudeSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + break; + //singe target + case SPELL_EPIC_ENSLAVE: + fDist = 80.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetWillSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + break; + case SPELL_EPIC_NAILSKY: + fDist = 20.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetWillSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + break; + case SPELL_EPIC_GR_RUIN: + case SPELL_EPIC_RUINN: + case SPELL_EPIC_DTHMARK: + case SPELL_EPIC_GODSMIT: + case SPELL_EPIC_SINGSUN: + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + return oTarget; + break; + + //area effect indescriminants + case SPELL_EPIC_HELBALL: + case SPELL_EPIC_MAGMA_B: + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + return oTarget; + break; + + case SPELL_EPIC_LEECH_F: + fDist = 40.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetWillSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID) + && GetDistanceToObject(oTarget) < fDist + && GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + break; + //check to not target those immune to insta-death + case SPELL_EPIC_TOLO_KW: + fDist = 40.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetIsImmune(oTarget,IMMUNITY_TYPE_DEATH) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + break; + case SPELL_EPIC_MORI: + fDist = 20.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetIsImmune(oTarget,IMMUNITY_TYPE_DEATH) + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) >= fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + break; + //check to not target those immune to ability lowering + case SPELL_EPIC_THEWITH: + fDist = 20.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetIsImmune(oTarget,IMMUNITY_TYPE_ABILITY_DECREASE) + && GetDistanceToObject(oTarget) < fDist + && i < 10 + && GetFortitudeSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID)) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + break; + //aslo willcheck + case SPELL_EPIC_PSION_S: + fDist = 20.0; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetIsImmune(oTarget,IMMUNITY_TYPE_ABILITY_DECREASE) + && GetDistanceToObject(oTarget) < fDist + && i < 10 + && GetWillSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID)) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + break; + //target spellcasters + //just gets last spellcaster + //or those with classes in spellcasting + case SPELL_EPIC_DWEO_TH: + fDist = 20.0; + + oTarget = GetLastSpellCaster(); + if(GetDistanceToObject(oTarget) < fDist + && i < 10 + && GetIsEnemy(oTarget)) + return oTarget; + else + { + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetLevelByClass(CLASS_TYPE_CLERIC) ==0 + && GetLevelByClass(CLASS_TYPE_DRUID) ==0 + && GetLevelByClass(CLASS_TYPE_WIZARD) ==0 + && GetLevelByClass(CLASS_TYPE_SORCERER) ==0 + && GetDistanceToObject(oTarget) < fDist + && i < 10) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; //no suitable targets + else + return oTarget; + } + break; + //target spellcasters + //just gets last spellcaster + //or those with classes in spellcasting + //also checks will fail will test + case SPELL_EPIC_SP_WORM: + fDist = 8.0; + oTarget = GetLastSpellCaster(); + if(GetDistanceToObject(oTarget) < fDist + && i < 10 + && GetIsEnemy(oTarget) + && GetWillSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF) + + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID)) + return oTarget; + else + { + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + while(GetLevelByClass(CLASS_TYPE_CLERIC) ==0 + && GetLevelByClass(CLASS_TYPE_DRUID) ==0 + && GetLevelByClass(CLASS_TYPE_WIZARD) ==0 + && GetLevelByClass(CLASS_TYPE_SORCERER) ==0 + && GetDistanceToObject(oTarget) < fDist + && i < 10 + && GetWillSavingThrow(oTarget)+10 > + GetEpicSpellSaveDC(OBJECT_SELF, oTarget, nSpellID)) + { + i++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_ENEMY,OBJECT_SELF, i); + } + if(GetDistanceToObject(oTarget) > fDist + || i >= 10) + return OBJECT_INVALID; + else + return oTarget; //no suitable targets + } + break; + + } + return OBJECT_INVALID; +} + +void DoEpicSpellcasterSpawn() +{ + //checks for able to cast anything epic + if(!GetIsEpicSpellcaster(OBJECT_SELF)) + return; + int nLevel = GetHitDice(OBJECT_SELF); + //give pseudoXP if not already given + if(!GetXP(OBJECT_SELF)) + { + int nXP =(nLevel*(nLevel-1))*500; + nXP = nXP + FloatToInt(IntToFloat(nLevel)*1000.0*((IntToFloat(Random(51))+25.0)/100.0)); + SetXP(OBJECT_SELF, nXP); + } + //fill slots + ReplenishSlots(OBJECT_SELF); + //TEMP + //This stuff gives them some Epic Spells for free + if(!GetCastableFeatCount(OBJECT_SELF)) + { + int nSlots = GetEpicSpellSlotLimit(OBJECT_SELF)+3; + int i; + for(i=0;i nHighestDC) + { + nHighestDC = GetDCForSpell(array_get_int(OBJECT_SELF, "AI_KnownEpicSpells",i)); + nHighestDCID = i; + } + } + //once you have checked all spells lower, swap the top one with the highest DC + nTemp = array_get_int(OBJECT_SELF, "AI_KnownEpicSpells",j); + array_set_int(OBJECT_SELF, "AI_KnownEpicSpells",j,array_get_int(OBJECT_SELF, "AI_KnownEpicSpells",nHighestDCID)); + array_set_int(OBJECT_SELF, "AI_KnownEpicSpells",nHighestDCID, nTemp); + } +// DoDebug("Finished sorting known spells"); +} + +// Test main +//void main(){} diff --git a/src/include/inc_epicspelldef.nss b/src/include/inc_epicspelldef.nss new file mode 100644 index 0000000..78b89b4 --- /dev/null +++ b/src/include/inc_epicspelldef.nss @@ -0,0 +1,1216 @@ + +//CONSTANTS FOR MESSAGES. +//reesearch +const string MES_LEARN_SPELL = "has gained the knowledge and use of this epic spell!"; +const string MES_KNOW_SPELL = "has already researched this spell"; +const string MES_NOT_ENOUGH_GOLD = "does not have the required gold"; +const string MES_NOT_ENOUGH_XP = "does not have the required experience"; +const string MES_NOT_ENOUGH_SKILL = "does not have the required skill"; +const string MES_NOT_HAVE_REQ_FEATS = "does not have the required knowledge"; +const string MES_CANNOT_RESEARCH_HERE = "does not have the Epic Spellcasting feat or does not have enough levels in the appropriate classes."; +const string MES_RESEARCH_SUCCESS = "has successfully researched an Epic spell! Congratulations! "; +const string MES_RESEARCH_FAILURE = "has not found success in their research..."; +//seeds +const string MES_CLASS_NOT_ALLOWED = "Your magical teachings do not seem to allow the learning of this epic spell seed."; +const string MES_LEARN_SEED = "You have gained the knowledge of this epic spell seed!"; +const string MES_KNOW_SEED = "You already have knowledge of this epic spell seed."; +const string MES_BOOK_DESTROYED = "The handling of this book has caused it to disintegrate!"; +//spellcraft +const string MES_SPELLCRAFT_CHECK_PASS = "Spellcraft check: Success!"; +const string MES_SPELLCRAFT_CHECK_FAIL = "Spellcraft check: Failed!"; +//errors +const string MES_CANNOT_CAST_SLOTS = "Spell failed! You do not have any epic spell slots remaining."; +const string MES_CANNOT_CAST_XP = "Spell failed! You do not have enough experience to cast this spell."; +//contingencies +const string MES_CONTINGENCIES_YES1 = "You have contingencies active, therefore you do not have your full complement of spell slots."; +const string MES_CONTINGENCIES_YES2 = "The contingencies must expire to allow you to regain the spell slots."; +/* DEFAULTS +const string MES_LEARN_SEED = "You have gained the knowledge of this epic spell seed!"; +const string MES_KNOW_SEED = "You already have knowledge of this epic spell seed."; +const string MES_LEARN_SPELL = "You have gained the knowledge and use of this epic spell!"; +const string MES_KNOW_SPELL = "You have already researched this spell."; +const string MES_CLASS_NOT_ALLOWED = "Your magical teachings do not seem to allow the learning of this epic spell seed."; +const string MES_BOOK_DESTROYED = "The handling of this book has caused it to disintegrate!"; +const string MES_CANNOT_RESEARCH_HERE = "You are not allowed to pursue magical research here."; +const string MES_RESEARCH_SUCCESS = "Congratulations! You have successfully researched an Epic spell!"; +const string MES_RESEARCH_FAILURE = "Failure! You have not found success in your research..."; +const string MES_SPELLCRAFT_CHECK_PASS = "Spellcraft check: Success!"; +const string MES_SPELLCRAFT_CHECK_FAIL = "Spellcraft check: Failed!"; +const string MES_NOT_ENOUGH_GOLD = "You do not have the required gold."; +const string MES_NOT_ENOUGH_XP = "You do not have the required experience."; +const string MES_NOT_ENOUGH_SKILL = "You do not have the required skill."; +const string MES_NOT_HAVE_REQ_FEATS = "You cannot research this, since you do not have the required knowledge."; +const string MES_CANNOT_CAST_SLOTS = "Spell failed! You do not have any epic spell slots remaining."; +const string MES_CANNOT_CAST_XP = "Spell failed! You do not have enough experience to cast this spell."; +const string MES_CONTINGENCIES_YES1 = "You have contingencies active, therefore you do not have your full complement of spell slots."; +const string MES_CONTINGENCIES_YES2 = "The contingencies must expire to allow you to regain the spell slots."; +*/ + +//Primogenitors SpellID constants +const int SPELL_EPIC_A_STONE = 0;//4007; +const int SPELL_EPIC_ACHHEEL = 1;//4000; +const int SPELL_EPIC_AL_MART = 2;//4002; +const int SPELL_EPIC_ALLHOPE = 3;//4001; +const int SPELL_EPIC_ANARCHY = 4;//4003; +const int SPELL_EPIC_ANBLAST = 5;//4004; +const int SPELL_EPIC_ANBLIZZ = 6;//4005; +const int SPELL_EPIC_ARMY_UN = 7;//4006; +const int SPELL_EPIC_BATTLEB = 999;//4008; +const int SPELL_EPIC_CELCOUN = 8;//4009; +const int SPELL_EPIC_CHAMP_V = 9;//4010; +const int SPELL_EPIC_CON_RES =10;//4011; +const int SPELL_EPIC_CON_REU =11;//4012; +const int SPELL_EPIC_DEADEYE =12;//4013; +const int SPELL_EPIC_DIREWIN =13;//4015; +const int SPELL_EPIC_DREAMSC =14;//4017; +const int SPELL_EPIC_DRG_KNI =15;//4016; +const int SPELL_EPIC_DTHMARK =1000;//4014; +const int SPELL_EPIC_DULBLAD =16;//4018; +const int SPELL_EPIC_DWEO_TH =17;//4019; +const int SPELL_EPIC_ENSLAVE =18;//4020; +const int SPELL_EPIC_EP_M_AR =19;//4021; +const int SPELL_EPIC_EP_RPLS =20;//4022; +const int SPELL_EPIC_EP_SP_R =21;//4023; +const int SPELL_EPIC_EP_WARD =22;//4024; +const int SPELL_EPIC_ET_FREE =23;//4025; +const int SPELL_EPIC_FIEND_W =24;//4026; +const int SPELL_EPIC_FLEETNS =25;//4027; +const int SPELL_EPIC_GEMCAGE =26;//4028; +const int SPELL_EPIC_GODSMIT =27;//4029; +const int SPELL_EPIC_GR_RUIN =28;//4030; +const int SPELL_EPIC_GR_SP_RE=29;//4031; +const int SPELL_EPIC_GR_TIME =30;//4032; +const int SPELL_EPIC_HELBALL =31;//4034; +const int SPELL_EPIC_HELSEND =1001;//4033; +const int SPELL_EPIC_HERCALL =32;//4035; +const int SPELL_EPIC_HERCEMP =33;//4036; +const int SPELL_EPIC_IMPENET =34;//4037; +const int SPELL_EPIC_LEECH_F =35;//4038; +const int SPELL_EPIC_LEG_ART =1002;//4039; +const int SPELL_EPIC_LIFE_FT =1003;//4040; +const int SPELL_EPIC_MAGMA_B =36;//4041; +const int SPELL_EPIC_MASSPEN =37;//4042; +const int SPELL_EPIC_MORI = 38;//4043; +const int SPELL_EPIC_MUMDUST =39;//4044; +const int SPELL_EPIC_NAILSKY =40;//4045; +const int SPELL_EPIC_NIGHTSU =1004;//4046; +const int SPELL_EPIC_ORDER_R =41;//4047; +const int SPELL_EPIC_PATHS_B =42;//4048; +const int SPELL_EPIC_PEERPEN =43;//4049; +const int SPELL_EPIC_PESTIL = 44;//4050; +const int SPELL_EPIC_PIOUS_P =45;//4051; +const int SPELL_EPIC_PLANCEL =46;//4052; +const int SPELL_EPIC_PSION_S =47;//4053; +const int SPELL_EPIC_RAINFIR =48;//4054; +const int SPELL_EPIC_RISEN_R =1005;//4055; +const int SPELL_EPIC_RUINN = 49;//4056; //NON_STANDARD +const int SPELL_EPIC_SINGSUN =50;//4057; +const int SPELL_EPIC_SP_WORM =51;//4058; +const int SPELL_EPIC_STORM_M =52;//4059; +const int SPELL_EPIC_SUMABER =53;//4060; +const int SPELL_EPIC_SUP_DIS =54;//4061; +const int SPELL_EPIC_SYMRUST =1006;//4062; +const int SPELL_EPIC_THEWITH =55;//4063; +const int SPELL_EPIC_TOLO_KW =56;//4064; +const int SPELL_EPIC_TRANVIT =57;//4065; +const int SPELL_EPIC_TWINF = 58;//4066; +const int SPELL_EPIC_UNHOLYD =59;//4067; +const int SPELL_EPIC_UNIMPIN =60;//4068; +const int SPELL_EPIC_UNSEENW =61;//4069; +const int SPELL_EPIC_WHIP_SH =62;//4070; + + +/* +this stuff is no longer neeed as constants +instead its defined in epicspells.2da and epicspellseeds.2da +/////////////////////////////////////////////////////////////////////////////// +//CONSTANTS FOR THE SEEDS +/////////////////////////////////////////////////////////////////////////////// +// const int SEEDNAME_DC is the base DC for learning an Epic Spell Seed +// const int SEEDNAME_FE is for the line number in the feat.2da file +// const int SEEDNAME_IP is the line number of the seed in iprp_feats.2da. +// const int SEEDNAME_XX is for allowing levels of access to the seed... +// Use the following numbers for SEEDNAME_XX for different options: +// 0 <---- Zero allows NO ONE to learn the seed. +// 1 <---- One allows only clerics to learn the seed. +// 2 <---- Two allows only druids to learn the seed. +// 4 <---- Four allows only sorcerers to learn the seed. +// 8 <---- Eight allows only wizards to learn the seed. +// ?? <---- Add the previous four numbers together to have combinations +// of classes able to learn the seed. (15 for ALL) +/////////////////////////////////////////////////////////////////////////////// +const int AFFLICT_DC = 14; +const int AFFLICT_FE = 5000; +const int AFFLICT_IP = 400; +const int AFFLICT_XX = 15; +const int ANIMATE_DC = 25; +const int ANIMATE_FE = 5001; +const int ANIMATE_IP = 401; +const int ANIMATE_XX = 15; +const int ANIDEAD_DC = 23; +const int ANIDEAD_FE = 5002; +const int ANIDEAD_IP = 402; +const int ANIDEAD_XX = 15; +const int ARMOR_DC = 14; +const int ARMOR_FE = 5003; +const int ARMOR_IP = 403; +const int ARMOR_XX = 15; +const int BANISH_DC = 27; +const int BANISH_FE = 5004; +const int BANISH_IP = 404; +const int BANISH_XX = 15; +const int COMPEL_DC = 19; +const int COMPEL_FE = 5005; +const int COMPEL_IP = 405; +const int COMPEL_XX = 15; +const int CONCEAL_DC = 17; +const int CONCEAL_FE = 5006; +const int CONCEAL_IP = 406; +const int CONCEAL_XX = 15; +const int CONJURE_DC = 21; +const int CONJURE_FE = 5007; +const int CONJURE_IP = 407; +const int CONJURE_XX = 15; +const int CONTACT_DC = 23; +const int CONTACT_FE = 5008; +const int CONTACT_IP = 408; +const int CONTACT_XX = 15; +const int DELUDE_DC = 14; +const int DELUDE_FE = 5009; +const int DELUDE_IP = 409; +const int DELUDE_XX = 15; +const int DESTROY_DC = 29; +const int DESTROY_FE = 5010; +const int DESTROY_IP = 410; +const int DESTROY_XX = 15; +const int DISPEL_DC = 19; +const int DISPEL_FE = 5011; +const int DISPEL_IP = 411; +const int DISPEL_XX = 15; +const int ENERGY_DC = 19; +const int ENERGY_FE = 5012; +const int ENERGY_IP = 412; +const int ENERGY_XX = 15; +const int FORESEE_DC = 17; +const int FORESEE_FE = 5013; +const int FORESEE_IP = 413; +const int FORESEE_XX = 15; +const int FORTIFY_DC = 17; +const int FORTIFY_FE = 5014; +const int FORTIFY_IP = 414; +const int FORTIFY_XX = 15; +const int HEAL_DC = 25; +const int HEAL_FE = 5015; +const int HEAL_IP = 415; +const int HEAL_XX = 15; +const int LIFE_DC = 27; +const int LIFE_FE = 5016; +const int LIFE_IP = 416; +const int LIFE_XX = 15; +const int LIGHT_DC = 14; +const int LIGHT_FE = 5017; +const int LIGHT_IP = 417; +const int LIGHT_XX = 15; +const int OPPOSIT_DC = 14; +const int OPPOSIT_FE = 5018; +const int OPPOSIT_IP = 418; +const int OPPOSIT_XX = 15; +const int REFLECT_DC = 27; +const int REFLECT_FE = 5019; +const int REFLECT_IP = 419; +const int REFLECT_XX = 15; +const int REVEAL_DC = 19; +const int REVEAL_FE = 5020; +const int REVEAL_IP = 420; +const int REVEAL_XX = 15; +const int SHADOW_DC = 21; +const int SHADOW_FE = 5021; +const int SHADOW_IP = 421; +const int SHADOW_XX = 15; +const int SLAY_DC = 25; +const int SLAY_FE = 5022; +const int SLAY_IP = 422; +const int SLAY_XX = 15; +const int SUMMON_DC = 14; +const int SUMMON_FE = 5023; +const int SUMMON_IP = 423; +const int SUMMON_XX = 15; +const int TIME_DC = 19; +const int TIME_FE = 5024; +const int TIME_IP = 424; +const int TIME_XX = 15; +const int TRANSFO_DC = 21; +const int TRANSFO_FE = 5025; +const int TRANSFO_IP = 425; +const int TRANSFO_XX = 15; +const int TRANSPO_DC = 27; +const int TRANSPO_FE = 5026; +const int TRANSPO_IP = 426; +const int TRANSPO_XX = 15; +const int WARD_DC = 14; +const int WARD_FE = 5027; +const int WARD_IP = 427; +const int WARD_XX = 15; + +//CONSTANTS FOR EACH EPIC SPELL +/////////////////////////////////////////////////////////////////////////////// +// EXAMPLE EPIC SPELL: "BLAH" +// const int BLAH_DC is for the DC of the spell for casting and researching. +// NOTE! Changing the DC will affect the costs of researching it as well! +// const int BLAH_IP is the line number of the CASTABLE feat in iprp_feats.2da. +// const int R_BLAH_IP is for the line number of the RESEARCHED feat in +// iprp_feats.2da. +// const int BLAH_FE is the line number of the CASTABLE feat in feat.2da. +// const int R_BLAH_FE is the line number of the RESEARCHED feat in feat.2da. +// const int BLAH_XP is the experience point cost for CASTING the spell. +// +// You can have up to four required feats to be able to learn the spell. +// Use the desired SEED_FE constants, +// or even a SPELL_FE constant if you want a +// prerequisite to be another spell. +// You should use the BLAH_R#'s up in order, leaving any unused ones as 0. +// const int BLAH_R1 is the first prerequisite to learn the spell. +// const int BLAH_R2 is the second prerequisite to learn the spell. +// const int BLAH_R3 is the third prerequisite to learn the spell. +// const int BLAH_R4 is the fourth prerequisite to learn the spell. +// +// const string BLAH_S is the school the spell belongs to. +// A = Abjuration +// C = Conjuration +// D = Divination +// E = Enchantment +// V = Evocation +// I = Illusion +// N = Necromancy +// T = Transmutation +/////////////////////////////////////////////////////////////////////////////// + +// EPIC SPELL: Achilles Heel +const int ACHHEEL_DC = 54; +const int ACHHEEL_IP = 429; +const int R_ACHHEEL_IP = 500; +const int ACHHEEL_FE = 5030; +const int R_ACHHEEL_FE = 5031; +const int ACHHEEL_XP = 0; +const int ACHHEEL_R1 = AFFLICT_FE; // Afflict seed +const int ACHHEEL_R2 = WARD_FE; // Ward seed +const int ACHHEEL_R3 = 0; +const int ACHHEEL_R4 = 0; +const string ACHHEEL_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: All Hope Lost +const int ALLHOPE_DC = 29; +const int ALLHOPE_IP = 430; +const int R_ALLHOPE_IP = 501; +const int ALLHOPE_FE = 5032; +const int R_ALLHOPE_FE = 5033; +const int ALLHOPE_XP = 0; +const int ALLHOPE_R1 = COMPEL_FE; // Compel seed +const int ALLHOPE_R2 = 0; +const int ALLHOPE_R3 = 0; +const int ALLHOPE_R4 = 0; +const string ALLHOPE_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Allied Martyr +const int AL_MART_DC = 47; +const int AL_MART_IP = 431; +const int R_AL_MART_IP = 502; +const int AL_MART_FE = 5034; +const int R_AL_MART_FE = 5035; +const int AL_MART_XP = 0; +const int AL_MART_R1 = HEAL_FE; // Heal seed +const int AL_MART_R2 = FORESEE_FE; // Foresee seed +const int AL_MART_R3 = 0; +const int AL_MART_R4 = 0; +const string AL_MART_S = "D"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Anarchy's Call +const int ANARCHY_DC = 43; +const int ANARCHY_IP = 432; +const int R_ANARCHY_IP = 503; +const int ANARCHY_FE = 5036; +const int R_ANARCHY_FE = 5037; +const int ANARCHY_XP = 0; +const int ANARCHY_R1 = OPPOSIT_FE; // Opposition seed +const int ANARCHY_R2 = COMPEL_FE; // Compel seed +const int ANARCHY_R3 = 0; +const int ANARCHY_R4 = 0; +const string ANARCHY_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Animus Blast +const int ANBLAST_DC = 30; +const int ANBLAST_IP = 433; +const int R_ANBLAST_IP = 504; +const int ANBLAST_FE = 5038; +const int R_ANBLAST_FE = 5039; +const int ANBLAST_XP = 0; +const int ANBLAST_R1 = ENERGY_FE; // Energy seed +const int ANBLAST_R2 = ANIDEAD_FE; // Animate Dead seed +const int ANBLAST_R3 = 0; +const int ANBLAST_R4 = 0; +const string ANBLAST_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Animus Blizzard +const int ANBLIZZ_DC = 58; +const int ANBLIZZ_IP = 434; +const int R_ANBLIZZ_IP = 505; +const int ANBLIZZ_FE = 5040; +const int R_ANBLIZZ_FE = 5041; +const int ANBLIZZ_XP = 0; +const int ANBLIZZ_R1 = ENERGY_FE; // Energy seed +const int ANBLIZZ_R2 = ANIDEAD_FE; // Animate Dead seed +const int ANBLIZZ_R3 = 0; +const int ANBLIZZ_R4 = 0; +const string ANBLIZZ_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Army Unfallen +const int ARMY_UN_DC = 66; +const int ARMY_UN_IP = 435; +const int R_ARMY_UN_IP = 506; +const int ARMY_UN_FE = 5042; +const int R_ARMY_UN_FE = 5043; +const int ARMY_UN_XP = 0; +const int ARMY_UN_R1 = LIFE_FE; // Life seed +const int ARMY_UN_R2 = HEAL_FE; // Heal seed +const int ARMY_UN_R3 = WARD_FE; // Ward seed +const int ARMY_UN_R4 = 0; +const string ARMY_UN_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Audience of Stone +const int A_STONE_DC = 41; +const int A_STONE_IP = 436; +const int R_A_STONE_IP = 507; +const int A_STONE_FE = 5044; +const int R_A_STONE_FE = 5045; +const int A_STONE_XP = 0; +const int A_STONE_R1 = TRANSFO_FE; // Transform seed +const int A_STONE_R2 = 0; +const int A_STONE_R3 = 0; +const int A_STONE_R4 = 0; +const string A_STONE_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Battle Bounding +const int BATTLEB_DC = 41; +const int BATTLEB_IP = 437; +const int R_BATTLEB_IP = 508; +const int BATTLEB_FE = 5046; +const int R_BATTLEB_FE = 5047; +const int BATTLEB_XP = 0; +const int BATTLEB_R1 = WARD_FE; // Ward seed +const int BATTLEB_R2 = TRANSPO_FE; // Transport seed +const int BATTLEB_R3 = 0; +const int BATTLEB_R4 = 0; +const string BATTLEB_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Celestial Council +const int CELCOUN_DC = 27; +const int CELCOUN_IP = 438; +const int R_CELCOUN_IP = 509; +const int CELCOUN_FE = 5048; +const int R_CELCOUN_FE = 5049; +const int CELCOUN_XP = 0; +const int CELCOUN_R1 = CONTACT_FE; // Contact seed +const int CELCOUN_R2 = 0; +const int CELCOUN_R3 = 0; +const int CELCOUN_R4 = 0; +const string CELCOUN_S = "D"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Champion's Valor +const int CHAMP_V_DC = 45; +const int CHAMP_V_IP = 439; +const int R_CHAMP_V_IP = 510; +const int CHAMP_V_FE = 5050; +const int R_CHAMP_V_FE = 5051; +const int CHAMP_V_XP = 200; +const int CHAMP_V_R1 = FORTIFY_FE; // Fortify seed +const int CHAMP_V_R2 = 0; +const int CHAMP_V_R3 = 0; +const int CHAMP_V_R4 = 0; +const string CHAMP_V_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Contingent Resurrection +const int CON_RES_DC = 52; +const int CON_RES_IP = 440; +const int R_CON_RES_IP = 511; +const int CON_RES_FE = 5052; +const int R_CON_RES_FE = 5053; +const int CON_RES_XP = 0; +const int CON_RES_R1 = LIFE_FE; // Life seed +const int CON_RES_R2 = 0; +const int CON_RES_R3 = 0; +const int CON_RES_R4 = 0; +const string CON_RES_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Contingent Reunion +const int CON_REU_DC = 44; +const int CON_REU_IP = 441; +const int R_CON_REU_IP = 512; +const int CON_REU_FE = 5054; +const int R_CON_REU_FE = 5055; +const int CON_REU_XP = 0; +const int CON_REU_R1 = TRANSPO_FE; // Transport seed +const int CON_REU_R2 = FORESEE_FE; // Foresee seed +const int CON_REU_R3 = 0; +const int CON_REU_R4 = 0; +const string CON_REU_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Deadeye Sense +const int DEADEYE_DC = 47; +const int DEADEYE_IP = 442; +const int R_DEADEYE_IP = 513; +const int DEADEYE_FE = 5056; +const int R_DEADEYE_FE = 5057; +const int DEADEYE_XP = 400; +const int DEADEYE_R1 = FORTIFY_FE; // Fortify seed +const int DEADEYE_R2 = 0; +const int DEADEYE_R3 = 0; +const int DEADEYE_R4 = 0; +const string DEADEYE_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Deathmark +const int DTHMARK_DC = 59; +const int DTHMARK_IP = 443; +const int R_DTHMARK_IP = 514; +const int DTHMARK_FE = 5058; +const int R_DTHMARK_FE = 5059; +const int DTHMARK_XP = 0; +const int DTHMARK_R1 = SLAY_FE; // Slay seed +const int DTHMARK_R2 = TIME_FE; // Time seed +const int DTHMARK_R3 = AFFLICT_FE; // Afflict seed +const int DTHMARK_R4 = 0; +const string DTHMARK_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Dire Winter +const int DIREWIN_DC = 99; +const int DIREWIN_IP = 444; +const int R_DIREWIN_IP = 515; +const int DIREWIN_FE = 5060; +const int R_DIREWIN_FE = 5061; +const int DIREWIN_XP = 2000; +const int DIREWIN_R1 = ENERGY_FE; // Energy seed +const int DIREWIN_R2 = 0; +const int DIREWIN_R3 = 0; +const int DIREWIN_R4 = 0; +const string DIREWIN_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Dragon Knight +const int DRG_KNI_DC = 48; +const int DRG_KNI_IP = 445; +const int R_DRG_KNI_IP = 516; +const int DRG_KNI_FE = 5062; +const int R_DRG_KNI_FE = 5063; +const int DRG_KNI_XP = 0; +const int DRG_KNI_R1 = SUMMON_FE; // Summon seed +const int DRG_KNI_R2 = 0; +const int DRG_KNI_R3 = 0; +const int DRG_KNI_R4 = 0; +const string DRG_KNI_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Dreamscape +const int DREAMSC_DC = 29; +const int DREAMSC_IP = 446; +const int R_DREAMSC_IP = 517; +const int DREAMSC_FE = 5064; +const int R_DREAMSC_FE = 5065; +const int DREAMSC_XP = 0; +const int DREAMSC_R1 = TRANSPO_FE; // Transport seed +const int DREAMSC_R2 = 0; +const int DREAMSC_R3 = 0; +const int DREAMSC_R4 = 0; +const string DREAMSC_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Dullblades +const int DULBLAD_DC = 54; +const int DULBLAD_IP = 447; +const int R_DULBLAD_IP = 518; +const int DULBLAD_FE = 5066; +const int R_DULBLAD_FE = 5067; +const int DULBLAD_XP = 0; +const int DULBLAD_R1 = WARD_FE; // Ward seed +const int DULBLAD_R2 = 0; +const int DULBLAD_R3 = 0; +const int DULBLAD_R4 = 0; +const string DULBLAD_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Dweomer Thief +const int DWEO_TH_DC = 65; +const int DWEO_TH_IP = 448; +const int R_DWEO_TH_IP = 519; +const int DWEO_TH_FE = 5068; +const int R_DWEO_TH_FE = 5069; +const int DWEO_TH_XP = 0; +const int DWEO_TH_R1 = REVEAL_FE; // Reveal seed +const int DWEO_TH_R2 = COMPEL_FE; // Compel seed +const int DWEO_TH_R3 = REFLECT_FE; // Reflect seed +const int DWEO_TH_R4 = 0; +const string DWEO_TH_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Enslave +const int ENSLAVE_DC = 60; +const int ENSLAVE_IP = 449; +const int R_ENSLAVE_IP = 520; +const int ENSLAVE_FE = 5070; +const int R_ENSLAVE_FE = 5071; +const int ENSLAVE_XP = 3500; +const int ENSLAVE_R1 = COMPEL_FE; // Compel seed +const int ENSLAVE_R2 = 0; +const int ENSLAVE_R3 = 0; +const int ENSLAVE_R4 = 0; +const string ENSLAVE_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Epic Mage Armor +const int EP_M_AR_DC = 46; +const int EP_M_AR_IP = 450; +const int R_EP_M_AR_IP = 521; +const int EP_M_AR_FE = 5072; +const int R_EP_M_AR_FE = 5073; +const int EP_M_AR_XP = 0; +const int EP_M_AR_R1 = ARMOR_FE; // Armor seed +const int EP_M_AR_R2 = 0; +const int EP_M_AR_R3 = 0; +const int EP_M_AR_R4 = 0; +const string EP_M_AR_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Epic Repulsion +const int EP_RPLS_DC = 34; +const int EP_RPLS_IP = 451; +const int R_EP_RPLS_IP = 522; +const int EP_RPLS_FE = 5074; +const int R_EP_RPLS_FE = 5075; +const int EP_RPLS_XP = 0; +const int EP_RPLS_R1 = WARD_FE; // Ward seed +const int EP_RPLS_R2 = 0; +const int EP_RPLS_R3 = 0; +const int EP_RPLS_R4 = 0; +const string EP_RPLS_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Epic Spell Reflection +const int EP_SP_R_DC = 52; +const int EP_SP_R_IP = 452; +const int R_EP_SP_R_IP = 523; +const int EP_SP_R_FE = 5076; +const int R_EP_SP_R_FE = 5077; +const int EP_SP_R_XP = 0; +const int EP_SP_R_R1 = REFLECT_FE; // Reflect seed +const int EP_SP_R_R2 = 0; +const int EP_SP_R_R3 = 0; +const int EP_SP_R_R4 = 0; +const string EP_SP_R_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Epic Warding +const int EP_WARD_DC = 68; +const int EP_WARD_IP = 453; +const int R_EP_WARD_IP = 524; +const int EP_WARD_FE = 5078; +const int R_EP_WARD_FE = 5079; +const int EP_WARD_XP = 0; +const int EP_WARD_R1 = WARD_FE; // Ward seed +const int EP_WARD_R2 = 0; +const int EP_WARD_R3 = 0; +const int EP_WARD_R4 = 0; +const string EP_WARD_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Eternal Freedom +const int ET_FREE_DC = 101; +const int ET_FREE_IP = 454; +const int R_ET_FREE_IP = 525; +const int ET_FREE_FE = 5080; +const int R_ET_FREE_FE = 5081; +const int ET_FREE_XP = 10000; +const int ET_FREE_R1 = WARD_FE; // Ward seed +const int ET_FREE_R2 = 0; +const int ET_FREE_R3 = 0; +const int ET_FREE_R4 = 0; +const string ET_FREE_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Fiendish Words +const int FIEND_W_DC = 27; +const int FIEND_W_IP = 455; +const int R_FIEND_W_IP = 526; +const int FIEND_W_FE = 5082; +const int R_FIEND_W_FE = 5083; +const int FIEND_W_XP = 0; +const int FIEND_W_R1 = CONTACT_FE; // Contact seed +const int FIEND_W_R2 = 0; +const int FIEND_W_R3 = 0; +const int FIEND_W_R4 = 0; +const string FIEND_W_S = "D"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Fleetness of Foot +const int FLEETNS_DC = 32; +const int FLEETNS_IP = 456; +const int R_FLEETNS_IP = 527; +const int FLEETNS_FE = 5084; +const int R_FLEETNS_FE = 5085; +const int FLEETNS_XP = 0; +const int FLEETNS_R1 = FORTIFY_FE; // Fortify seed +const int FLEETNS_R2 = 0; +const int FLEETNS_R3 = 0; +const int FLEETNS_R4 = 0; +const string FLEETNS_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Gem Cage +const int GEMCAGE_DC = 48; +const int GEMCAGE_IP = 457; +const int R_GEMCAGE_IP = 528; +const int GEMCAGE_FE = 5086; +const int R_GEMCAGE_FE = 5087; +const int GEMCAGE_XP = 0; +const int GEMCAGE_R1 = TRANSFO_FE; // Transform seed +const int GEMCAGE_R2 = TRANSPO_FE; // Transport seed +const int GEMCAGE_R3 = 0; +const int GEMCAGE_R4 = 0; +const string GEMCAGE_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Godsmite +const int GODSMIT_DC = 63; +const int GODSMIT_IP = 458; +const int R_GODSMIT_IP = 529; +const int GODSMIT_FE = 5088; +const int R_GODSMIT_FE = 5089; +const int GODSMIT_XP = 0; +const int GODSMIT_R1 = DESTROY_FE; // Destroy seed +const int GODSMIT_R2 = OPPOSIT_FE; // Opposition seed +const int GODSMIT_R3 = 0; +const int GODSMIT_R4 = 0; +const string GODSMIT_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Greater Ruin +const int GR_RUIN_DC = 59; +const int GR_RUIN_IP = 459; +const int R_GR_RUIN_IP = 530; +const int GR_RUIN_FE = 5090; +const int R_GR_RUIN_FE = 5091; +const int GR_RUIN_XP = 0; +const int GR_RUIN_R1 = DESTROY_FE; // Destroy seed +const int GR_RUIN_R2 = 0; +const int GR_RUIN_R3 = 0; +const int GR_RUIN_R4 = 0; +const string GR_RUIN_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Greater Spell Resistance +const int GR_SP_RE_DC = 67; +const int GR_SP_RE_IP = 460; +const int R_GR_SP_RE_IP = 531; +const int GR_SP_RE_FE = 5092; +const int R_GR_SP_RE_FE = 5093; +const int GR_SP_RE_XP = 0; +const int GR_SP_RE_R1 = FORTIFY_FE; // Fortify seed +const int GR_SP_RE_R2 = 0; +const int GR_SP_RE_R3 = 0; +const int GR_SP_RE_R4 = 0; +const string GR_SP_RE_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Greater Timestop +const int GR_TIME_DC = 27; +const int GR_TIME_IP = 461; +const int R_GR_TIME_IP = 532; +const int GR_TIME_FE = 5094; +const int R_GR_TIME_FE = 5095; +const int GR_TIME_XP = 0; +const int GR_TIME_R1 = TIME_FE; // Time seed +const int GR_TIME_R2 = 0; +const int GR_TIME_R3 = 0; +const int GR_TIME_R4 = 0; +const string GR_TIME_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Hell Send +const int HELSEND_DC = 34; +const int HELSEND_IP = 462; +const int R_HELSEND_IP = 533; +const int HELSEND_FE = 5096; +const int R_HELSEND_FE = 5097; +const int HELSEND_XP = 0; +const int HELSEND_R1 = TRANSPO_FE; // Transport seed +const int HELSEND_R2 = 0; +const int HELSEND_R3 = 0; +const int HELSEND_R4 = 0; +const string HELSEND_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Hellball +const int HELBALL_DC = 70; +const int HELBALL_IP = 463; +const int R_HELBALL_IP = 534; +const int HELBALL_FE = 5098; +const int R_HELBALL_FE = 5099; +const int HELBALL_XP = 400; +const int HELBALL_R1 = ENERGY_FE; // Energy seed +const int HELBALL_R2 = 0; +const int HELBALL_R3 = 0; +const int HELBALL_R4 = 0; +const string HELBALL_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Herculean Alliance +const int HERCALL_DC = 61; +const int HERCALL_IP = 464; +const int R_HERCALL_IP = 535; +const int HERCALL_FE = 5100; +const int R_HERCALL_FE = 5101; +const int HERCALL_XP = 0; +const int HERCALL_R1 = FORTIFY_FE; // Fortify seed +const int HERCALL_R2 = 5103; // the Herculean Empowerment spell researched +const int HERCALL_R3 = 0; +const int HERCALL_R4 = 0; +const string HERCALL_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Herculean Empowerment +const int HERCEMP_DC = 51; +const int HERCEMP_IP = 465; +const int R_HERCEMP_IP = 536; +const int HERCEMP_FE = 5102; +const int R_HERCEMP_FE = 5103; +const int HERCEMP_XP = 0; +const int HERCEMP_R1 = FORTIFY_FE; // Fortify seed +const int HERCEMP_R2 = 0; +const int HERCEMP_R3 = 0; +const int HERCEMP_R4 = 0; +const string HERCEMP_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Impenetrability +const int IMPENET_DC = 54; +const int IMPENET_IP = 466; +const int R_IMPENET_IP = 537; +const int IMPENET_FE = 5104; +const int R_IMPENET_FE = 5105; +const int IMPENET_XP = 0; +const int IMPENET_R1 = WARD_FE; // Ward seed +const int IMPENET_R2 = 0; +const int IMPENET_R3 = 0; +const int IMPENET_R4 = 0; +const string IMPENET_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Leech Field +const int LEECH_F_DC = 64; +const int LEECH_F_IP = 467; +const int R_LEECH_F_IP = 538; +const int LEECH_F_FE = 5106; +const int R_LEECH_F_FE = 5107; +const int LEECH_F_XP = 0; +const int LEECH_F_R1 = HEAL_FE; // Heal seed +const int LEECH_F_R2 = 0; +const int LEECH_F_R3 = 0; +const int LEECH_F_R4 = 0; +const string LEECH_F_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Legendary Artisan +const int LEG_ART_DC = 78; +const int LEG_ART_IP = 468; +const int R_LEG_ART_IP = 539; +const int LEG_ART_FE = 5108; +const int R_LEG_ART_FE = 5109; +const int LEG_ART_XP = 0; +const int LEG_ART_R1 = TRANSFO_FE; // Transform seed +const int LEG_ART_R2 = FORTIFY_FE; // Fortify seed +const int LEG_ART_R3 = ENERGY_FE; // Energy seed +const int LEG_ART_R4 = CONJURE_FE; // Conjure seed +const string LEG_ART_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Life Force Transfer +const int LIFE_FT_DC = 0; +const int LIFE_FT_IP = 469; +const int R_LIFE_FT_IP = 540; +const int LIFE_FT_FE = 5110; +const int R_LIFE_FT_FE = 5111; +const int LIFE_FT_XP = 0; +const int LIFE_FT_R1 = 0; +const int LIFE_FT_R2 = 0; +const int LIFE_FT_R3 = 0; +const int LIFE_FT_R4 = 0; +const string LIFE_FT_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Magma Burst +const int MAGMA_B_DC = 80; +const int MAGMA_B_IP = 470; +const int R_MAGMA_B_IP = 541; +const int MAGMA_B_FE = 5112; +const int R_MAGMA_B_FE = 5113; +const int MAGMA_B_XP = 0; +const int MAGMA_B_R1 = ENERGY_FE; // Energy seed +const int MAGMA_B_R2 = TRANSFO_FE; // Transform seed +const int MAGMA_B_R3 = 0; +const int MAGMA_B_R4 = 0; +const string MAGMA_B_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Mass Penguin +const int MASSPEN_DC = 35; +const int MASSPEN_IP = 471; +const int R_MASSPEN_IP = 542; +const int MASSPEN_FE = 5114; +const int R_MASSPEN_FE = 5115; +const int MASSPEN_XP = 0; +const int MASSPEN_R1 = TRANSFO_FE; // Transform seed +const int MASSPEN_R2 = 0; +const int MASSPEN_R3 = 0; +const int MASSPEN_R4 = 0; +const string MASSPEN_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Momento Mori +const int MORI_DC = 78; +const int MORI_IP = 472; +const int R_MORI_IP = 543; +const int MORI_FE = 5116; +const int R_MORI_FE = 5117; +const int MORI_XP = 0; +const int MORI_R1 = SLAY_FE; // Slay seed. +const int MORI_R2 = 0; +const int MORI_R3 = 0; +const int MORI_R4 = 0; +const string MORI_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Mummy Dust +const int MUMDUST_DC = 35; +const int MUMDUST_IP = 473; +const int R_MUMDUST_IP = 544; +const int MUMDUST_FE = 5118; +const int R_MUMDUST_FE = 5119; +const int MUMDUST_XP = 0; +const int MUMDUST_R1 = ANIDEAD_FE; // Animate Dead seed +const int MUMDUST_R2 = 0; +const int MUMDUST_R3 = 0; +const int MUMDUST_R4 = 0; +const string MUMDUST_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Nailed to the Sky +const int NAILSKY_DC = 42; +const int NAILSKY_IP = 474; +const int R_NAILSKY_IP = 545; +const int NAILSKY_FE = 5120; +const int R_NAILSKY_FE = 5121; +const int NAILSKY_XP = 1000; +const int NAILSKY_R1 = FORESEE_FE; // Foresee seed +const int NAILSKY_R2 = TRANSPO_FE; // Transport seed +const int NAILSKY_R3 = 0; +const int NAILSKY_R4 = 0; +const string NAILSKY_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Night's Undoing +const int NIGHTSU_DC = 24; +const int NIGHTSU_IP = 475; +const int R_NIGHTSU_IP = 546; +const int NIGHTSU_FE = 5122; +const int R_NIGHTSU_FE = 5123; +const int NIGHTSU_XP = 0; +const int NIGHTSU_R1 = LIGHT_FE; // Light seed +const int NIGHTSU_R2 = 0; +const int NIGHTSU_R3 = 0; +const int NIGHTSU_R4 = 0; +const string NIGHTSU_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Order Restored +const int ORDER_R_DC = 43; +const int ORDER_R_IP = 476; +const int R_ORDER_R_IP = 547; +const int ORDER_R_FE = 5124; +const int R_ORDER_R_FE = 5125; +const int ORDER_R_XP = 0; +const int ORDER_R_R1 = OPPOSIT_FE; // Opposition seed +const int ORDER_R_R2 = COMPEL_FE; // Compel seed +const int ORDER_R_R3 = 0; +const int ORDER_R_R4 = 0; +const string ORDER_R_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Paths Become Known +const int PATHS_B_DC = 36; +const int PATHS_B_IP = 477; +const int R_PATHS_B_IP = 548; +const int PATHS_B_FE = 5126; +const int R_PATHS_B_FE = 5127; +const int PATHS_B_XP = 0; +const int PATHS_B_R1 = FORESEE_FE; // Foresee seed +const int PATHS_B_R2 = REVEAL_FE; // Reveal seed +const int PATHS_B_R3 = 0; +const int PATHS_B_R4 = 0; +const string PATHS_B_S = "D"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Peerless Penitence +const int PEERPEN_DC = 68; +const int PEERPEN_IP = 478; +const int R_PEERPEN_IP = 549; +const int PEERPEN_FE = 5128; +const int R_PEERPEN_FE = 5129; +const int PEERPEN_XP = 1000; +const int PEERPEN_R1 = HEAL_FE; // Heal seed +const int PEERPEN_R2 = OPPOSIT_FE; // Opposition seed +const int PEERPEN_R3 = DESTROY_FE; // Destroy seed +const int PEERPEN_R4 = 0; +const string PEERPEN_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Pestilence +const int PESTIL_DC = 44; +const int PESTIL_IP = 479; +const int R_PESTIL_IP = 550; +const int PESTIL_FE = 5130; +const int R_PESTIL_FE = 5131; +const int PESTIL_XP = 600; +const int PESTIL_R1 = AFFLICT_FE; // Afflict seed +const int PESTIL_R2 = 0; +const int PESTIL_R3 = 0; +const int PESTIL_R4 = 0; +const string PESTIL_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Pious Parley +const int PIOUS_P_DC = 36; +const int PIOUS_P_IP = 480; +const int R_PIOUS_P_IP = 551; +const int PIOUS_P_FE = 5132; +const int R_PIOUS_P_FE = 5133; +const int PIOUS_P_XP = 0; +const int PIOUS_P_R1 = CONTACT_FE; // Contact seed +const int PIOUS_P_R2 = 0; +const int PIOUS_P_R3 = 0; +const int PIOUS_P_R4 = 0; +const string PIOUS_P_S = "D"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Planar Cell +const int PLANCEL_DC = 51; +const int PLANCEL_IP = 481; +const int R_PLANCEL_IP = 552; +const int PLANCEL_FE = 5134; +const int R_PLANCEL_FE = 5135; +const int PLANCEL_XP = 0; +const int PLANCEL_R1 = TRANSPO_FE; // Transport seed +const int PLANCEL_R2 = 0; +const int PLANCEL_R3 = 0; +const int PLANCEL_R4 = 0; +const string PLANCEL_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Psionic Salvo +const int PSION_S_DC = 74; +const int PSION_S_IP = 482; +const int R_PSION_S_IP = 553; +const int PSION_S_FE = 5136; +const int R_PSION_S_FE = 5137; +const int PSION_S_XP = 0; +const int PSION_S_R1 = AFFLICT_FE; // Afflict seed +const int PSION_S_R2 = 0; +const int PSION_S_R3 = 0; +const int PSION_S_R4 = 0; +const string PSION_S_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Rain of Fire +const int RAINFIR_DC = 75; +const int RAINFIR_IP = 483; +const int R_RAINFIR_IP = 554; +const int RAINFIR_FE = 5138; +const int R_RAINFIR_FE = 5139; +const int RAINFIR_XP = 500; +const int RAINFIR_R1 = ENERGY_FE; // Energy seed +const int RAINFIR_R2 = 0; +const int RAINFIR_R3 = 0; +const int RAINFIR_R4 = 0; +const string RAINFIR_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Risen Reunited +const int RISEN_R_DC = 67; +const int RISEN_R_IP = 484; +const int R_RISEN_R_IP = 555; +const int RISEN_R_FE = 5140; +const int R_RISEN_R_FE = 5141; +const int RISEN_R_XP = 0; +const int RISEN_R_R1 = TRANSPO_FE; // Transport seed +const int RISEN_R_R2 = CONTACT_FE; // Contact seed +const int RISEN_R_R3 = LIFE_FE; // Life seed +const int RISEN_R_R4 = 0; +const string RISEN_R_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Ruin +const int RUIN_DC = 29; +const int RUIN_IP = 485; +const int R_RUIN_IP = 556; +const int RUIN_FE = 5142; +const int R_RUIN_FE = 5143; +const int RUIN_XP = 0; +const int RUIN_R1 = DESTROY_FE; // Destroy seed +const int RUIN_R2 = 0; +const int RUIN_R3 = 0; +const int RUIN_R4 = 0; +const string RUIN_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Singular Sunder +const int SINGSUN_DC = 58; +const int SINGSUN_IP = 486; +const int R_SINGSUN_IP = 557; +const int SINGSUN_FE = 5144; +const int R_SINGSUN_FE = 5145; +const int SINGSUN_XP = 0; +const int SINGSUN_R1 = DESTROY_FE; // Destroy seed +const int SINGSUN_R2 = DISPEL_FE; // Dispel seed +const int SINGSUN_R3 = 0; +const int SINGSUN_R4 = 0; +const string SINGSUN_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Spell Worm +const int SP_WORM_DC = 25; +const int SP_WORM_IP = 487; +const int R_SP_WORM_IP = 558; +const int SP_WORM_FE = 5146; +const int R_SP_WORM_FE = 5147; +const int SP_WORM_XP = 0; +const int SP_WORM_R1 = COMPEL_FE; // Compel seed +const int SP_WORM_R2 = 0; +const int SP_WORM_R3 = 0; +const int SP_WORM_R4 = 0; +const string SP_WORM_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Storm Mantle +const int STORM_M_DC = 74; +const int STORM_M_IP = 488; +const int R_STORM_M_IP = 559; +const int STORM_M_FE = 5148; +const int R_STORM_M_FE = 5149; +const int STORM_M_XP = 0; +const int STORM_M_R1 = WARD_FE; // Ward seed +const int STORM_M_R2 = 0; +const int STORM_M_R3 = 0; +const int STORM_M_R4 = 0; +const string STORM_M_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Summon Aberration +const int SUMABER_DC = 42; +const int SUMABER_IP = 489; +const int R_SUMABER_IP = 560; +const int SUMABER_FE = 5150; +const int R_SUMABER_FE = 5151; +const int SUMABER_XP = 0; +const int SUMABER_R1 = SUMMON_FE; // Summon seed +const int SUMABER_R2 = 0; +const int SUMABER_R3 = 0; +const int SUMABER_R4 = 0; +const string SUMABER_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Superb Dispelling +const int SUP_DIS_DC = 59; +const int SUP_DIS_IP = 490; +const int R_SUP_DIS_IP = 561; +const int SUP_DIS_FE = 5152; +const int R_SUP_DIS_FE = 5153; +const int SUP_DIS_XP = 0; +const int SUP_DIS_R1 = DISPEL_FE; // Dispel seed +const int SUP_DIS_R2 = 0; +const int SUP_DIS_R3 = 0; +const int SUP_DIS_R4 = 0; +const string SUP_DIS_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Symrustar's Spellbinding +const int SYMRUST_DC = 0; +const int SYMRUST_IP = 491; +const int R_SYMRUST_IP = 562; +const int SYMRUST_FE = 5154; +const int R_SYMRUST_FE = 5155; +const int SYMRUST_XP = 0; +const int SYMRUST_R1 = 0; +const int SYMRUST_R2 = 0; +const int SYMRUST_R3 = 0; +const int SYMRUST_R4 = 0; +const string SYMRUST_S = "E"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: The Withering +const int THEWITH_DC = 69; +const int THEWITH_IP = 492; +const int R_THEWITH_IP = 563; +const int THEWITH_FE = 5156; +const int R_THEWITH_FE = 5157; +const int THEWITH_XP = 300; +const int THEWITH_R1 = AFFLICT_FE; // Afflict seed +const int THEWITH_R2 = 0; +const int THEWITH_R3 = 0; +const int THEWITH_R4 = 0; +const string THEWITH_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Tolodine's Killing Wind +const int TOLO_KW_DC = 91; +const int TOLO_KW_IP = 493; +const int R_TOLO_KW_IP = 564; +const int TOLO_KW_FE = 5158; +const int R_TOLO_KW_FE = 5159; +const int TOLO_KW_XP = 400; +const int TOLO_KW_R1 = AFFLICT_FE; // Afflict seed +const int TOLO_KW_R2 = SLAY_FE; // Slay seed +const int TOLO_KW_R3 = 0; +const int TOLO_KW_R4 = 0; +const string TOLO_KW_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Transcendent Vitality +const int TRANVIT_DC = 101; +const int TRANVIT_IP = 494; +const int R_TRANVIT_IP = 565; +const int TRANVIT_FE = 5160; +const int R_TRANVIT_FE = 5161; +const int TRANVIT_XP = 10000; +const int TRANVIT_R1 = FORTIFY_FE; // Fortify seed +const int TRANVIT_R2 = HEAL_FE; // Heal seed +const int TRANVIT_R3 = 0; +const int TRANVIT_R4 = 0; +const string TRANVIT_S = "T"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Twinfiend +const int TWINF_DC = 64; +const int TWINF_IP = 495; +const int R_TWINF_IP = 566; +const int TWINF_FE = 5162; +const int R_TWINF_FE = 5163; +const int TWINF_XP = 0; +const int TWINF_R1 = SUMMON_FE; // Summon seed +const int TWINF_R2 = 0; +const int TWINF_R3 = 0; +const int TWINF_R4 = 0; +const string TWINF_S = "C"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Unholy Disciple +const int UNHOLYD_DC = 47; +const int UNHOLYD_IP = 496; +const int R_UNHOLYD_IP = 567; +const int UNHOLYD_FE = 5164; +const int R_UNHOLYD_FE = 5165; +const int UNHOLYD_XP = 300; +const int UNHOLYD_R1 = SUMMON_FE; // Summon seed +const int UNHOLYD_R2 = 0; +const int UNHOLYD_R3 = 0; +const int UNHOLYD_R4 = 0; +const string UNHOLYD_S = "N"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Unimpinged +const int UNIMPIN_DC = 54; +const int UNIMPIN_IP = 497; +const int R_UNIMPIN_IP = 568; +const int UNIMPIN_FE = 5166; +const int R_UNIMPIN_FE = 5167; +const int UNIMPIN_XP = 0; +const int UNIMPIN_R1 = WARD_FE; // Ward seed +const int UNIMPIN_R2 = 0; +const int UNIMPIN_R3 = 0; +const int UNIMPIN_R4 = 0; +const string UNIMPIN_S = "A"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Unseen Wanderer +const int UNSEENW_DC = 101; +const int UNSEENW_IP = 498; +const int R_UNSEENW_IP = 569; +const int UNSEENW_FE = 5168; +const int R_UNSEENW_FE = 5169; +const int UNSEENW_XP = 10000; +const int UNSEENW_R1 = CONCEAL_FE; // Conceal seed +const int UNSEENW_R2 = 0; +const int UNSEENW_R3 = 0; +const int UNSEENW_R4 = 0; +const string UNSEENW_S = "I"; +/////////////////////////////////////////////////////////////////////////////// +// EPIC SPELL: Whip of Shar +const int WHIP_SH_DC = 73; +const int WHIP_SH_IP = 499; +const int R_WHIP_SH_IP = 570; +const int WHIP_SH_FE = 5170; +const int R_WHIP_SH_FE = 5171; +const int WHIP_SH_XP = 0; +const int WHIP_SH_R1 = SHADOW_FE; // Shadow seed +const int WHIP_SH_R2 = CONJURE_FE; // Conjure seed +const int WHIP_SH_R3 = TRANSFO_FE; // Transform seed +const int WHIP_SH_R4 = 0; +const string WHIP_SH_S = "V"; +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +*/ + + + diff --git a/src/include/inc_epicspellfnc.nss b/src/include/inc_epicspellfnc.nss new file mode 100644 index 0000000..59f4dd6 --- /dev/null +++ b/src/include/inc_epicspellfnc.nss @@ -0,0 +1,273 @@ + +int GetFeatForSeed(int nSeedID); +int GetIPForSeed(int nSeedID); +int GetDCForSeed(int nSeedID); +int GetClassForSeed(int nSeedID); +int GetCanLearnSeed(object oPC, int nSeedID); +int GetSeedFromAbrev(string sAbrev); +string GetNameForSeed(int nSeedID); + +int GetDCForSpell(int nSpellID); +int GetFeatForSpell(int nSpellID); +int GetResearchFeatForSpell(int nSpellID); +int GetIPForSpell(int nSpellID); +int GetResearchIPForSpell(int nSpellID); +int GetCastXPForSpell(int nSpellID); +string GetSchoolForSpell(int nSpellID); +int GetR1ForSpell(int nSpellID); +int GetR2ForSpell(int nSpellID); +int GetR3ForSpell(int nSpellID); +int GetR4ForSpell(int nSpellID); +string GetNameForSpell(int nSpellID); +int GetSpellFromAbrev(string sAbrev); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_utility" +//#include "inc_epicspelldef" + +// SEED FUNCTIONS + +int GetFeatForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "FeatID", nSeedID)); +} + +int GetIPForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "FeatIPID", nSeedID)); +} + +int GetDCForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "DC", nSeedID)); +} + +int GetClassForSeed(int nSeedID) +{ + return StringToInt(Get2DACache("epicspellseeds", "Class", nSeedID)); +} + +int GetSeedFromAbrev(string sAbrev) +{ + sAbrev = GetStringLowerCase(sAbrev); + if(GetStringLeft(sAbrev, 8) == "epic_sd_") + sAbrev = GetStringRight(sAbrev, GetStringLength(sAbrev)-8); + int i = 0; + string sLabel = GetStringLowerCase(Get2DACache("epicspellseeds", "LABEL", i)); + while(sLabel != "") + { + if(sAbrev == sLabel) + return i; + i++; + sLabel = GetStringLowerCase(Get2DACache("epicspellseeds", "LABEL", i)); + } + return -1; +} + +string GetNameForSeed(int nSeedID) +{ + int nFeat = GetFeatForSeed(nSeedID); + string sName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))); + return sName; +} + +/* +Bit-flags set in epicspellseeds.2da in Class column +used to restrict access to epic spell seeds for some classes +ie: 13 means that only clerics, sorcerers and wizards can learn that seed (1 + 4 + 8), +all classes can use == 32767 +*/ +int _Class2BitFlag(int nClass) +{ + switch(nClass) + { + case CLASS_TYPE_CLERIC: return 1; + case CLASS_TYPE_DRUID: return 2; + case CLASS_TYPE_SORCERER: return 4; + case CLASS_TYPE_WIZARD: return 8; + case CLASS_TYPE_HEALER: return 16; + case CLASS_TYPE_BEGUILER: return 32; + case CLASS_TYPE_SUBLIME_CHORD: return 64; + case CLASS_TYPE_DREAD_NECROMANCER: return 128; + case CLASS_TYPE_MYSTIC: return 256; + case CLASS_TYPE_ARCHIVIST: return 512; + case CLASS_TYPE_SHAMAN: return 4096; + case CLASS_TYPE_FAVOURED_SOUL: return 8192; + case CLASS_TYPE_WARMAGE: return 16384; + case CLASS_TYPE_UR_PRIEST: return 1; + case CLASS_TYPE_BLIGHTER: return 2; + } + return -1; +} + +int _CheckEpicSpellcastingForClass(object oPC, int nClass) +{ + if(GetHitDice(oPC) < 21) + return FALSE; + + switch(nClass) + { + case CLASS_TYPE_CLERIC: return GetIsEpicCleric(oPC); + case CLASS_TYPE_DRUID: return GetIsEpicDruid(oPC); + case CLASS_TYPE_SORCERER: return GetIsEpicSorcerer(oPC); + case CLASS_TYPE_WIZARD: return GetIsEpicWizard(oPC); + case CLASS_TYPE_HEALER: return GetIsEpicHealer(oPC); + case CLASS_TYPE_BEGUILER: return GetIsEpicBeguiler(oPC); + case CLASS_TYPE_SUBLIME_CHORD: return GetIsEpicSublimeChord(oPC); + case CLASS_TYPE_DREAD_NECROMANCER: return GetIsEpicDreadNecromancer(oPC); + case CLASS_TYPE_ARCHIVIST: return GetIsEpicArchivist(oPC); + case CLASS_TYPE_SHAMAN: return GetIsEpicShaman(oPC); + case CLASS_TYPE_FAVOURED_SOUL: return GetIsEpicFavSoul(oPC); + case CLASS_TYPE_WARMAGE: return GetIsEpicWarmage(oPC); + case CLASS_TYPE_BLIGHTER: return GetIsEpicBlighter(oPC); + case CLASS_TYPE_UR_PRIEST: return GetIsEpicUrPriest(oPC); + } + return FALSE; +} + +int GetCanLearnSeed(object oPC, int nSeedID) +{ + int nRestr = GetClassForSeed(nSeedID); + int i, nClass; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oPC); + if(_CheckEpicSpellcastingForClass(oPC, nClass)//this class has epic spellcasting + && (nRestr & _Class2BitFlag(nClass)))//and was added to class column in epicspellseeds.2da + { + return TRUE; + } + } + return FALSE; +} + +// SPELL FUNCTIONS + +int GetDCForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "DC", nSpellID)); +} + +int GetFeatForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "SpellFeatID", nSpellID)); +} + +int GetResearchFeatForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "ResFeatID", nSpellID)); +} + +int GetIPForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "SpellFeatIPID", nSpellID)); +} + +int GetResearchIPForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "ResFeatIPID", nSpellID)); +} + +int GetCastXPForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "CastingXP", nSpellID)); +} + +string GetSchoolForSpell(int nSpellID) +{ + return Get2DACache("epicspells", "School", nSpellID); +} + +int GetR1ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq1", nSpellID)); +} + +int GetR2ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq2", nSpellID)); +} + +int GetR3ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq3", nSpellID)); +} + +int GetR4ForSpell(int nSpellID) +{ + return StringToInt(Get2DACache("epicspells", "Prereq4", nSpellID)); +} + +int GetS1ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed1", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS2ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed2", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS3ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed3", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS4ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed4", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetS5ForSpell(int nSpellID) +{ + string sSeed = Get2DACache("epicspells", "PrereqSeed5", nSpellID); + if(sSeed == "") + return -1; + return StringToInt(sSeed); +} + +int GetSpellFromAbrev(string sAbrev) +{ + sAbrev = GetStringLowerCase(sAbrev); + if(GetStringLeft(sAbrev, 8) == "epic_sp_") + sAbrev = GetStringRight(sAbrev, GetStringLength(sAbrev)-8); + if(DEBUG) DoDebug("sAbrew to check vs: " + sAbrev); + int i = 0; + string sLabel = GetStringLowerCase(Get2DACache("epicspells", "LABEL", i)); + while(sLabel != "") + { + if(DEBUG) DoDebug("sLabel to check vs: " + sLabel); + if(sAbrev == sLabel) + { + if(DEBUG) DoDebug("SpellID: " + IntToString(i)); + return i; + } + i++; + sLabel = GetStringLowerCase(Get2DACache("epicspells", "LABEL", i)); + } + return -1; +} + +string GetNameForSpell(int nSpellID) +{ + int nFeat = GetFeatForSpell(nSpellID); + string sName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))); + return sName; +} + +//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_epicspells.nss b/src/include/inc_epicspells.nss new file mode 100644 index 0000000..ec3eac9 --- /dev/null +++ b/src/include/inc_epicspells.nss @@ -0,0 +1,857 @@ +//::////////////////////////////////////////////// +//:: FileName: "inc_epicspells" +/* Purpose: This is the #include file that contains all constants and + functions needed for the Epic Spellcasting System. +*/ +//::////////////////////////////////////////////// +//:: Created By: Boneshank (Don Armstrong) +//:: Last Updated On: March 18, 2004 +//::////////////////////////////////////////////// + +/* +CONSTANTS FOR OPTIONAL FEATURES +*/ +/* moved to prc_inc_switch as runtime switches rather than compiletime + +// Use the "XP Costs" option, making casters expend some experience when they +// cast certain spells? +const int XP_COSTS = TRUE; + +// Use the "Take 10" variant rule? +// If TRUE, all Spellcraft checks will be automatically equal to the caster's +// Spellcraft skill level, plus 10. The outcome is never a surprise. +// If FALSE, every Spellcraft check is a roll of the dice, being equal to the +// caster's Spellcraft skill level, plus 1d20. Risky, but more fun! +const int TAKE_TEN_RULE = FALSE; + +// Use the "Primary Ability Modifier Bonus to Skills" variant rule? +// If TRUE, caster's use their primary ability (WISDOM for clerics and druids, +// CHARISMA for sorcerers) instead of intelligence as a modifier on their +// Spellcraft checks for casting and researching epic spells, as well as +// their total Lore skill level for determining spell slots per day. +const int PRIMARY_ABILITY_MODIFIER_RULE = TRUE; + +// Enable BACKLASH damage on spells? TRUE for yes, FALSE for no. +const int BACKLASH_DAMAGE = TRUE; + +// Sets the DC adjustment active or inactive for researching spells. +// If TRUE, the player's spell foci feats are used to lower the spell's DC which +// lowers the overall costs of researching the spell. For example, if the +// spell is from the school of Necromancy, and the player has the feat Epic +// Spell Focus: Necromancy, then the DC for the rearch would be lowered by +// six. This would (under default ELHB settings) lower the gold cost by +// 54000 gold and 2160 exp. points, as well as makee the spell accessible +// to the player earlier and with a greater chance of success (due to the +// Spellcraft check). +// Setting this to FALSE will disable this feature. +const int FOCI_ADJUST_DC = TRUE; + +// This sets the multiplier for the cost, in gold, to a player for the +// researching of an epic spell. The number is multiplied by the DC of +// the spell to be researched. ELHB default is 9000. +const int GOLD_MULTIPLIER = 9000; + +// This sets the number to divide the gold cost by to determine the cost, +// in experience, to research an epic spell. The formula is as follows: +// XP Cost = Spell's DC x GOLD_MULTIPLIER / XP_FRACTION. The default from +// the ELHB is 25. +const int XP_FRACTION = 25; + +// Set the number you want to divide the gold cost by for research failures. +// Examples: 2 would result in half the loss of the researcher's gold. +// 3 would result in a third of the gold lost. +// 4 would result in a quarter, etc. +const int FAILURE_FRACTION_GOLD = 2; + +// Sets the percentage chance that a seed book is destroyed on a use of it. +// 0 = the book is never randomly destroyed from reading (using) it. +// 100 = the book is always destroyed from reading it. +// NOTE! This function is only ever called when the player actually acquires +// the seed feat. It is a way to control mass "gift-giving" amongst players +const int BOOK_DESTRUCTION = 50; +*/ + + +// Play cutscenes for learning Epic Spell Seeds and researching Epic Spells? +const int PLAY_RESEARCH_CUTS = FALSE; +const int PLAY_SPELLSEED_CUT = FALSE; + +// What school of magic does each spell belong to? (for research cutscenes) +// A = Abjuration +// C = Conjuration +// D = Divination +// E = Enchantment +// V = Evocation +// I = Illusion +// N = Necromancy +// T = Transmutation +// Between the quotation marks, enter the name of the cutscene script. +const string SCHOOL_A = ""; +const string SCHOOL_C = ""; +const string SCHOOL_D = ""; +const string SCHOOL_E = ""; +const string SCHOOL_V = ""; +const string SCHOOL_I = ""; +const string SCHOOL_N = ""; +const string SCHOOL_T = ""; +const string SPELLSEEDS_CUT = ""; + + + +/****************************************************************************** +FUNCTION DECLARATIONS +******************************************************************************/ + + + +// Returns the combined caster level of oPC. +int GetTotalCastingLevel(object oPC); + +// returns TRUE if oPC is an Epic level Dread Necromancer +int GetIsEpicDreadNecromancer(object oPC); + +// returns TRUE if oPC is an Epic level warmage +int GetIsEpicWarmage(object oPC); + +// returns TRUE if oPC is an Epic level healer. +int GetIsEpicHealer(object oPC); + +// returns TRUE if oPC is an Epic level favored soul. +int GetIsEpicFavSoul(object oPC); + +// Returns TRUE if oPC is an Epic level cleric. +int GetIsEpicCleric(object oPC); + +// Returns TRUE if oPC is an Epic level druid. +int GetIsEpicDruid(object oPC); + +// Returns TRUE if oPC is an Epic level sorcerer. +int GetIsEpicSorcerer(object oPC); + +// Returns TRUE if oPC is an Epic level wizard. +int GetIsEpicWizard(object oPC); + +// returns TRUE if oPC is an epic level shaman. +int GetIsEpicShaman(object oPC); + +// returns TRUE if oPC is an epic level witch. +int GetIsEpicWitch(object oPC); + +// returns TRUE if oPC is an epic level sublime chord. +int GetIsEpicSublimeChord(object oPC); + +// returns TRUE if oPC is an epic level archivist. +int GetIsEpicArchivist(object oPC); + +// returns TRUE if oPC is an epic level beguiler. +int GetIsEpicBeguiler(object oPC); + +// returns TRUE if oPC is an epic level ur-priest. +int GetIsEpicUrPriest(object oPC); + +// returns TRUE if oPC is an epic level blighter. +int GetIsEpicBlighter(object oPC); + +// returns TRUE if oPC is an Epic spellcaster +int GetIsEpicSpellcaster(object oPC); + +// Performs a check on the book to randomly destroy it or not when used. +void DoBookDecay(object oBook, object oPC); + +// Returns oPC's spell slot limit, based on Lore and on optional rules. +int GetEpicSpellSlotLimit(object oPC); + +// Returns the number of remaining unused spell slots for oPC. +int GetSpellSlots(object oPC); + +// Replenishes oPC's Epic spell slots. +void ReplenishSlots(object oPC); + +// Decrements oPC's Epic spell slots by one. +void DecrementSpellSlots(object oPC); + +// Lets oPC know how many Epic spell slots remain for use. +void MessageSpellSlots(object oPC); + +// Returns a Spellcraft check for oPC, based on optional rules. +int GetSpellcraftCheck(object oPC); + +// Returns the Spellcraft skill level of oPC, based on optional rules. +int GetSpellcraftSkill(object oPC); + +// Returns TRUE if oPC has enough gold to research the spell. +int GetHasEnoughGoldToResearch(object oPC, int nSpellDC); + +// Returns TRUE if oPC has enough excess experience to research the spell. +int GetHasEnoughExperienceToResearch(object oPC, int nSpellDC); + +// Returns TRUE if oPC has the passed in required feats (Seeds or other Epic spells)... needs BLAH_IP's +int GetHasRequiredFeatsForResearch(object oPC, int nReq1, int nReq2 = 0, int nReq3 = 0, int nReq4 = 0, + int nSeed1 = 0, int nSeed2 = 0, int nSeed3 = 0, int nSeed4 = 0, int nSeed5 = 0); + +// Returns success (TRUE) or failure (FALSE) in oPC's researching of a spell. +int GetResearchResult(object oPC, int nSpellDC); + +// Takes the gold & experience (depending on success) from oPC for researching. +void TakeResourcesFromPC(object oPC, int nSpellDC, int nSuccess); + +// Returns TRUE if oPC can cast the spell. +int GetCanCastSpell(object oPC, int nEpicSpell); + +// Returns the adjusted DC of a spell that takes into account oPC's Spell Foci. +int GetDCSchoolFocusAdjustment(object oPC, string sChool); + +// Checks to see if oPC has a creature hide. If not, create and equip one. +void EnsurePCHasSkin(object oPC); + +// Add nFeatIP to oPC's creature hide. +void GiveFeat(object oPC, int nFeatIP); + +// Remove nFeatIP from oPC's creature hide. +void TakeFeat(object oPC, int nFeatIP); + +// Checks to see how many castable epic spell feats oPC has ready to use. +// This is used for the control of the radial menu issue. +int GetCastableFeatCount(object oPC); + +// When a contingency spell is active, oCaster loses the use of one slot per day +void PenalizeSpellSlotForCaster(object oCaster); + +// When a contingecy expires, restore the spell slot for the caster. +void RestoreSpellSlotForCaster(object oCaster); + +// Researches an Epic Spell for the caster. +void DoSpellResearch(object oCaster, int nSpellDC, int nSpellIP, string sSchool, object oBook); + +// Cycles through equipped items on oTarget, and unequips any having nImmunityType +void UnequipAnyImmunityItems(object oTarget, int nImmType); + +// Finds a given spell's DC +int GetEpicSpellSaveDC(object oCaster = OBJECT_SELF, object oTarget = OBJECT_INVALID, int nSpellID = -1); + + +int GetHasEpicSpellKnown(int nEpicSpell, object oPC); +void SetEpicSpellKnown(int nEpicSpell, object oPC, int nState = TRUE); + +int GetHasEpicSeedKnown(int nEpicSeed, object oPC); +void SetEpicSeedKnown(int nEpicSeed, object oPC, int nState = TRUE); + +#include "prc_inc_spells" +#include "prc_class_const" +#include "inc_epicspelldef" +#include "inc_epicspellfnc" +#include "inc_utility" +#include "prc_add_spell_dc" +//#include "x2_inc_spellhook" + + +/****************************************************************************** +FUNCTION BODIES +******************************************************************************/ + +int GetIsEpicArchivist(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_ARCHIVIST, oPC, FALSE) > 16 + && GetAbilityScore(oPC, ABILITY_INTELLIGENCE) > 18; +} + +int GetIsEpicBeguiler(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_BEGUILER, oPC, FALSE) > 17 + && GetAbilityScore(oPC, ABILITY_INTELLIGENCE) > 18; +} + +int GetIsEpicCleric(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_CLERIC, oPC, FALSE) > 16 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicDreadNecromancer(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_DREAD_NECROMANCER, oPC, FALSE) > 17 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +int GetIsEpicDruid(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_DRUID, oPC, FALSE) > 16 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicFavSoul(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_FAVOURED_SOUL, oPC, FALSE) > 17 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +int GetIsEpicHealer(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_HEALER, oPC, FALSE) > 16 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicUrPriest(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_UR_PRIEST, oPC, FALSE) > 8 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicShaman(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_SHAMAN, oPC, FALSE) > 16 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicSorcerer(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_SORCERER, oPC, FALSE) > 17 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +int GetIsEpicSublimeChord(object oPC) +{ + return GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oPC) > 8 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +int GetIsEpicBlighter(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_BLIGHTER, oPC, FALSE) > 8 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicWarmage(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_WARMAGE, oPC, FALSE) > 17 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +int GetIsEpicWitch(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_WITCH, oPC, FALSE) > 17 + && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; +} + +int GetIsEpicWizard(object oPC) +{ + return GetPrCAdjustedCasterLevel(CLASS_TYPE_WIZARD, oPC, FALSE) >= 17 + && GetAbilityScore(oPC, ABILITY_INTELLIGENCE) > 18; +} + +int GetIsEpicSpellcaster(object oPC) +{ + if(GetHitDice(oPC) < 21) + return FALSE; + + if(GetIsEpicArchivist(oPC) + || GetIsEpicBeguiler(oPC) + || GetIsEpicCleric(oPC) + || GetIsEpicDreadNecromancer(oPC) + || GetIsEpicDruid(oPC) + || GetIsEpicFavSoul(oPC) + || GetIsEpicHealer(oPC) + || GetIsEpicUrPriest(oPC) + || GetIsEpicShaman(oPC) + || GetIsEpicSorcerer(oPC) + || GetIsEpicSublimeChord(oPC) + || GetIsEpicBlighter(oPC) + || GetIsEpicWarmage(oPC) + || GetIsEpicWitch(oPC) + || GetIsEpicWizard(oPC)) + return TRUE; + + return FALSE; +} + +void DoBookDecay(object oBook, object oPC) +{ + if (d100() >= GetPRCSwitch(PRC_EPIC_BOOK_DESTRUCTION)) + { + DestroyObject(oBook, 2.0); + SendMessageToPC(oPC, MES_BOOK_DESTROYED); + } +} + +int GetEpicSpellSlotLimit(object oPC) +{ + int nLimit; + int nPen = GetLocalInt(oPC, "nSpellSlotPenalty"); + int nBon = GetLocalInt(oPC, "nSpellSlotBonus"); + // What's oPC's Lore skill?. + nLimit = GetSkillRank(SKILL_LORE, oPC); + // Variant rule implementation. + if (GetPRCSwitch(PRC_EPIC_PRIMARY_ABILITY_MODIFIER_RULE) == TRUE) + { + if (GetIsEpicSorcerer(oPC) || GetIsEpicFavSoul(oPC) || GetIsEpicWarmage(oPC) || GetIsEpicDreadNecromancer(oPC)) + { + nLimit -= GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + nLimit += GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + else if (GetIsEpicCleric(oPC) || GetIsEpicDruid(oPC) || GetIsEpicHealer(oPC) || GetIsEpicBlighter(oPC) || GetIsEpicShaman(oPC) || GetIsEpicUrPriest(oPC)) + { + nLimit -= GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + nLimit += GetAbilityModifier(ABILITY_WISDOM, oPC); + } + } + // Primary calculation of slots. + nLimit /= 10; + // Modified calculation (for contingencies, bonuses, etc) + nLimit = nLimit + nBon; + nLimit = nLimit - nPen; + return nLimit; +} + +int GetSpellSlots(object oPC) +{ + int nSlots = GetLocalInt(oPC, "nEpicSpellSlots"); + if(!GetIsPC(oPC) && !GetLocalInt(oPC, "EpicSpellSlotsReplenished")) + { + nSlots = GetEpicSpellSlotLimit(oPC); + SetLocalInt(oPC, "EpicSpellSlotsReplenished", TRUE); + SetLocalInt(oPC, "nEpicSpellSlots", nSlots); + } + return nSlots; +} + +void ReplenishSlots(object oPC) +{ + SetLocalInt(oPC, "nEpicSpellSlots", GetEpicSpellSlotLimit(oPC)); + MessageSpellSlots(oPC); +} + +void DecrementSpellSlots(object oPC) +{ + SetLocalInt(oPC, "nEpicSpellSlots", GetLocalInt(oPC, "nEpicSpellSlots")-1); + MessageSpellSlots(oPC); +} + +void MessageSpellSlots(object oPC) +{ + SendMessageToPC(oPC, "You now have " + + IntToString(GetSpellSlots(oPC)) + + " Epic spell slots available."); +} + +int GetHasEpicSpellKnown(int nEpicSpell, object oPC) +{ + int nReturn = GetPersistantLocalInt(oPC, "EpicSpellKnown_"+IntToString(nEpicSpell)); + if(!nReturn) + nReturn = GetHasFeat(GetResearchFeatForSpell(nEpicSpell), oPC); + return nReturn; +} + +void SetEpicSpellKnown(int nEpicSpell, object oPC, int nState = TRUE) +{ + SetPersistantLocalInt(oPC, "EpicSpellKnown_"+IntToString(nEpicSpell), nState); +} + +int GetHasEpicSeedKnown(int nEpicSeed, object oPC) +{ + int nReturn = GetPersistantLocalInt(oPC, "EpicSeedKnown_"+IntToString(nEpicSeed)); + if(!nReturn) + nReturn = GetHasFeat(GetFeatForSeed(nEpicSeed), oPC); + return nReturn; +} + +void SetEpicSeedKnown(int nEpicSeed, object oPC, int nState = TRUE) +{ + SetPersistantLocalInt(oPC, "EpicSeedKnown_"+IntToString(nEpicSeed), nState); +} + +int GetSpellcraftCheck(object oPC) +{ + // Get oPC's skill rank. + int nCheck = GetSpellcraftSkill(oPC); + // Do the check, dependant on "Take 10" variant rule. + if (GetPRCSwitch(PRC_EPIC_TAKE_TEN_RULE) == TRUE) + nCheck += 10; + else + nCheck += d20(); + return nCheck; +} + +int GetSpellcraftSkill(object oPC) +{ + // Determine initial Spellcraft skill. + int nSkill = GetSkillRank(SKILL_SPELLCRAFT, oPC); + // Variant rule implementation. + if (GetPRCSwitch(PRC_EPIC_PRIMARY_ABILITY_MODIFIER_RULE) == TRUE) + { + if (GetIsEpicSorcerer(oPC) || GetIsEpicFavSoul(oPC) || GetIsEpicWarmage(oPC) || GetIsEpicDreadNecromancer(oPC)) + { + nSkill -= GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + nSkill += GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + else if (GetIsEpicCleric(oPC) || GetIsEpicDruid(oPC) || GetIsEpicHealer(oPC) || GetIsEpicBlighter(oPC) || GetIsEpicShaman(oPC) || GetIsEpicUrPriest(oPC)) + { + nSkill -= GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + nSkill += GetAbilityModifier(ABILITY_WISDOM, oPC); + } + } + return nSkill; +} + +int GetHasEnoughGoldToResearch(object oPC, int nSpellDC) +{ + int nCost = nSpellDC * GetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER); + if (GetHasGPToSpend(oPC, nCost)) + return TRUE; + return FALSE; +} + +int GetHasEnoughExperienceToResearch(object oPC, int nSpellDC) +{ + int nXPCost = nSpellDC * GetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER) / GetPRCSwitch(PRC_EPIC_XP_FRACTION); + if (GetHasXPToSpend(oPC, nXPCost)) + return TRUE; + return FALSE; +} + +int GetHasRequiredFeatsForResearch(object oPC, int nReq1, int nReq2 = 0, int nReq3 = 0, int nReq4 = 0, + int nSeed1 = 0, int nSeed2 = 0, int nSeed3 = 0, int nSeed4 = 0, int nSeed5 = 0) +{ + if(DEBUG) + { + DoDebug("Requirement #1: " + IntToString(nReq1)); + DoDebug("Requirement #2: " + IntToString(nReq2)); + DoDebug("Requirement #3: " + IntToString(nReq3)); + DoDebug("Requirement #4: " + IntToString(nReq4)); + DoDebug("Seed #1: " + IntToString(nSeed1)); + DoDebug("Seed #2: " + IntToString(nSeed2)); + DoDebug("Seed #3: " + IntToString(nSeed3)); + DoDebug("Seed #4: " + IntToString(nSeed4)); + DoDebug("Seed #4: " + IntToString(nSeed5)); + } + + if ((GetHasFeat(nReq1, oPC) || nReq1 == 0) + && (GetHasFeat(nReq2, oPC) || nReq2 == 0) + && (GetHasFeat(nReq3, oPC) || nReq3 == 0) + && (GetHasFeat(nReq4, oPC) || nReq4 == 0) + && (GetHasEpicSeedKnown(nSeed1, oPC) || nSeed1 == -1) + && (GetHasEpicSeedKnown(nSeed2, oPC) || nSeed2 == -1) + && (GetHasEpicSeedKnown(nSeed3, oPC) || nSeed3 == -1) + && (GetHasEpicSeedKnown(nSeed4, oPC) || nSeed4 == -1) + && (GetHasEpicSeedKnown(nSeed5, oPC) || nSeed5 == -1)) + { + return TRUE; + } + return FALSE; +} +int GetResearchResult(object oPC, int nSpellDC) +{ + int nCheck = GetSpellcraftCheck(oPC); + SendMessageToPC(oPC, "Your spellcraft check was a " + + IntToString(nCheck) + ", against a researching DC of " + + IntToString(nSpellDC)); + if (nCheck >= nSpellDC) + { + SendMessageToPC(oPC, MES_SPELLCRAFT_CHECK_PASS); + return TRUE; + } + else + { + SendMessageToPC(oPC, MES_SPELLCRAFT_CHECK_FAIL); + return FALSE; + } +} + +void TakeResourcesFromPC(object oPC, int nSpellDC, int nSuccess) +{ + if (nSuccess != TRUE) + { + int nGold = nSpellDC * + GetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER) / GetPRCSwitch(PRC_EPIC_FAILURE_FRACTION_GOLD); + SpendGP(oPC, nGold); + } + else + { + int nGold = nSpellDC * GetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER); + SpendGP(oPC, nGold); + int nXP = nSpellDC * GetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER) / GetPRCSwitch(PRC_EPIC_XP_FRACTION); + SpendXP(oPC, nXP); + } +} + +int GetCanCastSpell(object oPC, int nEpicSpell) +{ + int nSpellDC = GetDCForSpell(nEpicSpell); + string sChool = GetSchoolForSpell(nEpicSpell); + int nSpellXP =GetCastXPForSpell(nEpicSpell); + // Adjust the DC to account for Spell Foci feats. + nSpellDC -= GetDCSchoolFocusAdjustment(oPC, sChool); + int nCheck = GetSpellcraftCheck(oPC); + // Does oPC already know it + if (!GetHasEpicSpellKnown(nEpicSpell, oPC)) + { + return FALSE; + } + if (!(GetSpellSlots(oPC) >= 1)) + { // No? Cancel spell, then. + SendMessageToPC(oPC, MES_CANNOT_CAST_SLOTS); + return FALSE; + } + if (GetPRCSwitch(PRC_EPIC_XP_COSTS) == TRUE) + { + // Does oPC have the needed XP available to cast the spell? + if (!GetHasXPToSpend(oPC, nSpellXP)) + { // No? Cancel spell, then. + SendMessageToPC(oPC, MES_CANNOT_CAST_XP); + return FALSE; + } + } + // Does oPC pass the Spellcraft check for the spell's casting? + if (!(nCheck >= nSpellDC)) + { // No? + SendMessageToPC(oPC, MES_SPELLCRAFT_CHECK_FAIL); + SendMessageToPC(oPC, + IntToString(nCheck) + " against a DC of " + IntToString(nSpellDC)); + // Failing a Spellcraft check still costs a spell slot, so decrement... + DecrementSpellSlots(oPC); + return FALSE; + } + // If the answer is YES to all three, cast the spell! + SendMessageToPC(oPC, MES_SPELLCRAFT_CHECK_PASS); + SendMessageToPC(oPC, + IntToString(nCheck) + " against a DC of " + IntToString(nSpellDC)); + SpendXP(oPC, nSpellXP); // Only spends the XP on a successful casting. + DecrementSpellSlots(oPC); + return TRUE; +} + +void GiveFeat(object oPC, int nFeatIP) +{ + object oSkin = GetPCSkin(oPC); + if (oSkin != OBJECT_INVALID) + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(nFeatIP), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); +} + +void TakeFeat(object oPC, int nFeatIP) +{ + object oSkin = GetPCSkin(oPC); + itemproperty ipX = GetFirstItemProperty(oSkin); + while (GetIsItemPropertyValid(ipX)) + { + if (GetItemPropertyType(ipX) == ITEM_PROPERTY_BONUS_FEAT) + { + if(GetItemPropertySubType(ipX) == nFeatIP) + { + RemoveItemProperty(oSkin, ipX); + break; + } + } + ipX = GetNextItemProperty(oSkin); + } +} + +int GetCastableFeatCount(object oPC) +{ + int nX = 0; + int i = 0; + int nFeat = GetFeatForSpell(i); + while(nFeat != 0) + { + //test for the castable feat + if(GetHasFeat(nFeat, oPC)) + nX += 1; + i++; + nFeat = GetFeatForSpell(i); + } + return nX; +} + +void PenalizeSpellSlotForCaster(object oCaster) +{ + int nMod = GetLocalInt(oCaster, "nSpellSlotPenalty"); + SetLocalInt(oCaster, "nSpellSlotPenalty", nMod + 1); + SendMessageToPC(oCaster, MES_CONTINGENCIES_YES1); + SendMessageToPC(oCaster, MES_CONTINGENCIES_YES2); + SendMessageToPC(oCaster, "Your epic spell slot limit is now " + + IntToString(GetEpicSpellSlotLimit(oCaster)) + "."); +} + +void RestoreSpellSlotForCaster(object oCaster) +{ + int nMod = GetLocalInt(oCaster, "nSpellSlotPenalty"); + if (nMod > 0) SetLocalInt(oCaster, "nSpellSlotPenalty", nMod - 1); + SendMessageToPC(oCaster, "Your epic spell slot limit is now " + + IntToString(GetEpicSpellSlotLimit(oCaster)) + "."); +} + +void DoSpellResearch(object oCaster, int nSpellDC, int nSpellIP, string sSchool, object oBook) +{ + float fDelay = 2.0; + string sCutScript; + int nResult = GetResearchResult(oCaster, nSpellDC); + if (PLAY_RESEARCH_CUTS == TRUE) + { + if (sSchool == "A") sCutScript = SCHOOL_A; + if (sSchool == "C") sCutScript = SCHOOL_C; + if (sSchool == "D") sCutScript = SCHOOL_D; + if (sSchool == "E") sCutScript = SCHOOL_E; + if (sSchool == "I") sCutScript = SCHOOL_I; + if (sSchool == "N") sCutScript = SCHOOL_N; + if (sSchool == "T") sCutScript = SCHOOL_T; + if (sSchool == "V") sCutScript = SCHOOL_V; + ExecuteScript(sCutScript, oCaster); + fDelay = 10.0; + } + DelayCommand(fDelay, TakeResourcesFromPC(oCaster, nSpellDC, nResult)); + if (nResult == TRUE) + { + DelayCommand(fDelay, SendMessageToPC(oCaster, GetName(oCaster) + " " + MES_RESEARCH_SUCCESS)); + //DelayCommand(fDelay, GiveFeat(oCaster, nSpellIP)); + DelayCommand(fDelay, SetEpicSpellKnown(nSpellIP, oCaster, TRUE)); + DelayCommand(fDelay, DestroyObject(oBook)); + //research time + //1 day per 50,000GP +1 + int nDays = (nSpellDC * GetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER))/50000; + nDays++; + float fSeconds = HoursToSeconds(24*nDays); + AdvanceTimeForPlayer(oCaster, fSeconds); + } + else + { + DelayCommand(fDelay, SendMessageToPC(oCaster, GetName(oCaster) + " " + MES_RESEARCH_FAILURE)); + } +} + +void UnequipAnyImmunityItems(object oTarget, int nImmType) +{ + object oItem; + int nX; + for (nX = 0; nX <= 13; nX++) // Does not include creature items in search. + { + oItem = GetItemInSlot(nX, oTarget); + // Debug. + //SendMessageToPC(oTarget, "Checking slot " + IntToString(nX)); + if (oItem != OBJECT_INVALID) + { + // Debug. + //SendMessageToPC(oTarget, "Valid item."); + itemproperty ipX = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(ipX)) + { + // Debug. + //SendMessageToPC(oTarget, "Valid ip"); + if (GetItemPropertySubType(ipX) == nImmType) + { + // Debug. + //SendMessageToPC(oTarget, "ip match!!"); + SendMessageToPC(oTarget, GetName(oItem) + + " cannot be equipped at this time."); + AssignCommand(oTarget, ClearAllActions()); + AssignCommand(oTarget, ActionUnequipItem(oItem)); + break; + } + else + ipX = GetNextItemProperty(oItem); + } + } + } +} + +int GetTotalCastingLevel(object oCaster) +{ + int iBestArcane = GetLevelByTypeArcaneFeats(); + int iBestDivine = GetLevelByTypeDivineFeats(); + int iBest = (iBestDivine > iBestArcane) ? iBestDivine : iBestArcane; + + //SendMessageToPC(oCaster, "Epic casting at level " + IntToString(iBest)); + + return iBest; +} + +int GetDCSchoolFocusAdjustment(object oPC, string sChool) +{ + int nNewDC = 0; + if (sChool == "A") // Abjuration spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ABJURATION, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ABJURATION, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_ABJURATION, oPC)) nNewDC = 2; + } + if (sChool == "C") // Conjuration spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_CONJURATION, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_CONJURATION, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_CONJURATION, oPC)) nNewDC = 2; + } + if (sChool == "D") // Divination spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_DIVINATION, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_DIVINIATION, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_DIVINATION, oPC)) nNewDC = 2; + } + if (sChool == "E") // Enchantment spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ENCHANTMENT, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_ENCHANTMENT, oPC)) nNewDC = 2; + } + if (sChool == "V") // Evocation spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_EVOCATION, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_EVOCATION, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_EVOCATION, oPC)) nNewDC = 2; + } + if (sChool == "I") // Illusion spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ILLUSION, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ILLUSION, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_ILLUSION, oPC)) nNewDC = 2; + } + if (sChool == "N") // Necromancy spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_NECROMANCY, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_NECROMANCY, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_NECROMANCY, oPC)) nNewDC = 2; + } + if (sChool == "T") // Transmutation spell? + { + if (GetHasFeat(FEAT_EPIC_SPELL_FOCUS_TRANSMUTATION, oPC)) nNewDC = 6; + else if (GetHasFeat(FEAT_GREATER_SPELL_FOCUS_TRANSMUTATION, oPC)) nNewDC = 4; + else if (GetHasFeat(FEAT_SPELL_FOCUS_TRANSMUTATION, oPC)) nNewDC = 2; + } + return nNewDC; +} + +int GetEpicSpellSaveDC(object oCaster = OBJECT_SELF, object oTarget = OBJECT_INVALID, int nSpellID = -1) +{ + int iDiv = GetPrCAdjustedCasterLevelByType(TYPE_DIVINE, oCaster); // ie. wisdom determines DC + int iWiz = GetPrCAdjustedCasterLevel(CLASS_TYPE_WIZARD, oCaster); // int determines DC + int iWMa = GetPrCAdjustedCasterLevel(CLASS_TYPE_WARMAGE, oCaster); // cha determines DC + int iDNc = GetPrCAdjustedCasterLevel(CLASS_TYPE_DREAD_NECROMANCER, oCaster); // cha determines DC + int iSor = GetPrCAdjustedCasterLevel(CLASS_TYPE_SORCERER, oCaster); // cha determines DC + int iWit = GetPrCAdjustedCasterLevel(CLASS_TYPE_WITCH, oCaster); // wis determines DC + int iArc = GetPrCAdjustedCasterLevel(CLASS_TYPE_ARCHIVIST, oCaster); // int determines DC + int iBeg = GetPrCAdjustedCasterLevel(CLASS_TYPE_BEGUILER, oCaster); // int determines DC + int iTpl = GetPrCAdjustedCasterLevel(CLASS_TYPE_TEMPLAR, oCaster); // cha determines DC + + int iBest = 0; + int iAbility; + if(nSpellID == -1) + nSpellID = PRCGetSpellId(); + + if (iArc > iBest) { iAbility = ABILITY_INTELLIGENCE; iBest = iWit; } + if (iTpl > iBest) { iAbility = ABILITY_CHARISMA; iBest = iTpl; } + if (iWiz > iBest) { iAbility = ABILITY_INTELLIGENCE; iBest = iWiz; } + if (iWMa > iBest) { iAbility = ABILITY_CHARISMA; iBest = iWMa; } + if (iDNc > iBest) { iAbility = ABILITY_CHARISMA; iBest = iDNc; } + if (iSor > iBest) { iAbility = ABILITY_CHARISMA; iBest = iSor; } + if (iWit > iBest) { iAbility = ABILITY_WISDOM; iBest = iWit; } + if (iBeg > iBest) { iAbility = ABILITY_INTELLIGENCE; iBest = iBeg; } + if (iDiv > iBest) { iAbility = ABILITY_WISDOM; iBest = iDiv; } + + int nDC; + if (iBest) nDC = 20 + GetAbilityModifier(iAbility, oCaster); + else nDC = 20; // DC = 20 if the epic spell is cast some other way. + + nDC += GetDCSchoolFocusAdjustment(oCaster, Get2DACache("spells", "school", nSpellID)); + nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, GetSpellSchool(nSpellID)); + + return nDC; +} + +// Test main +//void main(){} diff --git a/src/include/inc_eventhook.nss b/src/include/inc_eventhook.nss new file mode 100644 index 0000000..f1cd511 --- /dev/null +++ b/src/include/inc_eventhook.nss @@ -0,0 +1,883 @@ +//:://///////////////////////////////////////////// +//:: Generic eventhook include +//:: inc_eventhook +//::////////////////////////////////////////////// +/** @file + A system for scheduling scripts to be run on + an arbitrary event during runtime (instead of + being hardcoded in compilation). + + Scheduling a script happens by calling + AddEventScript with the object the script is + to be run on (and on which the data about the + script is stored on), an EVENT_* constant + determining the event that the script is to be + run on and the name of the script to be run. + + In addition to these, there is a parameter to + control whether the script will be just during + the next invocation of the event, or during all + invocations from now on until the script is + explicitly descheduled. + This feature only automatically works when using + ExecuteAllScriptsHookedToEvent(). That is, merely + viewing the eventscript list does not trigger the + effect. + + See the comments in function prototype section for + more details. + + + Added event constants to be used with items. For + example, now you can define a script to be fired + for The Sword of Foo every time someone equips it. + + + NOTE: Persistence of scripts hooked to non-creatures + over module boundaries is not guaranteed. ie, if + the player takes abovementioned Sword of Foo to + another module, it most likely will lose the locals + defining the script hooked into it. + + + @author Ordedan + @date Created - 28.02.2005 + @date Modified - 26.05.2005 + @date Modified - 04.09.2005 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +// Module events + +/// Module event - On Acquire Item +const int EVENT_ONACQUIREITEM = 1; +/// Module event - On Activate Item +const int EVENT_ONACTIVATEITEM = 2; +/// Module event - On Client Enter +const int EVENT_ONCLIENTENTER = 3; +/// Module event - On Client Leave +const int EVENT_ONCLIENTLEAVE = 4; +/// Module event - On Cutscene Abort +const int EVENT_ONCUTSCENEABORT = 5; +/// Module event - On Heartbeat +const int EVENT_ONHEARTBEAT = 6; +/// Module event - On Player Death +const int EVENT_ONPLAYERDEATH = 9; +/// Module event - On Player Dying +const int EVENT_ONPLAYERDYING = 10; +/// Module event - On Player Equip Item +const int EVENT_ONPLAYEREQUIPITEM = 11; +/// Module event - On Player Level Up +const int EVENT_ONPLAYERLEVELUP = 12; +/// Module event - On Player Rest Cancelled +const int EVENT_ONPLAYERREST_CANCELLED = 13; +/// Module event - On Player Rest Started +const int EVENT_ONPLAYERREST_STARTED = 14; +/// Module event - On Player Rest Finished +const int EVENT_ONPLAYERREST_FINISHED = 15; +/// Module event - On Player Unequip Item +const int EVENT_ONPLAYERUNEQUIPITEM = 16; +/// Module event - On Player Respawn +const int EVENT_ONPLAYERRESPAWN = 17; +/// Module event - On Unacquire Item +const int EVENT_ONUNAQUIREITEM = 18; +/// Module event - On Player Chat +const int EVENT_ONPLAYERCHAT = 49; + +/** + * Module Event - On Userdefined + * This has special handling + * @see prc_onuserdef.nss + */ +const int EVENT_ONUSERDEFINED = 19; + + +// Other events +/// Virtual event - On Hit +/// Requires OnHitCastSpell: Unique on the weapon used +const int EVENT_ONHIT = 20; +/// Virtual event - On Spell Cast +//const int EVENT_ONSPELLCAST = 21; +/// Virtual event - On Power Manifest +//const int EVENT_ONPOWERMANIFEST = 22; + + +/// Virtual event - On Player Level Down +/// WARNING: Event detection is slightly inaccurate +const int EVENT_ONPLAYERLEVELDOWN = 35; + +/// Virtual event - On Physically Attacked +/// WARNING: Event detection is highly inaccurate +const int EVENT_VIRTUAL_ONPHYSICALATTACKED = 36; + +/// Virtual event - On Blocked +/// WARNING: Event detection is inaccurate +const int EVENT_VIRTUAL_ONBLOCKED = 37; + +/// Virtual event - On Combat Round End +/// WARNING: Event detection is inaccurate +const int EVENT_VIRTUAL_ONCOMBATROUNDEND = 38; + +/// Virtual event - On Conversation +/// Does not work on PCs! +const int EVENT_VIRTUAL_ONCONVERSATION = 39; + +/// Virtual event - On Damaged +/// WARNING: Event detection is slightly inaccurate +const int EVENT_VIRTUAL_ONDAMAGED = 40; + +/// Virtual event - On Disturbed +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONDISTURBED = 41; + +/// Virtual event - On Perception +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONPERCEPTION = 42; + +/// Virtual event - On Spawned +const int EVENT_VIRTUAL_ONSPAWNED = 43; + +/// Virtual event - On Spell Cast At +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONSPELLCASTAT = 44; + +/// Virtual event - On Death +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONDEATH = 45; + +/// Virtual event - On Rested +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONRESTED = 46; + +/// Virtual event - On User Defined +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONUSERDEFINED = 47; + +/// Virtual event - On Heartbeat +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONHEARTBEAT = 48; + +/// Virtual event - On Player Chat +/// WARNING: Event detection may be inaccurate +const int EVENT_VIRTUAL_ONPLAYERCHAT = 50; + +// NPC events +/// NPC event - On Blocked +const int EVENT_NPC_ONBLOCKED = 23; +/// NPC event - On Combat Round End +const int EVENT_NPC_ONCOMBATROUNDEND = 24; +/// NPC event - On Conversation +const int EVENT_NPC_ONCONVERSATION = 25; +/// NPC event - On Damaged +const int EVENT_NPC_ONDAMAGED = 26; +/// NPC event - On Death +const int EVENT_NPC_ONDEATH = 27; +/// NPC event - On Disturbed +const int EVENT_NPC_ONDISTURBED = 28; +/// NPC event - On Heartbeat +const int EVENT_NPC_ONHEARTBEAT = 29; +/// NPC event - On Perception +const int EVENT_NPC_ONPERCEPTION = 30; +/// NPC event - On Physically Attacked +const int EVENT_NPC_ONPHYSICALATTACKED = 31; +/// NPC event - On Rested +const int EVENT_NPC_ONRESTED = 32; +/// NPC event - On Spell Cast At +const int EVENT_NPC_ONSPELLCASTAT = 34; + + +/* Item events */ +/// Virtual item event - On Acquire Item +const int EVENT_ITEM_ONACQUIREITEM = 1000; +/// Virtual item event - On Activate Item +const int EVENT_ITEM_ONACTIVATEITEM = 1001; +/// Virtual item event - On Player Equip Item +const int EVENT_ITEM_ONPLAYEREQUIPITEM = 1002; +/// Virtual item event - On Player Unequip Item +const int EVENT_ITEM_ONPLAYERUNEQUIPITEM = 1003; +/// Virtual item event - On Acquire Item +const int EVENT_ITEM_ONUNAQUIREITEM = 1004; +/// Virtual item event - On Hit +/// Requires OnHitCastSpell: Unique on the item used to hit +const int EVENT_ITEM_ONHIT = 1005; + +/* Placeable events */ +//Note these will only fire for placeables using the +//prc_plc_* scriptset + +// Placeable event - OnClick (1.67 or later only) +const int EVENT_PLACEABLE_ONCLICK = 3001; +// Placeable event - OnClose +const int EVENT_PLACEABLE_ONCLOSE = 3002; +// Placeable event - OnDamaged +const int EVENT_PLACEABLE_ONDAMAGED = 3003; +// Placeable event - OnDeath +const int EVENT_PLACEABLE_ONDEATH = 3004; +// Placeable event - OnHeartbeat +const int EVENT_PLACEABLE_ONHEARTBEAT = 3005; +// Placeable event - OnDisturbed +const int EVENT_PLACEABLE_ONDISTURBED = 3006; +// Placeable event - OnLock +const int EVENT_PLACEABLE_ONLOCK = 3007; +// Placeable event - OnPhysicalAttacked +const int EVENT_PLACEABLE_ONATTACKED = 3008; +// Placeable event - OnOpen +const int EVENT_PLACEABLE_ONOPEN = 3009; +// Placeable event - OnSpellCastAt +const int EVENT_PLACEABLE_ONSPELL = 3010; +// Placeable event - OnUnLock +const int EVENT_PLACEABLE_ONUNLOCK = 3011; +// Placeable event - OnUsed +const int EVENT_PLACEABLE_ONUSED = 3012; +// Placeable event - OnUserDefined +const int EVENT_PLACEABLE_ONUSERDEFINED = 3013; + +/* Door events */ +//Note these will only fire for doors using the +//Note that placeable doors use the placeable set +//prc_door_* scriptset +// Door event - OnAreaTransitionClick +const int EVENT_DOOR_ONTRANSITION = 4001; +// Door event - OnClose +const int EVENT_DOOR_ONCLOSE = 4002; +// Door event - OnDamaged +const int EVENT_DOOR_ONDAMAGED = 4003; +// Door event - OnDeath +const int EVENT_DOOR_ONDEATH = 4004; +// Door event - OnFailToOpen +const int EVENT_DOOR_ONFAILTOOPEN = 4005; +// Door event - OnHeartbeat +const int EVENT_DOOR_ONHEARTBEAT = 4006; +// Door event - OnLock +const int EVENT_DOOR_ONLOCK = 4007; +// Door event - OnPhysicalAttacked +const int EVENT_DOOR_ONATTACKED = 4008; +// Door event - OnOpen +const int EVENT_DOOR_ONOPEN = 4009; +// Door event - OnSpellCastAt +const int EVENT_DOOR_ONSPELL = 4010; +// Door event - OnUnLock +const int EVENT_DOOR_ONUNLOCK = 4011; +// Door event - OnUserDefined +const int EVENT_DOOR_ONUSERDEFINED = 4012; + + + +/* Callback hooks */ +/// Callback hook - Unarmed evaluation +const int CALLBACKHOOK_UNARMED = 2000; + + +/// When TRUE, ExecuteAllScriptsHookedToEvent() will print a list of the scripts it executes. +/// Disabling DEBUG will disablet this, too. +const int PRINT_EVENTHOOKS = FALSE; + +///////////////////////// +// Internal constants // +///////////////////////// + +const string PERMANENCY_SUFFIX = "_permanent"; + +// Unused events +//const int EVENT_ONMODULELOAD = 7; // Not included, since anything that would be hooked to this event +//const string NAME_ONMODULELOAD = "prc_event_array_onmoduleload"; // should be directly in the eventscript anyway. +//const int EVENT_NPC_ONSPAWN = 33; // No way to add script to the hook for a creature before this fires +//const string NAME_NPC_ONSPAWN = "prc_event_array_npc_onspawn"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Adds the given script to be fired when the event next occurs for the given object. + * NOTE! Do not add a script that calls ExecuteAllScriptsHookedToEvent() to an eventhook. + * It will result in recursive infinite loop. + * + * @param oObject The object that the script is to be fired for + * @param nEvent One of the EVENT_* constants defined in "inc_eventhook", + * (or any number, but then need to be a bit more careful, since the system won't complain if you typo it) + * @param sScript The script to be fired on the event. Special case: "" will not be stored. + * @param bPermanent Unless this is set, the script will be only fired once, after which it + * is removed from the list + * + * @param bAllowDuplicate This being set makes the function first check if a script with + * the same name is already queued for the event and avoids adding a + * duplicate. This will not remove duplicates already present, though. + */ +int AddEventScript(object oObject, int nEvent, string sScript, int bPermanent = FALSE, int bAllowDuplicate = TRUE); + +/** + * Removes all instances of the given script from the given eventhook + * + * @param oObject The object that the script is to be removed from the list for. + * @param nEvent One of the EVENT_* constants defined in "inc_eventhook" + * @param sScript The script to be removed from the event + * + * @param bPermanent Depending on the state of this switch, the script is either removed + * from the one-shot or permanent list. + * + * @param bIgnorePermanency Setting this to true will make the function clear the script from + * both one-shot and permanent lists, regardless of the value of bPermanent + */ +void RemoveEventScript(object oObject, int nEvent, string sScript, int bPermanent = FALSE, int bIgnorePermanency = FALSE); + +/** + * Removes all scripts in the given eventhook + * + * @param oObject The object to clear script list for. + * @param nEvent One of the EVENT_* constants defined in "inc_eventhook" + * + * @param bPermanent Depending on the state of this switch, the scripts are either removed + * from the one-shot or permanent list. + * + * @param bIgnorePermanency Setting this to true will make the function clear both one-shot and + * permanent lists, regardless of the value of bPermanent + */ +void ClearEventScriptList(object oObject, int nEvent, int bPermanent = FALSE, int bIgnorePermanency = FALSE); + +/** + * Gets the first script hooked to the given event. + * This must be called before any calls to GetNextEventScript() are made. + * + * @param oObject The object to get a script for. + * @param nEvent One of the EVENT_* constants defined in "inc_eventhook" + * @param bPermanent Which list to get the first script from. + * + * @return The name of the first script stored, or "" if one was not found. + */ +string GetFirstEventScript(object oObject, int nEvent, int bPermanent); + +/** + * Gets the next script hooked to the given event. + * You should call GetFirstEventScript before calling this. + * + * @param oObject The object to get a script for. + * @param nEvent One of the EVENT_* constants defined in "inc_eventhook" + * @param bPermanent Which list to get the first script from. + * + * @return The name of the next script in the list, or "" if there are no more scripts + * left. Also returns "" if GetFirstEventScript hasn't been called. + */ +string GetNextEventScript(object oObject, int nEvent, int bPermanent); + +/** + * Executes all scripts in both the one-shot and permanent hooks and + * clears scripts off the one-shot hook afterwards. + * It is recommended this be used instead of manually going through + * the script lists with Get(First|Next)EventScript. + * + * All the scripts will be ExecuteScripted on OBJECT_SELF, so they will + * behave as if being in the script slot for that event. + * + * @param oObject The object to execute listed scripts for. + * @param nEvent One of the EVENT_* constants defined in "inc_eventhook" + */ +void ExecuteAllScriptsHookedToEvent(object oObject, int nEvent); + +/** + * Gets the event currently being run via ExecuteAllScriptsHookedToEvent + * + * @return One of the EVENT_* constants if an ExecuteAllScriptsHookedToEvent + * is being run, FALSE otherwise. + */ +int GetRunningEvent(); + + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "inc_utility" +//#include "prc_inc_array" +#include "inc_pers_array" // link higher than prc_inc_array + + + +/////////////////////////////////////////////////////////////////////// +/* Private function prototypes - Move on people, nothing to see here */ +/////////////////////////////////////////////////////////////////////// + +/// Internal function. Returns the name matching the given integer constant +string EventTypeIdToName(int nEvent); + +string _GetMarkerLocalName(string sScript, string sArrayName); + +/// Internal function - Array wrapper +int wrap_array_create(object store, string name); +/// Internal function - Array wrapper +int wrap_array_set_string(object store, string name, int i, string entry); +/// Internal function - Array wrapper +string wrap_array_get_string(object store, string name, int i); +/// Internal function - Array wrapper +int wrap_array_shrink(object store, string name, int size_new); +/// Internal function - Array wrapper +int wrap_array_get_size(object store, string name); +/// Internal function - Array wrapper +int wrap_array_exists(object store, string name); + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + + +int AddEventScript(object oObject, int nEvent, string sScript, int bPermanent = FALSE, int bAllowDuplicate = TRUE){ + // If an eventhook is running, place the call into queue + if(GetLocalInt(GetModule(), "prc_eventhook_running")){ + int nQueue = GetLocalInt(GetModule(), "prc_eventhook_pending_queue") + 1; + SetLocalInt(GetModule(), "prc_eventhook_pending_queue", nQueue); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_operation", 1); + SetLocalObject(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_target", oObject); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_event", nEvent); + SetLocalString(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_script", sScript); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_flags", ((!(!bPermanent)) << 1) | (!(!bAllowDuplicate))); + return FALSE; //TODO: What should this really be? + } + + string sArrayName = EventTypeIdToName(nEvent); + + // Abort if the object given / event / script isn't valid + if(!GetIsObjectValid(oObject) || sArrayName == "" || sScript == "") return FALSE; + + + sArrayName += bPermanent ? PERMANENCY_SUFFIX : ""; + + // Create the array if necessary + if(!wrap_array_exists(oObject, sArrayName)){ + wrap_array_create(oObject, sArrayName); + } + + // Check for duplicates if necessary + int bAdd = TRUE; + if(!bAllowDuplicate){ + // Check if a marker is present. + if(GetLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName))) + bAdd = FALSE; + // Since this might be the first time it is looked up, loop through the whole list anyway + else + { + int i, nMax = wrap_array_get_size(oObject, sArrayName); + for(i = 0; i < nMax; i++){ + if(wrap_array_get_string(oObject, sArrayName, i) == sScript){ + // Add a marker that the script is present + SetLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName), TRUE); + bAdd = FALSE; + break; + } } } } + // Add to the array if needed + if(bAdd) + { + wrap_array_set_string(oObject, sArrayName, wrap_array_get_size(oObject, sArrayName), sScript); + // Add a marker that the script is present + SetLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName), TRUE); + } + return bAdd; +} + + +void RemoveEventScript(object oObject, int nEvent, string sScript, int bPermanent = FALSE, int bIgnorePermanency = FALSE){ + // If an eventhook is running, place the call into queue + if(GetLocalInt(GetModule(), "prc_eventhook_running")){ + int nQueue = GetLocalInt(GetModule(), "prc_eventhook_pending_queue") + 1; + SetLocalInt(GetModule(), "prc_eventhook_pending_queue", nQueue); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_operation", 2); + SetLocalObject(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_target", oObject); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_event", nEvent); + SetLocalString(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_script", sScript); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_flags", ((!(!bPermanent)) << 1) | (!(!bIgnorePermanency))); + return; + } + + string sArrayNameBase = EventTypeIdToName(nEvent), + sArrayName; + + // Abort if the object given / event / script isn't valid + if(!GetIsObjectValid(oObject) || sArrayNameBase == "" || sScript == "") return; + + // Go through one-shot array + if(!bPermanent || bIgnorePermanency){ + sArrayName = sArrayNameBase; + // First, check if there is an array to look through at all and that the script is in the array + if(wrap_array_exists(oObject, sArrayName)/* && + GetLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName))*/ + ){ + int nMoveBackBy = 0; + int i = 0; + int nArraySize = wrap_array_get_size(oObject, sArrayName); + // Loop through the array elements + for(; i < nArraySize; i++){ + // See if we have an entry to remove + if(wrap_array_get_string(oObject, sArrayName, i) == sScript){ + nMoveBackBy++; + } + // Move the entries in the array back by an amount great enough to overwrite entries containing sScript + else if(nMoveBackBy){ + wrap_array_set_string(oObject, sArrayName, i - nMoveBackBy, + wrap_array_get_string(oObject, sArrayName, i)); + } } + + // Shrink the array by the number of entries removed, if any + if(nMoveBackBy) + wrap_array_shrink(oObject, sArrayName, wrap_array_get_size(oObject, sArrayName) - nMoveBackBy); + + // Remove the script presence marker + DeleteLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName)); + } } + + // Go through the permanent array + if(bPermanent || bIgnorePermanency){ + sArrayName = sArrayNameBase + PERMANENCY_SUFFIX; + // First, check if there is an array to look through at all and that the script is in the array + if(wrap_array_exists(oObject, sArrayName)/* && + GetLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName))*/ + ){ + int nMoveBackBy = 0; + int i = 0; + int nArraySize = wrap_array_get_size(oObject, sArrayName); + // Loop through the array elements + for(; i < nArraySize; i++){ + // See if we have an entry to remove + if(wrap_array_get_string(oObject, sArrayName, i) == sScript){ + nMoveBackBy++; + } + // Move the entries in the array back by an amount great enough to overwrite entries containing sScript + else if(nMoveBackBy){ + wrap_array_set_string(oObject, sArrayName, i - nMoveBackBy, + wrap_array_get_string(oObject, sArrayName, i)); + } } + + // Shrink the array by the number of entries removed, if any + if(nMoveBackBy) + wrap_array_shrink(oObject, sArrayName, wrap_array_get_size(oObject, sArrayName) - nMoveBackBy); + + // Remove the script presence marker + DeleteLocalInt(oObject, _GetMarkerLocalName(sScript, sArrayName)); + } } +} + + +void ClearEventScriptList(object oObject, int nEvent, int bPermanent = FALSE, int bIgnorePermanency = FALSE){ + // If an eventhook is running, place the call into queue + if(GetLocalInt(GetModule(), "prc_eventhook_running")){ + int nQueue = GetLocalInt(GetModule(), "prc_eventhook_pending_queue") + 1; + SetLocalInt(GetModule(), "prc_eventhook_pending_queue", nQueue); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_operation", 3); + SetLocalObject(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_target", oObject); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_event", nEvent); + SetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(nQueue) + "_flags", ((!(!bPermanent)) << 1) | (!(!bIgnorePermanency))); + return; + } + + string sArrayNameBase = EventTypeIdToName(nEvent), + sArrayName; + + // Abort if the object given / event isn't valid + if(!GetIsObjectValid(oObject) || sArrayNameBase == "") return; + + // Go through one-shot array + if(!bPermanent || bIgnorePermanency){ + sArrayName = sArrayNameBase; + // First, check if there is an array present + if(wrap_array_exists(oObject, sArrayName)){ + // Remove all markers + int i = 0; + for(; i <= wrap_array_get_size(oObject, sArrayName); i++){ + DeleteLocalInt(oObject, _GetMarkerLocalName(wrap_array_get_string(oObject, sArrayName, i), sArrayName)); + } + // Shrink the array to 0 + wrap_array_shrink(oObject, sArrayName, 0); + } } + + // Go through the permanent array + if(bPermanent || bIgnorePermanency){ + sArrayName = sArrayNameBase + PERMANENCY_SUFFIX; + // First, check if there is an array present + if(wrap_array_exists(oObject, sArrayName)){ + // Remove all markers + int i = 0; + for(; i <= wrap_array_get_size(oObject, sArrayName); i++){ + DeleteLocalInt(oObject, _GetMarkerLocalName(wrap_array_get_string(oObject, sArrayName, i), sArrayName)); + } + // Shrink the array to 0 + wrap_array_shrink(oObject, sArrayName, 0); + } } +} + + +string GetFirstEventScript(object oObject, int nEvent, int bPermanent){ + string sArrayName = EventTypeIdToName(nEvent); + + // Abort if the object given / event isn't valid + if(!GetIsObjectValid(oObject) || sArrayName == "") return ""; + + sArrayName += bPermanent ? PERMANENCY_SUFFIX : ""; + + // DelayCommand is somewhat expensive, so do this bit only if there is actually an array to iterate over + if(wrap_array_exists(oObject, sArrayName)) { + SetLocalInt(oObject, sArrayName + "_index", 1); + DelayCommand(0.0f, DeleteLocalInt(oObject, sArrayName + "_index")); + } + + return wrap_array_get_string(oObject, sArrayName, 0); +} + + +string GetNextEventScript(object oObject, int nEvent, int bPermanent){ + string sArrayName = GetLocalInt(GetModule(), "prc_eventhook_running") ? + GetLocalString(GetModule(), "prc_eventhook_running_sArrayName") : + EventTypeIdToName(nEvent); + + // Abort if the object given / event isn't valid + if(!GetIsObjectValid(oObject) || sArrayName == "") return ""; + + sArrayName += bPermanent ? PERMANENCY_SUFFIX : ""; + + int nIndex = GetLocalInt(oObject, sArrayName + "_index"); + if(nIndex) + SetLocalInt(oObject, sArrayName + "_index", nIndex + 1); + else{ + WriteTimestampedLogEntry("GetNextEventScript called without first calling GetFirstEventScript"); + return ""; + } + + return wrap_array_get_string(oObject, sArrayName, nIndex); +} + + +void ExecuteAllScriptsHookedToEvent(object oObject, int nEvent){ + // Mark that an eventhook is being run, so calls to modify the + // scripts listed are delayd until the eventhook is done. + SetLocalInt(GetModule(), "prc_eventhook_running", nEvent); + SetLocalString(GetModule(), "prc_eventhook_running_sArrayName", EventTypeIdToName(nEvent)); + + if(PRINT_EVENTHOOKS && DEBUG) + DoDebug("Executing eventhook for event " + IntToString(nEvent) + "; object = " + DebugObject2Str(oObject) + ". Hooked scripts:"); + + // Loop through the scripts to be fired only once + string sScript = GetFirstEventScript(oObject, nEvent, FALSE); + int bNeedClearing = FALSE; + while(sScript != ""){ + bNeedClearing = TRUE; + + if(PRINT_EVENTHOOKS && DEBUG) + DoDebug("\nOneshot: '" + sScript + "'"); + ExecuteScript(sScript, OBJECT_SELF); + + sScript = GetNextEventScript(oObject, nEvent, FALSE); + } + + // Clear the one-shot script list + if(bNeedClearing) + ClearEventScriptList(oObject, nEvent, FALSE, FALSE); + + // Loop through the persistent scripts + sScript = GetFirstEventScript(oObject, nEvent, TRUE); + while(sScript != ""){ + if(PRINT_EVENTHOOKS && DEBUG) + DoDebug("\nPermanent: '" + sScript + "'"); + ExecuteScript(sScript, OBJECT_SELF); + + sScript = GetNextEventScript(oObject, nEvent, TRUE); + } + + // Remove the lock on modifying the script lists + DeleteLocalInt(GetModule(), "prc_eventhook_running"); + DeleteLocalString(GetModule(), "prc_eventhook_running_sArrayName"); + + // Run the delayed commands + int nQueued = GetLocalInt(GetModule(), "prc_eventhook_pending_queue"), + nOperation, nFlags, i; + object oTarget; + for(i = 1; i <= nQueued; i++){ + nOperation = GetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_operation"); + oTarget = GetLocalObject(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_target"); + nEvent = GetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_event"); + sScript = GetLocalString(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_script"); + nFlags = GetLocalInt(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_flags"); + + switch(nOperation){ + case 1: + AddEventScript(oTarget, nEvent, sScript, nFlags >>> 1, nFlags & 1); + break; + case 2: + RemoveEventScript(oTarget, nEvent, sScript, nFlags >>> 1, nFlags & 1); + break; + case 3: + ClearEventScriptList(oTarget, nEvent, nFlags >>> 1, nFlags & 1); + break; + + default: + WriteTimestampedLogEntry("Invalid value in delayed eventhook manipulation operation queue"); + } + + DeleteLocalInt (GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_operation"); + DeleteLocalObject(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_target"); + DeleteLocalInt (GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_event"); + DeleteLocalString(GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_script"); + DeleteLocalInt (GetModule(), "prc_eventhook_pending_queue_" + IntToString(i) + "_flags"); + } + + DeleteLocalInt(GetModule(), "prc_eventhook_pending_queue"); + +} + + +int GetRunningEvent(){ + return GetLocalInt(GetModule(), "prc_eventhook_running"); +} + + +string EventTypeIdToName(int nEvent){ + /* + switch(nEvent){ + // Module events + case EVENT_ONACQUIREITEM: + return NAME_ONACQUIREITEM; + case EVENT_ONACTIVATEITEM: + return NAME_ONACTIVATEITEM; + case EVENT_ONCLIENTENTER: + return NAME_ONCLIENTENTER; + case EVENT_ONCLIENTLEAVE: + return NAME_ONCLIENTLEAVE; + case EVENT_ONCUTSCENEABORT: + return NAME_ONCUTSCENEABORT; + case EVENT_ONHEARTBEAT: + return NAME_ONHEARTBEAT; +// case EVENT_ONMODULELOAD: +// return NAME_ONMODULELOAD; + case EVENT_ONPLAYERDEATH: + return NAME_ONPLAYERDEATH; + case EVENT_ONPLAYERDYING: + return NAME_ONPLAYERDYING; + case EVENT_ONPLAYEREQUIPITEM: + return NAME_ONPLAYEREQUIPITEM; + case EVENT_ONPLAYERLEVELUP: + return NAME_ONPLAYERLEVELUP; + case EVENT_ONPLAYERREST_CANCELLED: + return NAME_ONPLAYERREST_CANCELLED; + case EVENT_ONPLAYERREST_STARTED: + return NAME_ONPLAYERREST_STARTED; + case EVENT_ONPLAYERREST_FINISHED: + return NAME_ONPLAYERREST_FINISHED; + case EVENT_ONPLAYERUNEQUIPITEM: + return NAME_ONPLAYERUNEQUIPITEM; + case EVENT_ONPLAYERRESPAWN: + return NAME_ONPLAYERRESPAWN; + case EVENT_ONUNAQUIREITEM: + return NAME_ONUNAQUIREITEM; + case EVENT_ONUSERDEFINED: + return NAME_ONUSERDEFINED; + case EVENT_ONPLAYERLEVELDOWN: + return NAME_ONPLAYERLEVELDOWN; + + // NPC events + case EVENT_NPC_ONBLOCKED: + return NAME_NPC_ONBLOCKED; + case EVENT_NPC_ONCOMBATROUNDEND: + return NAME_NPC_ONCOMBATROUNDEND; + case EVENT_NPC_ONCONVERSATION: + return NAME_NPC_ONCONVERSATION; + case EVENT_NPC_ONDAMAGED: + return NAME_NPC_ONDAMAGED; + case EVENT_NPC_ONDEATH: + return NAME_NPC_ONDEATH; + case EVENT_NPC_ONDISTURBED: + return NAME_NPC_ONDISTURBED; + case EVENT_NPC_ONHEARTBEAT: + return NAME_NPC_ONHEARTBEAT; + case EVENT_NPC_ONPERCEPTION: + return NAME_NPC_ONPERCEPTION; + case EVENT_NPC_ONPHYSICALATTACKED: + return NAME_NPC_ONPHYSICALATTACKED; + case EVENT_NPC_ONRESTED: + return NAME_NPC_ONRESTED; +// case EVENT_NPC_ONSPAWN: +// return NAME_NPC_ONSPAWN; + case EVENT_NPC_ONSPELLCASTAT: + return NAME_NPC_ONSPELLCASTAT; + + // Other events + case EVENT_ONHIT: + return NAME_ONHIT; + case EVENT_ONSPELLCAST: + return NAME_ONSPELLCAST; + case EVENT_ONPOWERMANIFEST: + return NAME_ONPOWERMANIFEST; + + // Item events + case EVENT_ITEM_ONACQUIREITEM: + return NAME_ITEM_ONACQUIREITEM; + case EVENT_ITEM_ONACTIVATEITEM: + return NAME_ITEM_ONACTIVATEITEM; + case EVENT_ITEM_ONPLAYEREQUIPITEM: + return NAME_ITEM_ONPLAYEREQUIPITEM; + case EVENT_ITEM_ONPLAYERUNEQUIPITEM: + return NAME_ITEM_ONPLAYERUNEQUIPITEM; + case EVENT_ITEM_ONUNAQUIREITEM: + return NAME_ITEM_ONUNAQUIREITEM; + case EVENT_ITEM_ONHIT: + return NAME_ITEM_ONHIT; + + // Callbackhooks + case CALLBACKHOOK_UNARMED: + return NAME_CALLBACKHOOK_UNARMED; + + default: + WriteTimestampedLogEntry("Unknown event id passed to EventTypeIdToName: " + IntToString(nEvent) + "\nAdding a name constant for it recommended."); + return "prc_event_array_" + IntToString(nEvent); + } + */ + + return "prc_event_array_" + IntToString(nEvent); + + //return ""; // Never going to reach this, but the compiler doesn't realize that :P +} + +string _GetMarkerLocalName(string sScript, string sArrayName) +{ + return "prc_eventhook_script:" + sScript + ";array:" + sArrayName; +} + + +int wrap_array_create(object store, string name){ + if(GetIsPC(store)) + return persistant_array_create(store, name); + else + return array_create(store, name); +} +int wrap_array_set_string(object store, string name, int i, string entry){ + if(GetIsPC(store)) + return persistant_array_set_string(store, name, i, entry); + else + return array_set_string(store, name, i, entry); +} +string wrap_array_get_string(object store, string name, int i){ + if(GetIsPC(store)) + return persistant_array_get_string(store, name, i); + else + return array_get_string(store, name, i); +} +int wrap_array_shrink(object store, string name, int size_new){ + if(GetIsPC(store)) + return persistant_array_shrink(store, name, size_new); + else + return array_shrink(store, name, size_new); +} +int wrap_array_get_size(object store, string name){ + if(GetIsPC(store)) + return persistant_array_get_size(store, name); + else + return array_get_size(store, name); +} +int wrap_array_exists(object store, string name){ + if(GetIsPC(store)) + return persistant_array_exists(store, name); + else + return array_exists(store, name); +} diff --git a/src/include/inc_heap.nss b/src/include/inc_heap.nss new file mode 100644 index 0000000..6127072 --- /dev/null +++ b/src/include/inc_heap.nss @@ -0,0 +1,592 @@ +//:://///////////////////////////////////////////// +//:: Heap include +//:: inc_heap +//::////////////////////////////////////////////// +/** @file + A simple maxheap, backed by an array. + Insertion priority is determined by an interger + parameter, data stored may be anything. + + Heap element indices begin at one, for convenience. + + For optimization, I use binary search instead of + switches. Result: It's fugly + + Return values are similar to the ones in + Mr. Figglesworth's sdl_array + + @author Ornedan + @date Created - 16.03.2005 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// +/* +const int SDL_SUCCESS = 1; +const int SDL_ERROR_ALREADY_EXISTS = 1001; +const int SDL_ERROR_DOES_NOT_EXIST = 1002; +const int SDL_ERROR_OUT_OF_BOUNDS = 1003; +const int SDL_ERROR_NO_ZERO_SIZE = 1004; +const int SDL_ERROR_NOT_VALID_OBJECT = 1005; +*/ + +/// Heap entity type - float +const int ENTITY_TYPE_FLOAT = 1; +/// Heap entity type - integer +const int ENTITY_TYPE_INTEGER = 2; +/// Heap entity type - object +const int ENTITY_TYPE_OBJECT = 3; +/// Heap entity type - string +const int ENTITY_TYPE_STRING = 4; + +// Internal constants +const string HEAP_PREFIX = "heap_"; +const string KEY_SUFFIX = "_key"; +const string ELEMENT_SUFFIX = "_element"; +const string TYPE_SUFFIX = "_type"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Initializes heap variables on the storage object. + * + * @param oStore object that the heap will be stored as locals on + * @param sName the name of the heap + * @return SDL_* constant + */ +int heap_create(object oStore, string sName); + +/** + * Deletes the heap and all it's entries. + * + * @param oStore object the heap is stored on + * @param sName the name of the heap + * @return SDL_* constant + */ +int heap_delete(object oStore, string sName); + +/** + * Checks to see if a heap exists. + * + * @param oStore object the heap is stored on + * @param sName the name of the heap + * @return TRUE if a heap with the given name is stored on oStore. + * FALSE otherwise. + */ +int heap_exists(object oStore, string sName); + +/** + * Gets the number of elements in the heap + * + * @param oStore object the heap is stored on + * @param sName the name of the heap + * @return the number of elements in the heap, or -1 on error. + */ +int heap_get_size(object oStore, string sName); + + +/** + * Heap insertion functions - float. + * Inserts the given key & element pair at a location in the heap + * determined by the key. + * Return order of elements inserted with the same key is not defined. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @param nKey integer value used to determine insertion location + * @param fEntry element to be insterted + * @return SDL_* constant + */ +int heap_put_float(object oStore, string sName, int nKey, float fEntry); + +/** + * Heap insertion functions - integer. + * Inserts the given key & element pair at a location in the heap + * determined by the key. + * Return order of elements inserted with the same key is not defined. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @param nKey integer value used to determine insertion location + * @param nEntry element to be insterted + * @return SDL_* constant + */ +int heap_put_int(object oStore, string sName, int nKey, int nEntry); + +/** + * Heap insertion functions - object. + * Inserts the given key & element pair at a location in the heap + * determined by the key. + * Return order of elements inserted with the same key is not defined. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @param nKey integer value used to determine insertion location + * @param oEntry element to be insterted + * @return SDL_* constant + */ +int heap_put_object(object oStore, string sName, int nKey, object oEntry); + +/** + * Heap insertion functions - string +. + * Inserts the given key & element pair at a location in the heap + * determined by the key. + * Return order of elements inserted with the same key is not defined. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @param nKey integer value used to determine insertion location + * @param sEntry element to be insterted + * @return SDL_* constant + */ +int heap_put_string(object oStore, string sName, int nKey, string sEntry); + + +/** + * Checks the type of the element at the top of the heap. Errors if + * heap does not exist or is empty. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @return one of the ENTITY_TYPE_* constants, or 0 on error. + */ +int heap_get_type(object oStore, string sName); + +/** + * Gets the top element of the heap as float. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @return top element of the heap as float. If the type + * of the top element was not float, returns 0.0f + */ +float heap_get_float(object oStore, string sName); + +/** + * Gets the top element of the heap as integer. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @return top element of the heap as integer. If the type + * of the top element was not integer, returns 0 + */ +int heap_get_int(object oStore, string sName); + +/** + * Gets the top element of the heap as object. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @return top element of the heap as object. If the type + * of the top element was not object, returns OBJECT_INVALID + */ +object heap_get_object(object oStore, string sName); + +/** + * Gets the top element of the heap as string. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @return top element of the heap as string. If the type + * of the top element was not string, returns "" + */ +string heap_get_string(object oStore, string sName); + +/** + * Deletes the top element of the heap and reorders the heap to + * preserve the heap conditions. + * + * @param oStore object the heap to be used is stored on + * @param sName the name of the heap + * @return one of the SDL_* constants + */ +int heap_remove(object oStore, string sName); + + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "inc_utility" +#include "prc_inc_array" //The only part of inc_utility it needs + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + + + +int heap_create(object oStore, string sName){ + // Validity checks + if(!GetIsObjectValid(oStore)) + return SDL_ERROR_NOT_VALID_OBJECT; + if(GetLocalInt(oStore, sName)) + return SDL_ERROR_ALREADY_EXISTS; + + // Initialize the size (always one greater than the actual size) + SetLocalInt(oStore, HEAP_PREFIX + sName, 1); + return SDL_SUCCESS; +} + + +int heap_delete(object oStore, string sName){ + // Validity checks + int nSize = GetLocalInt(oStore, HEAP_PREFIX + sName); + if(!nSize) + return SDL_ERROR_DOES_NOT_EXIST; + + nSize -= 1; + int nTempType; + for(; nSize >= 0; nSize--){ + // Delete the storage values + nTempType = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + TYPE_SUFFIX); + DeleteLocalInt (oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + TYPE_SUFFIX); + DeleteLocalInt (oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + KEY_SUFFIX); + + if(nTempType > ENTITY_TYPE_INTEGER){ + if(nTempType > ENTITY_TYPE_OBJECT) + DeleteLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + else + DeleteLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + }else{ + if(nTempType > ENTITY_TYPE_FLOAT) + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + else + DeleteLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + } + } + + // Delete the size variable + DeleteLocalInt(oStore, HEAP_PREFIX + sName); + return SDL_SUCCESS; +} + + +int heap_exists(object oStore, string sName){ + if(GetLocalInt(oStore, HEAP_PREFIX + sName)) + return TRUE; + else + return FALSE; +} + + +int heap_get_size(object oStore, string sName){ + return GetLocalInt(oStore, HEAP_PREFIX + sName) - 1; +} + + +/* Some functions for simulating the element links */ +int heap_parent(int nIndex){ return (nIndex - 1) / 2; } +int heap_lchild(int nIndex){ return (nIndex * 2) + 1; } +int heap_rchild(int nIndex){ return (nIndex * 2) + 2; } +/* An element swapper */ +void heap_swap(object oStore, string sName, int nInd1, int nInd2){ + int nTempKey = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + KEY_SUFFIX); + int nTempType = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + TYPE_SUFFIX); + float fTemp; + int nTemp; + object oTemp; + string sTemp; + + // Grab the element from index1 + if(nTempType > ENTITY_TYPE_INTEGER){ + if(nTempType > ENTITY_TYPE_OBJECT){ + sTemp = GetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + DeleteLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + }else{ + oTemp = GetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + DeleteLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + }}else{ + if(nTempType > ENTITY_TYPE_FLOAT){ + nTemp = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + }else{ + fTemp = GetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + DeleteLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX); + }} + + // Start moving from index2 + int nTempType2 = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + TYPE_SUFFIX); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + TYPE_SUFFIX, + nTempType2); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + KEY_SUFFIX, + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + KEY_SUFFIX)); + // Illegal use of enumerations. Don't do this at home :p + if(nTempType2 > ENTITY_TYPE_INTEGER){ + if(nTempType2 > ENTITY_TYPE_OBJECT){ + SetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX, + GetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX)); + DeleteLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX); + }else{ + SetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX, + GetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX)); + DeleteLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX); + }}else{ + if(nTempType2 > ENTITY_TYPE_FLOAT){ + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX, + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX)); + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX); + }else{ + SetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd1) + ELEMENT_SUFFIX, + GetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX)); + DeleteLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX); + }} + + // Place the stuff copied to temporary variables to their new place + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + TYPE_SUFFIX, + nTempType); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + KEY_SUFFIX, + nTempKey); + if(nTempType > ENTITY_TYPE_INTEGER){ + if(nTempType > ENTITY_TYPE_OBJECT) + SetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX, + sTemp); + else + SetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX, + oTemp); + }else{ + if(nTempType > ENTITY_TYPE_FLOAT) + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX, + nTemp); + else + SetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInd2) + ELEMENT_SUFFIX, + fTemp); + } +} + +/* A function that gets the location where the given + * key should be inserted. Moves other elements around + * to clear the location + */ +int heap_get_insert_location(object oStore, string sName, int nKey){ + // Insert into position just beyond the end of current elements + int nIndex = heap_get_size(oStore, sName); + int nTempType; + while(nIndex > 0 && GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + KEY_SUFFIX) < nKey){ + // Move the parent entry down + nTempType = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + TYPE_SUFFIX); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + TYPE_SUFFIX, + nTempType); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + KEY_SUFFIX, + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + KEY_SUFFIX)); + // Illegal use of enumerations. Don't do this at home :p + // The old entry is deleted, since the entry to be inserted might not be of the same type + if(nTempType > ENTITY_TYPE_INTEGER){ + if(nTempType > ENTITY_TYPE_OBJECT){ + SetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + ELEMENT_SUFFIX, + GetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX)); + DeleteLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX); + }else{ + SetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + ELEMENT_SUFFIX, + GetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX)); + DeleteLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX); + }}else{ + if(nTempType > ENTITY_TYPE_FLOAT){ + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + ELEMENT_SUFFIX, + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX)); + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX); + }else{ + SetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + ELEMENT_SUFFIX, + GetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX)); + DeleteLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(heap_parent(nIndex)) + ELEMENT_SUFFIX); + }} + + nIndex = heap_parent(nIndex); + } + + return nIndex; +} +/*if(a > 2){ + if(a > 3) + b = ENTITY_TYPE_STRING; + else + b = ENTITY_TYPE_OBJECT; +}else{ + if(a > 1) + b = ENTITY_TYPE_INTEGER; + else + b = ENTITY_TYPE_FLOAT; +}*/ + +int heap_put_float(object oStore, string sName, int nKey, float fEntry){ + // Validity checks + if(!GetLocalInt(oStore, HEAP_PREFIX + sName)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Get the location to insert to + int nInsert = heap_get_insert_location(oStore, sName, nKey); + + // Insert the new element + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + TYPE_SUFFIX, ENTITY_TYPE_FLOAT); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + KEY_SUFFIX, nKey); + SetLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + ELEMENT_SUFFIX, fEntry); + + // Mark the insertion + SetLocalInt(oStore, HEAP_PREFIX + sName, GetLocalInt(oStore, HEAP_PREFIX + sName) + 1); + return SDL_SUCCESS; +} + +int heap_put_int(object oStore, string sName, int nKey, int nEntry){ + // Validity checks + if(!GetLocalInt(oStore, HEAP_PREFIX + sName)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Get the location to insert to + int nInsert = heap_get_insert_location(oStore, sName, nKey); + + // Insert the new element + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + KEY_SUFFIX, nKey); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + TYPE_SUFFIX, ENTITY_TYPE_INTEGER); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + ELEMENT_SUFFIX, nEntry); + + // Mark the insertion + SetLocalInt(oStore, HEAP_PREFIX + sName, GetLocalInt(oStore, HEAP_PREFIX + sName) + 1); + return SDL_SUCCESS; +} + +int heap_put_string(object oStore, string sName, int nKey, string sEntry){ + // Validity checks + if(!GetLocalInt(oStore, HEAP_PREFIX + sName)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Get the location to insert to + int nInsert = heap_get_insert_location(oStore, sName, nKey); + + // Insert the new element + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + KEY_SUFFIX, nKey); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + TYPE_SUFFIX, ENTITY_TYPE_STRING); + SetLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + ELEMENT_SUFFIX, sEntry); + + // Mark the insertion + SetLocalInt(oStore, HEAP_PREFIX + sName, GetLocalInt(oStore, HEAP_PREFIX + sName) + 1); + return SDL_SUCCESS; +} + +int heap_put_object(object oStore, string sName, int nKey, object oEntry){ + // Validity checks + if(!GetLocalInt(oStore, HEAP_PREFIX + sName)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Get the location to insert to + int nInsert = heap_get_insert_location(oStore, sName, nKey); + + // Insert the new element + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + KEY_SUFFIX, nKey); + SetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + TYPE_SUFFIX, ENTITY_TYPE_OBJECT); + SetLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nInsert) + ELEMENT_SUFFIX, oEntry); + + // Mark the insertion + SetLocalInt(oStore, HEAP_PREFIX + sName, GetLocalInt(oStore, HEAP_PREFIX + sName) + 1); + return SDL_SUCCESS; +} + + +int heap_remove(object oStore, string sName){ + // Validity checks + if(!GetLocalInt(oStore, HEAP_PREFIX + sName)) + return SDL_ERROR_DOES_NOT_EXIST; + + int nSize = heap_get_size(oStore, sName); + if(!nSize) + return SDL_ERROR_OUT_OF_BOUNDS; + + // Move the bottommost element over the max + nSize--; + heap_swap(oStore, sName, 0, nSize); + // Delete the bottommost element + int nTempType = GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + TYPE_SUFFIX); + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + TYPE_SUFFIX); + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + KEY_SUFFIX); + + if(nTempType > ENTITY_TYPE_INTEGER){ + if(nTempType > ENTITY_TYPE_OBJECT) + DeleteLocalString(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + else + DeleteLocalObject(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + }else{ + if(nTempType > ENTITY_TYPE_FLOAT) + DeleteLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + else + DeleteLocalFloat(oStore, HEAP_PREFIX + sName + "_" + IntToString(nSize) + ELEMENT_SUFFIX); + } + // Mark the heapsize as reduced + SetLocalInt(oStore, HEAP_PREFIX + sName, nSize + 1); + // Move nSize to point at the new last entry + nSize--; + // Re-assert the heap conditions + int nLeft, nRight, nMax, nIndex = 0; + int bContinue = TRUE; + while(bContinue){ + bContinue = FALSE; + nLeft = heap_lchild(nIndex); + nRight = heap_rchild(nIndex); + + if(nRight <= nSize){ + if(GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nLeft) + KEY_SUFFIX) + > + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nLeft) + KEY_SUFFIX)) + nMax = nLeft; + else + nMax = nRight; + + if(GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + KEY_SUFFIX) + < + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nMax) + KEY_SUFFIX)){ + heap_swap(oStore, sName, nIndex, nMax); + bContinue = TRUE; + } + } + else if(nLeft == nSize && + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nIndex) + KEY_SUFFIX) + < + GetLocalInt(oStore, HEAP_PREFIX + sName + "_" + IntToString(nLeft) + KEY_SUFFIX)) + heap_swap(oStore, sName, nIndex, nLeft); + } + + return SDL_SUCCESS; +} + + +int heap_get_type(object oStore, string sName){ + // Validity checks + if(!GetLocalInt(oStore, HEAP_PREFIX + sName)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Return the heap top element's type + return GetLocalInt(oStore, HEAP_PREFIX + sName + "_0" + TYPE_SUFFIX); +} + + +float heap_get_float(object oStore, string sName){ + return GetLocalInt(oStore, HEAP_PREFIX + sName + "_0" + TYPE_SUFFIX) == ENTITY_TYPE_FLOAT ? + GetLocalFloat(oStore, HEAP_PREFIX + sName + "_0" + ELEMENT_SUFFIX) : + 0.0f; +} + +int heap_get_int(object oStore, string sName){ + return GetLocalInt(oStore, HEAP_PREFIX + sName + "_0" + TYPE_SUFFIX) == ENTITY_TYPE_INTEGER ? + GetLocalInt(oStore, HEAP_PREFIX + sName + "_0" + ELEMENT_SUFFIX) : + 0; +} + +object heap_get_object(object oStore, string sName){ + return GetLocalInt(oStore, HEAP_PREFIX + sName + "_0" + TYPE_SUFFIX) == ENTITY_TYPE_OBJECT ? + GetLocalObject(oStore, HEAP_PREFIX + sName + "_0" + ELEMENT_SUFFIX) : + OBJECT_INVALID; +} + +string heap_get_string(object oStore, string sName){ + return GetLocalInt(oStore, HEAP_PREFIX + sName + "_0" + TYPE_SUFFIX) == ENTITY_TYPE_STRING ? + GetLocalString(oStore, HEAP_PREFIX + sName + "_0" + ELEMENT_SUFFIX) : + ""; +} + + +//void main(){} diff --git a/src/include/inc_item_props.nss b/src/include/inc_item_props.nss new file mode 100644 index 0000000..b1edfc2 --- /dev/null +++ b/src/include/inc_item_props.nss @@ -0,0 +1,1961 @@ +//:://///////////////////////////////////////////// +//:: [Item Property Function] +//:: [inc_item_props.nss] +//::////////////////////////////////////////////// +/** @file + This file defines several functions used to + manipulate item properties. In particular, + It defines functions used in the prc_* files + to apply passive feat bonuses. + + Take special note of SetCompositeBonus. This + function is crucial for allowing bonuses of the + same type from different PRCs to stack. +*/ +//::////////////////////////////////////////////// +//:: Created By: Aaon Graywolf +//:: Created On: Dec 19, 2003 +//::////////////////////////////////////////////// +//:: Update: Jan 4 2002 +//:: - Extended Composite bonus function to handle pretty much +//:: every property that can possibly be composited. + +////////////////////////////// +// Function Prototypes // +////////////////////////////// + +#include "prc_feat_const" +#include "prc_misc_const" + +//:: Test void +//:: void main (){} + +/** + * Checks oItem for all properties matching iType and iSubType. Removes all + * these properties and returns their total CostTableVal. + * This function should only be used for Item Properties that have simple + * integer CostTableVals, such as AC, Save/Skill Bonuses, etc. + * + * @param oItem The item on which to look for the itemproperties. Usually a + * skin object. + * @param iType ITEM_PROPERTY_* constant of the itemproperty to look for. + * @param iSubType IP_CONST_* constant of itemproperty SubType if applicable. + * @return The total CostTableValue of the itemproperties found + * matching iType and iSubType. + */ +int TotalAndRemoveProperty(object oItem, int iType, int iSubType = -1); + +/** + * Used to roll bonuses from multiple sources into a single property. + * Only supports properties which have simple integer CostTableVals. + * See the code for a list of supported types. Some important properties + * that CANNOT be composited are SpellResistance, DamageBonus, DamageReduction + * DamageResistance and MassiveCritical, as these use 2da-referencing constants + * instead of integers for CostTableVals. + * + * + * LocalInts from SetCompositeBonus() when applied to the skin need to be + * added to DeletePRCLocalInts() in prc_inc_function in order for the system to + * properly clear the properties when the itemproperties are removed using + * ScrubPCSkin(). + * When applied to equipment, the LocalInts need to be added to + * DeletePRCLocalIntsT() in inc_item_props. + * + * + * Use SetCompositeBonus() for the skin, SetCompositeBonusT() for other equipment. + * + * + * @param oPC Object wearing / using the item + * @param oItem Object to apply bonus to + * @param sBonus String name of the source for this bonus + * @param iVal Integer value to set this bonus to + * @param iType ITEM_PROPERTY_* constant of itemproperty to apply + * @param iSubType IP_CONST_* constant of itemproperty SubType if applicable + */ +void SetCompositeBonus(object oItem, string sBonus, int iVal, int iType, int iSubType = -1); + +/** + * See SetCompositeBonus(). This is an equivalent, except it applies the itemproperties as + * temporary ones with duration of 9999 seconds. + */ +void SetCompositeBonusT(object oItem, string sBonus, int iVal, int iType, int iSubType = -1); + +/** + * Calculates the total Bonus AC granted by itemproperties on an item. + * + * @param oItem The item to calculate AC bonus given by. + * @return The total of all AC bonus itemproperties on oItem. + */ +int GetACBonus(object oItem); + +/** + * Calculates the Base AC (i.e. AC without bonuses) of an item + * + * @param oItem The item to calculate base AC for. + * @return The base AC, as calculated by removing the value returned by + * GetACBonus() on the item from the value returned by GetItemACValue(). + */ +int GetBaseAC(object oItem); + +/** + * Gets the itemproperty number of a specific SR value + */ +int GetSRByValue(int nValue); + +/** + * Returns the opposite element from iElem or -1 if iElem is not valid + * Can be useful for determining elemental strengths and weaknesses + * + * @param iElem IP_CONST_DAMAGETYPE_* constant. + * @return IP_CONST_DAMAGETYPE_* constant of the opposing damage type. + */ +int GetOppositeElement(int iElem); + +/** + * Used to find the damage type done by any given weapon using 2da lookups. + * Only usable on weapon items. + * + * @param oWeapon The weapon whose damage type to examine. + * @return One of IP_CONST_DAMAGETYPE_BLUDGEONING, + * IP_CONST_DAMAGETYPE_PIERCING, + * IP_CONST_DAMAGETYPE_SLASHING + * if used on a weapon item. Otherwise -1. + */ +int GetItemPropertyDamageType(object oWeapon); + +/** + * Used to find the damage type done by any given weapon using 2da lookups. + * Only usable on weapon items. + * + * @param oWeapon The weapon whose damage type to examine. + * @return One of DAMAGE_TYPE_BLUDGEONING, + * DAMAGE_TYPE_PIERCING, + * DAMAGE_TYPE_SLASHING + * if used on a weapon item. Otherwise -1. + */ +int GetItemDamageType(object oWeapon); + +/** + * To ensure a damage bonus stacks with any existing enhancement bonus, + * create a temporary damage bonus on the weapon. You do not want to do this + * if the weapon is of the "slashing and piercing" type, because the + * enhancement bonus is considered "physical", not "slashing" or "piercing". + * + * Because of this strange Bioware behavior, you'll want to only call this code as such: + * + * if (StringToInt(Get2DACache("baseitems","WeaponType",GetBaseItemType(oWeapon))) != 4) + * { + * IPEnhancementBonusToDamageBonus(oWeapon); + * } + * + * + * @param oWeap The weapon to perform the operation on. + */ +void IPEnhancementBonusToDamageBonus(object oWeap); + +/** + * Used to roll bonuses from multiple sources into a single property + * Only supports damage bonuses in a linear fashion - +1 through +20. + * + * Note: If you do not define iSubType, the damage applied will most likely not + * stack with any enhancement bonus. See IPEnhancementBonusToDamageBonus() above. + * + * + * LocalInts from SetCompositeDamageBonus() need to be added to + * DeletePRCLocalInts() in prc_inc_function. + * LocalInts from SetCompositeDamageBonusT() need to be added to + * DeletePRCLocalIntsT() in inc_item_props. + * + * + * + * @param oItem Object to apply bonus to + * @param sBonus String name of the source for this bonus + * @param iVal Integer value to set this bonus to (damage +1 through +20) + * @param iSubType IP_CONST_DAMAGETYPE* constant -- leave blank to use the weapon's damage type. + */ +void SetCompositeDamageBonusT(object oItem, string sBonus, int iVal, int iSubType = -1); // for temporary bonuses + +/** + * Removes a number of itemproperties matching the parameters. + * + * @param oItem The item to remove itemproperties from. + * @param iType ITEM_PROPERTY_* constant. + * @param iSubType IP_CONST_* constant of the itemproperty subtype or -1 to + * match all possible subtypes. Also use -1 if the itemproperty + * has no subtypes. + * @param iCostVal CostTableValue of the itemproperty to remove. Again, -1 for + * any. + * @param iNum How many matching itemproperties to remove. -1 for all. Defaults + * to 1. + * @param sFlag Name of a local integer on the item to set to 0 when this is run. + * If anyone knows why the fuck this is done, please write here - Ornedan + * @param iParam1 Param1 value of the itemproperty to remove. Again, -1 for any. + * @param iDuration DURATION_TYPE_* constant. The duration type of the itemproperty. + * Again, -1 for any. + */ +void RemoveSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1, int iNum = 1, + string sFlag = "", int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT); + +/** + * Finds the first itemproperty matching the parameters. + * Use GetIsItemPropertyValid() to check if an itemproperty exists. + * + * @param oItem The item to remove itemproperties from. + * @param iType ITEM_PROPERTY_* constant. + * @param iSubType IP_CONST_* constant of the itemproperty subtype or -1 to + * match all possible subtypes. Also use -1 if the itemproperty + * has no subtypes. + * @param iCostVal CostTableValue of the itemproperty to remove. Again, -1 for + * any. + * @param iParam1 Param1 value of the itemproperty to remove. Again, -1 for any. + * @param iDuration DURATION_TYPE_* constant. The duration type of the itemproperty. + * Again, -1 for any. + */ +itemproperty GetSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1, + int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT); + +/** + * Keeps track of Attack Bonus effects and stacks them appropriately... you cannot set up + * "special" attack bonuses against races or alignments, but it will keep seperate tabs on + * on-hand attack bonuses and off-hand attack bonuses. + * + * NOTE: This attack bonus is an effect on the creature, not an item property. Item Property + * attacks have the downside that they pierce DR, whereas effects do not. + * + * NOTE: DO *NOT* USE THIS FUNCTION WITH SPELL/SLA EFFECTS. They stack fine on their own. + * + * + * LocalInts in and finally SetCompositeAttackBonus() need to be added to + * DeletePRCLocalInts() in prc_inc_function. + * + * + * + * @param oPC PC/NPC you wish to apply an attack bonus effect to + * @param sBonus The unique name you wish to give this attack bonus + * @param iVal The amount the attack bonus should be (there is a hardcoded limit of 20) + * @param iSubType ATTACK_BONUS_MISC applies to both hands, ATTACK_BONUS_ONHAND applies to the right (main) + * hand, and ATTACK_BONUS_OFFHAND applies to the left (off) hand + */ +void SetCompositeAttackBonus(object oPC, string sBonus, int iVal, int iSubType = ATTACK_BONUS_MISC); + +/** + * Internal function. + * Handles maintaining a list of composite itemproperty names for + * use when clearing the itemproperties away. + * + * @param oItem The item a composite bonus is being set on. + * @param sBase The base name for the local variables used. For differentiating between + * permanent and temporary lists. + * @param sComposite The name of a composite property being set. + */ +void UpdateUsedCompositeNamesList(object oItem, string sBase, string sComposite); + +/** + * Internal function. + * Deletes all the composite itemproperty names listed and deletes the list. + * + * @param oItem The item being cleaned of composite properties. + * @param sBase The base name for the local variables used. For differentiating between + * permanent and temporary lists. + */ +void DeleteNamedComposites(object oItem, string sBase); + +/** + * Determines if any of the given item's itemproperties would make it a + * magical item. + * + * @param oItem The item to test + * @return TRUE if the item is a magical item, FALSE otherwise + */ +int GetIsMagicItem(object oItem); + +/** + * Special function for adding ip feats to creatures hide. + * The hide is always equipped so it easier to use GetHasFeat() + * than to loop over all ips to check if given skin was already + * added. + * @param nFeat The ID of real feat we want to add (row number in feat.2da) + * @param IPFeat The ID of ip feat (row number in iprp_feats.2da) + * @param oSkin Target item to which we add the feat - should be an equipped + * creature skin (the script doesn't check it it's valid) + * @param oPC Owner of oSkin + */ +void AddSkinFeat(int nFeat, int IPFeat, object oSkin, object oPC = OBJECT_SELF, float fDuration = 0.0f); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "inc_2dacache" +#include "inc_persist_loca" +#include "inc_prc_npc" +//#include "inc_utility" +#include "prc_ipfeat_const" +#include "prc_inc_array" + +////////////////////////////// +// Function Definitions // +////////////////////////////// + +int TotalAndRemoveProperty(object oItem, int iType, int iSubType = -1) +{ + itemproperty ip = GetFirstItemProperty(oItem); + int total = 0; + + while(GetIsItemPropertyValid(ip)){ + if(GetItemPropertyType(ip) == iType && (GetItemPropertySubType(ip) == iSubType || iSubType == -1)){ + total += GetItemPropertyCostTableValue(ip); + RemoveItemProperty(oItem, ip); + } + ip = GetNextItemProperty(oItem); + } + return total; +} + +itemproperty GetSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1, + int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT) +{ + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)){ + if(GetItemPropertyType(ip) == iType && + (GetItemPropertyDurationType(ip) == iDuration || iDuration == -1) && + (GetItemPropertySubType(ip) == iSubType || iSubType == -1) && + (GetItemPropertyCostTableValue(ip) == iCostVal || iCostVal == -1) && + (GetItemPropertyParam1Value(ip) == iParam1 || iParam1 == -1) + ) + { + return ip; + } + ip = GetNextItemProperty(oItem); + } + return ip; +} + +// Removes one or more item properties from an item that match the specified criteria. +// +// Parameters: +// oItem - The item object from which the property or properties will be removed. +// iType - The item property type (e.g., ITEM_PROPERTY_ENHANCEMENT_BONUS). +// iSubType - Optional. The property subtype to match. Use -1 to match any subtype. +// iCostVal - Optional. The cost table value to match. Use -1 to match any value. +// iNum - Optional. The number of matching properties to remove. Use -1 to remove all matches. Default is 1. +// sFlag - Optional. A local string/int flag name to clear on the item after removal. Default is "" (does nothing). +// iParam1 - Optional. Additional property-specific parameter to match (e.g., for saving throw bonuses). Use -1 to ignore. +// iDuration - Optional. Duration type of the property to match (e.g., DURATION_TYPE_PERMANENT). Use -1 to match any. +// +// Notes: +// - This function loops through item properties and removes the first `iNum` that match the given filters. +// - If iNum is -1, it removes all matching properties. +// - After removal, if sFlag is not an empty string, the local int with name sFlag is cleared on the item. + +void RemoveSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1, int iNum = 1, + string sFlag = "", int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT) +{ + int iRemoved = 0; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip) && (iRemoved < iNum || iNum == -1)){ + if(GetItemPropertyType(ip) == iType && + (GetItemPropertyDurationType(ip) == iDuration || iDuration == -1) && + (GetItemPropertySubType(ip) == iSubType || iSubType == -1) && + (GetItemPropertyCostTableValue(ip) == iCostVal || iCostVal == -1) && + (GetItemPropertyParam1Value(ip) == iParam1 || iParam1 == -1) + ) + { + RemoveItemProperty(oItem, ip); + iRemoved++; + } + ip = GetNextItemProperty(oItem); + } + SetLocalInt(oItem, sFlag, 0); +} + +//SetCompositeBonus is a special case that doesn't need to use DelayAddItemProperty because +//the property that is added is always different from the one that was deleted by TotalAndRemoveProperty. +void SetCompositeBonus(object oItem, string sBonus, int iVal, int iType, int iSubType = -1) +{ + int iOldVal = GetLocalInt(oItem, sBonus); + int iChange = iVal - iOldVal; + int iCurVal = 0; + + if(iChange == 0) return; + + // Store the bonus name for use during cleanup + UpdateUsedCompositeNamesList(oItem, "PRC_CBon", sBonus); + + //Moved TotalAndRemoveProperty into switch to prevent + //accidental deletion of unsupported property types + switch(iType) + { + case ITEM_PROPERTY_ABILITY_BONUS: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + iCurVal -= GetPersistantLocalInt(GetItemPossessor(oItem), "LetoAbility_"+IntToString(iSubType)); + if (DEBUG) DoDebug("Ability Decrease #1: oItem "+GetName(oItem)+" sBonus "+sBonus+" iChange "+IntToString(iChange)+" iCurVal "+IntToString(iCurVal)+" iSubType "+IntToString(iSubType)); + if ((iCurVal + iChange) > 50) + { + iVal -= iCurVal + iChange - 50; + iCurVal = 50; + iChange = 0; + } + if(iCurVal+iChange > 0) + { + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(iSubType, iCurVal + iChange), oItem); + if (DEBUG) DoDebug("Ability Increase"); + } + else if(iCurVal+iChange < 0) + { + if (DEBUG) DoDebug("Ability Decrease #2: oItem "+GetName(oItem)+" sBonus "+sBonus+" iChange "+IntToString(iChange)+" iSubType "+IntToString(iSubType)); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(iSubType, -1*(iCurVal + iChange)), oItem); + } + break; + case ITEM_PROPERTY_AC_BONUS: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonus(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsAlign(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsDmgType(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsRace(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsSAlign(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ATTACK_BONUS: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonus(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonusVsAlign(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonusVsRace(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonusVsSAlign(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDamageBonusVsRace(iSubType, DAMAGE_TYPE_SLASHING, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 10) + { + iVal -= iCurVal + iChange - 10; + iCurVal = 10; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_AC: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 5) + { + iVal -= iCurVal + iChange - 5; + iCurVal = 5; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAC(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 5) + { + iVal -= iCurVal + iChange - 5; + iCurVal = 5; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackPenalty(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 5) + { + iVal -= iCurVal + iChange - 5; + iCurVal = 5; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementPenalty(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_SAVING_THROWS: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyReducedSavingThrow(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyReducedSavingThrowVsX(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 10) + { + iVal -= iCurVal + iChange - 10; + iCurVal = 10; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseSkill(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonus(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonusVsAlign(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonusVsRace(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonusVsSAlign(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_MIGHTY: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyMaxRangeStrengthMod(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_REGENERATION: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyRegeneration(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + iCurVal = TotalAndRemoveProperty(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyVampiricRegeneration(iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_SAVING_THROW_BONUS: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusSavingThrowVsX(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusSavingThrow(iSubType, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_SKILL_BONUS: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 50) + { + iVal -= iCurVal + iChange - 50; + iCurVal = 50; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertySkillBonus(iSubType, iCurVal + iChange), oItem); + break; + } + SetLocalInt(oItem, sBonus, iVal); +} + +int GetACBonus(object oItem) +{ + if(!GetIsObjectValid(oItem)) return 0; + + itemproperty ip = GetFirstItemProperty(oItem); + int iTotal = 0; + + while(GetIsItemPropertyValid(ip)){ + if(GetItemPropertyType(ip) == ITEM_PROPERTY_AC_BONUS) + iTotal += GetItemPropertyCostTableValue(ip); + ip = GetNextItemProperty(oItem); + } + return iTotal; +} + +int GetBaseAC(object oItem){ return GetItemACValue(oItem) - GetACBonus(oItem); } + +int GetOppositeElement(int iElem) +{ + switch(iElem){ + case IP_CONST_DAMAGETYPE_ACID: + return DAMAGE_TYPE_ELECTRICAL; + case IP_CONST_DAMAGETYPE_COLD: + return IP_CONST_DAMAGETYPE_FIRE; + case IP_CONST_DAMAGETYPE_DIVINE: + return IP_CONST_DAMAGETYPE_NEGATIVE; + case IP_CONST_DAMAGETYPE_ELECTRICAL: + return IP_CONST_DAMAGETYPE_ACID; + case IP_CONST_DAMAGETYPE_FIRE: + return IP_CONST_DAMAGETYPE_COLD; + case IP_CONST_DAMAGETYPE_NEGATIVE: + return IP_CONST_DAMAGETYPE_POSITIVE; + } + return -1; +} + +int TotalAndRemovePropertyT(object oItem, int iType, int iSubType = -1) +{ + itemproperty ip = GetFirstItemProperty(oItem); + int total = 0; + int iTemp; + + while(GetIsItemPropertyValid(ip)){ + if(GetItemPropertyType(ip) == iType && (GetItemPropertySubType(ip) == iSubType || iSubType == -1)){ + iTemp = GetItemPropertyCostTableValue(ip); + total = iTemp > total ? iTemp : total; + if (GetItemPropertyDurationType(ip)== DURATION_TYPE_TEMPORARY) RemoveItemProperty(oItem, ip); + } + ip = GetNextItemProperty(oItem); + } + return total; +} + +//SetCompositeBonusT is a special case that doesn't need to use DelayAddItemProperty because +//the property that is added is always different from the one that was deleted by TotalAndRemovePropertyT. +void SetCompositeBonusT(object oItem, string sBonus, int iVal, int iType, int iSubType = -1) +{ + int iOldVal = GetLocalInt(oItem, sBonus); + if (GetLocalInt(GetItemPossessor(oItem),"ONREST")) iOldVal =0; + int iChange = iVal - iOldVal; + int iCurVal = 0; + + if(iChange == 0) return; + + // Store the bonus name for use during cleanup + UpdateUsedCompositeNamesList(oItem, "PRC_CBonT", sBonus); + + //Moved TotalAndRemoveProperty into switch to prevent + //accidental deletion of unsupported property types + switch(iType) + { + case ITEM_PROPERTY_ABILITY_BONUS: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 50) + { + iVal -= iCurVal + iChange - 50; + iCurVal = 50; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAbilityBonus(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_AC_BONUS: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonus(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsAlign(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsDmgType(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsRace(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsSAlign(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ATTACK_BONUS: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonus(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonusVsAlign(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonusVsRace(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonusVsSAlign(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + if(iCurVal+iChange > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDamageBonusVsRace(iSubType, DAMAGE_TYPE_SLASHING, iCurVal + iChange), oItem); + break; + case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDecreaseAbility(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DECREASED_AC: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 5) + { + iVal -= iCurVal + iChange - 5; + iCurVal = 5; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDecreaseAC(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 5) + { + iVal -= iCurVal + iChange - 5; + iCurVal = 5; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackPenalty(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 5) + { + iVal -= iCurVal + iChange - 5; + iCurVal = 5; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementPenalty(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DECREASED_SAVING_THROWS: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyReducedSavingThrowVsX(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyReducedSavingThrow(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 10) + { + iVal -= iCurVal + iChange - 10; + iCurVal = 10; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDecreaseSkill(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonus(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsAlign(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsRace(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsSAlign(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_MIGHTY: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyMaxRangeStrengthMod(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_REGENERATION: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyRegeneration(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + iCurVal = TotalAndRemovePropertyT(oItem, iType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyVampiricRegeneration(iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_SAVING_THROW_BONUS: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyBonusSavingThrowVsX(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 20) + { + iVal -= iCurVal + iChange - 20; + iCurVal = 20; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyBonusSavingThrow(iSubType, iCurVal + iChange), oItem,9999.0); + break; + case ITEM_PROPERTY_SKILL_BONUS: + iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType); + if ((iCurVal + iChange) > 50) + { + iVal -= iCurVal + iChange - 50; + iCurVal = 50; + iChange = 0; + } + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertySkillBonus(iSubType, iCurVal + iChange), oItem,9999.0); + break; + } + SetLocalInt(oItem, sBonus, iVal); +} + +int GetItemPropertyDamageType(object oWeapon) +{ + if(GetObjectType(oWeapon) != OBJECT_TYPE_ITEM) + return -1; + + int iWeaponType = GetBaseItemType(oWeapon); + int iDamageType = StringToInt(Get2DACache("baseitems","WeaponType",iWeaponType)); + switch(iDamageType) + { + case 1: return IP_CONST_DAMAGETYPE_PIERCING; break; + case 2: return IP_CONST_DAMAGETYPE_BLUDGEONING; break; + case 3: return IP_CONST_DAMAGETYPE_SLASHING; break; + case 4: return IP_CONST_DAMAGETYPE_SLASHING; break; // slashing & piercing... slashing bonus. + + default: return -1; + } + return -1; +} + +int GetItemDamageType(object oWeapon) +{ + if(GetObjectType(oWeapon) != OBJECT_TYPE_ITEM) + return -1; + + int iWeaponType = GetBaseItemType(oWeapon); + int iDamageType = StringToInt( Get2DACache("baseitems","WeaponType",iWeaponType) ); + switch(iDamageType) + { + case 1: return DAMAGE_TYPE_PIERCING; break; + case 2: return DAMAGE_TYPE_BLUDGEONING; break; + case 3: return DAMAGE_TYPE_SLASHING; break; + case 4: return DAMAGE_TYPE_SLASHING; break; // slashing & piercing... slashing bonus. + + default: return -1; + } + return -1; +} + +// To ensure the damage bonus stacks with any existing enhancement bonus, +// we create a temporary damage bonus on the weapon. We do not want to do this +// if the weapon is of the "slashing and piercing" type, because the +// enhancement bonus is considered "physical", not "slashing" or "piercing". +// If you borrow this code, make sure to keep the "IPEnh" and realize that +// "slashing and piercing" weapons need a special case. +void IPEnhancementBonusToDamageBonus(object oWeap) +{ + int iBonus = 0; + int iTemp; + + if (GetLocalInt(oWeap, "IPEnh") || !GetIsObjectValid(oWeap)) return; + + itemproperty ip = GetFirstItemProperty(oWeap); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_ENHANCEMENT_BONUS) + iTemp = GetItemPropertyCostTableValue(ip); + iBonus = iTemp > iBonus ? iTemp : iBonus; + ip = GetNextItemProperty(oWeap); + } + + SetCompositeDamageBonusT(oWeap,"IPEnh",iBonus); +} + +int TotalAndRemoveDamagePropertyT(object oItem, int iSubType) +{ + itemproperty ip = GetFirstItemProperty(oItem); + int iPropertyValue; + int total = 0; + int iTemp; + + while(GetIsItemPropertyValid(ip)) + { + iPropertyValue = GetItemPropertyCostTableValue(ip); + + if((GetItemPropertyType(ip) == ITEM_PROPERTY_DAMAGE_BONUS) && + (GetItemPropertySubType(ip) == iSubType) && + ((iPropertyValue < 6) || (iPropertyValue > 15))) + { + total = iPropertyValue > total ? iPropertyValue : total; + if (GetItemPropertyDurationType(ip)== DURATION_TYPE_TEMPORARY) RemoveItemProperty(oItem, ip); + } + ip = GetNextItemProperty(oItem); + + } + return total; +} + +void SetCompositeDamageBonusT(object oItem, string sBonus, int iVal, int iSubType = -1) +{ + int iOldVal = GetLocalInt(oItem, sBonus); + int iChange = iVal - iOldVal; + int iLinearDamage = 0; + int iCurVal = 0; + + if(iChange == 0) return; + + // Store the bonus name for use during cleanup + UpdateUsedCompositeNamesList(oItem, "PRC_CBonT", sBonus); + + if (iSubType == -1) iSubType = GetItemPropertyDamageType(oItem); + if (iSubType == -1) return; // if it's still -1 we're not dealing with a weapon. + + iCurVal = TotalAndRemoveDamagePropertyT(oItem, iSubType); + + if (iCurVal > 15) iCurVal -= 10; // values 6-20 are in the 2da as lines 16-30 + iLinearDamage = iCurVal + iChange; + if (iLinearDamage > 20) + { + iVal = iLinearDamage - 20; // Change the stored value to reflect the fact that we overflowed + iLinearDamage = 20; // This is prior to adjustment due to non-linear values + } + if (iLinearDamage > 5) iLinearDamage += 10; // values 6-20 are in the 2da as lines 16-30 + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonus(iSubType, iLinearDamage), oItem,9999.0); + + SetLocalInt(oItem, sBonus, iVal); +} + +void TotalRemovePropertyT(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyDurationType(ip)== DURATION_TYPE_TEMPORARY) + RemoveItemProperty(oItem, ip); + ip = GetNextItemProperty(oItem); + } +} + +void DeletePRCLocalIntsT(object oPC, object oItem = OBJECT_INVALID) +{ + // See if we were given a valid item as parameter. If we were, we + // will be removing ints from it. + // Otherwise, we will take the item in each slot and removing the + // ints that should be on it. + int bGivenObject = GetIsObjectValid(oItem); + + // RIGHT HAND + if(!bGivenObject) + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); + + if(bGivenObject || GetIsObjectValid(oItem)) + { + // Clear composite bonuses + TotalRemovePropertyT(oItem); + DeleteNamedComposites(oItem, "PRC_CBonT"); + + /* Clear other stuff + * + * All the commented out values are the ones that didn't seem to be used anywhere anymore - Ornedan + */ + //Stormlord + DeleteLocalInt(oItem,"STShock"); /// @todo Rewrite the Stormlord not to directly manipulate values + DeleteLocalInt(oItem,"STThund"); + DeleteLocalInt(oItem,"ManArmsCore"); + //Vile/Sanctify & Un/Holy Martial Strike + DeleteLocalInt(oItem,"SanctMar"); + DeleteLocalInt(oItem,"MartialStrik"); + DeleteLocalInt(oItem,"UnholyStrik"); + DeleteLocalInt(oItem,"USanctMar"); + //Duelist Precise Strike + DeleteLocalInt(oItem,"DuelistPreciseSlash"); + //DeleteLocalInt(oItem,"DuelistPreciseSmash"); + // Dispater + DeleteLocalInt(oItem,"DispIronPowerA"); + DeleteLocalInt(oItem,"DispIronPowerD"); + // Dragonwrack + DeleteLocalInt(oItem,"DWright"); + } + + // LEFT HAND + if(!bGivenObject) + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + + if(bGivenObject || GetIsObjectValid(oItem)) + { + if(!bGivenObject) + { + // Clear composite bonuses + TotalRemovePropertyT(oItem); + DeleteNamedComposites(oItem, "PRC_CBonT"); + } + + //Vile/Sanctify & Un/Holy Martial Strike + DeleteLocalInt(oItem,"SanctMar"); + DeleteLocalInt(oItem,"MartialStrik"); + DeleteLocalInt(oItem,"UnholyStrik"); + DeleteLocalInt(oItem,"USanctMar"); + // Dragonwrack + DeleteLocalInt(oItem,"DWleft"); + // Dispater + DeleteLocalInt(oItem,"DispIronPowerA"); + DeleteLocalInt(oItem,"DispIronPowerD"); + } + + // CHEST + if(!bGivenObject) + oItem = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + + if(bGivenObject || GetIsObjectValid(oItem)) + { + if(!bGivenObject) + { + // Clear composite bonuses + TotalRemovePropertyT(oItem); + DeleteNamedComposites(oItem, "PRC_CBonT"); + } + // Frenzied Berzerker + DeleteLocalInt(oItem,"AFrenzy"); + // Shadowlord + DeleteLocalInt(oItem,"ShaDiscorp"); + // Dragonwrack + DeleteLocalInt(oItem,"Dragonwrack"); + } + + // LEFT RING + if(!bGivenObject) + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + + if(bGivenObject || GetIsObjectValid(oItem)) + { + if(!bGivenObject) + { + // Clear composite bonuses + TotalRemovePropertyT(oItem); + DeleteNamedComposites(oItem, "PRC_CBonT"); + } + } + + // ARMS + if(!bGivenObject) + oItem = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if(bGivenObject || GetIsObjectValid(oItem)) + { + if(!bGivenObject) + { + // Clear composite bonuses + TotalRemovePropertyT(oItem); + DeleteNamedComposites(oItem, "PRC_CBonT"); + } + + // Disciple of Mephistopheles + DeleteLocalInt(oItem,"DiscMephGlove"); + } +} + +void SetCompositeAttackBonus(object oPC, string sBonus, int iVal, int iSubType = ATTACK_BONUS_MISC) +{ + object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oPC)); + + int iSpl = 2732; //SPELL_SET_COMPOSITE_ATTACK_BONUS; + + SetLocalString(oCastingObject, "SET_COMPOSITE_STRING", sBonus); + SetLocalInt(oCastingObject, "SET_COMPOSITE_VALUE", iVal); + SetLocalInt(oCastingObject, "SET_COMPOSITE_SUBTYPE", iSubType); + + DelayCommand(0.1, AssignCommand(oCastingObject, ActionCastSpellAtObject(iSpl, oPC, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE))); + + DestroyObject(oCastingObject, 6.0); +} + +int GetSRByValue(int nValue) +{ + switch(nValue) + { + case 1: return 52; + case 2: return 53; + case 3: return 54; + case 4: return 55; + case 5: return 56; + case 6: return 57; + case 7: return 58; + case 8: return 59; + case 9: return 60; + case 10: return 0; + case 12: return 1; + case 14: return 2; + case 16: return 3; + case 18: return 4; + case 20: return 5; + case 22: return 6; + case 24: return 7; + case 26: return 8; + case 28: return 9; + case 30: return 10; + case 32: return 11; + case 34: return 12; + case 36: return 13; + case 38: return 14; + case 40: return 15; + case 42: return 16; + case 44: return 17; + case 46: return 18; + case 48: return 19; + case 50: return 20; + case 52: return 21; + case 54: return 22; + case 56: return 23; + case 58: return 24; + case 60: return 25; + case 11: return 26; + case 13: return 27; + case 15: return 28; + case 17: return 29; + case 19: return 30; + case 21: return 31; + case 23: return 32; + case 25: return 33; + case 27: return 34; + case 29: return 35; + case 31: return 36; + case 33: return 37; + case 35: return 38; + case 37: return 39; + case 39: return 40; + case 41: return 41; + case 43: return 42; + case 45: return 43; + case 47: return 44; + case 49: return 45; + case 51: return 46; + case 53: return 47; + case 55: return 48; + case 57: return 49; + case 59: return 50; + case 61: return 51; + } + if(nValue < 1) + return -1; + if(nValue > 98) + return 61;//99 max + if(nValue > 61) + return 51;//61 flat cap + return -1; +} + +void UpdateUsedCompositeNamesList(object oItem, string sBase, string sComposite) +{ + // Add the bonus name to the list if it isn't there already + if(!GetLocalInt(oItem, sBase + "_Exist_" + sComposite)) + { + if(DEBUG) DoDebug("Storing the composite name '" + sComposite + "' for later deletion"); + + string sArrayName = sBase + "_Names"; + // Create the array if it doesn't exist already + if(!array_exists(oItem, sArrayName)) + array_create(oItem, sArrayName); + + // Store the bonus name in a list + array_set_string(oItem, sArrayName, array_get_size(oItem, sArrayName), sComposite); + + // Store a marker so we don't need to loop over the list to find out if the name has been stored already + SetLocalInt(oItem, sBase + "_Exist_" + sComposite, TRUE); + } +} + +void DeleteNamedComposites(object oItem, string sBase) +{ + if(DEBUG) DoDebug("Deleting composite bonus list '" + sBase + "' on " + DebugObject2Str(oItem)); + + string sArrayName = sBase + "_Names"; + string sComposite; + int nMax = array_get_size(oItem, sArrayName); + int i = 0; + + // Delete all composite values and markers + for(; i < nMax; i++) + { + sComposite = array_get_string(oItem, sArrayName, i); + if(DEBUG) DoDebug("Deleting bonus marker '" + sComposite + "'"); + DeleteLocalInt(oItem, sComposite); + DeleteLocalInt(oItem, sBase + "_Exist_" + sComposite); + } + + // Delete the array + array_delete(oItem, sArrayName); +} + +int GetIsMagicItem(object oItem) +{ + // Exclusion for specific item resrefs: if the item is one of these, it's non-magical. + string sResRef = GetResRef(oItem); + if(sResRef == "x1_wmgrenade001" || + sResRef == "prc_it_acidfire" || + sResRef == "prc_agony" || + sResRef == "prc_it_alcslpgas." || + sResRef == "x1_wmgrenade002" || + sResRef == "prc_it_alcfrost" || + sResRef == "prc_it_alcspark" || + sResRef == "prc_it_antitox" || + sResRef == "prc_baccaran" || + sResRef == "prc_it_biledrp" || + sResRef == "prc_it_blendcrm" || + sResRef == "prc_brittlebn" || + sResRef == "prc_it_crcklpdr" || + sResRef == "prc_devilweed" || + sResRef == "prc_it_emblmfr" || + sResRef == "prc_it_fareyeoil" || + sResRef == "prc_it_festerbmb" || + sResRef == "prc_it_flashplt" || + sResRef == "prc_it_healblm" || + sResRef == "prc_it_keenear" || + sResRef == "prc_it_lockslip" || + sResRef == "prc_luhix" || + sResRef == "prc_mshrm_pwdr" || + sResRef == "prc_it_natdrgt" || + sResRef == "prc_it_nerv" || + sResRef == "prc_sannish" || + sResRef == "prc_it_scrmflsk" || + sResRef == "prc_it_shedden" || + sResRef == "prc_it_shedden2" || + sResRef == "prc_it_shedden3" || + sResRef == "prc_it_shedden4" || + sResRef == "prc_it_shedden5" || + sResRef == "prc_it_softfoot" || + sResRef == "x1_wmgrenade006" || + sResRef == "prc_terran_brndy" || + sResRef == "x1_wmgrenade007" || + sResRef == "prc_vodare" || + sResRef == "prc_it_weepstn") + { + return 0; + } + + // Exception for torches: + // If the only permanent property is the Light property (44), it's not magical. + if(GetBaseItemType(oItem) == BASE_ITEM_TORCH) + { + int nCount = 0; + int nOnlyType = -1; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { + nCount++; + nOnlyType = GetItemPropertyType(ip); + } + ip = GetNextItemProperty(oItem); + } + if(nCount == 1 && nOnlyType == 44) + { + return 0; + } + } + + // Exception for healer's kits: + // If the only permanent property is type 80, it's not magical. + if(GetBaseItemType(oItem) == BASE_ITEM_HEALERSKIT) + { + int nCount = 0; + int nOnlyType = -1; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { + nCount++; + nOnlyType = GetItemPropertyType(ip); + } + ip = GetNextItemProperty(oItem); + } + if(nCount == 1 && nOnlyType == 80) + { + return 0; + } + } + + // Exception for thief's tools: + // If the only permanent property is type 55, it's not magical. + if(GetBaseItemType(oItem) == BASE_ITEM_THIEVESTOOLS) + { + int nCount = 0; + int nOnlyType = -1; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { + nCount++; + nOnlyType = GetItemPropertyType(ip); + } + ip = GetNextItemProperty(oItem); + } + if(nCount == 1 && nOnlyType == 55) + { + return 0; + } + } + + // Normal magic property checking + itemproperty ip = GetFirstItemProperty(oItem); + int nType; + int nSubtype; + while(GetIsItemPropertyValid(ip)) // loop through item properties looking for a magical one + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { // ignore temporary properties + nType = GetItemPropertyType(ip); + if((nType >= 0 && nType <= 9) || // read from itempropdef.2da + (nType >= 13 && nType <= 20) || + (nType == 26) || + (nType >= 32 && nType <= 44) || + (nType == 46) || + (nType >= 51 && nType <= 59) || + (nType == 61) || + (nType >= 67 && nType <= 69) || + (nType >= 71 && nType <= 80) || + (nType == 82) || + (nType == 84) || + (nType >= 100 && nType <= 105) || + (nType >= 133 && nType <= 134)) + { + return 1; // magical property found + } + nSubtype = GetItemPropertySubType(ip); + if(nType == ITEM_PROPERTY_BONUS_FEAT) + { + if(GetBaseItemType(oItem) == BASE_ITEM_WHIP) + { + if(nSubtype != IP_CONST_FEAT_DISARM_WHIP) + return 1; + } + else + return 1; + } + } + ip = GetNextItemProperty(oItem); + } + return 0; +} + +/* int GetIsMagicItem(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + int nType; + int nSubtype; + while(GetIsItemPropertyValid(ip)) //loop through item properties looking for a magical one + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { //ignore temporary properties + nType = GetItemPropertyType(ip); + if((nType >= 0 && nType <= 9) || //read from itempropdef.2da + (nType >= 13 && nType <= 20) || + (nType == 26) || + (nType >= 32 && nType <= 44) || + (nType == 46) || + (nType >= 51 && nType <= 59) || + (nType == 61) || + (nType >= 67 && nType <= 69) || + (nType >= 71 && nType <= 80) || + (nType == 82) || + (nType == 84) || + (nType >= 100 && nType <= 105) || + (nType >= 133 && nType <= 134)) + { + return 1; //magical property + } + nSubtype = GetItemPropertySubType(ip); + + if(nType == ITEM_PROPERTY_BONUS_FEAT) + { + if(GetBaseItemType(oItem) == BASE_ITEM_WHIP) + { + if(nSubtype != IP_CONST_FEAT_DISARM_WHIP) + return 1; + } + else + return 1; + } + ip = GetNextItemProperty(oItem); + } + } + return 0; +} */ + +int FeatToIprop(int nFeat) +{ + switch(nFeat) + {//: Weapon Focus + case FEAT_WEAPON_FOCUS_BASTARD_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_BASTARD_SWORD; + case FEAT_WEAPON_FOCUS_BATTLE_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_BATTLE_AXE; + case FEAT_WEAPON_FOCUS_CLUB: return IP_CONST_FEAT_WEAPON_FOCUS_CLUB; + case FEAT_WEAPON_FOCUS_DAGGER: return IP_CONST_FEAT_WEAPON_FOCUS_DAGGER; + case FEAT_WEAPON_FOCUS_DART: return IP_CONST_FEAT_WEAPON_FOCUS_DART; + case FEAT_WEAPON_FOCUS_DIRE_MACE: return IP_CONST_FEAT_WEAPON_FOCUS_DIRE_MACE; + case FEAT_WEAPON_FOCUS_DOUBLE_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_AXE; + case FEAT_WEAPON_FOCUS_DWAXE: return IP_CONST_FEAT_WEAPON_FOCUS_DWAXE; + case FEAT_WEAPON_FOCUS_GREAT_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_GREAT_AXE; + case FEAT_WEAPON_FOCUS_GREAT_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_GREAT_SWORD; + case FEAT_WEAPON_FOCUS_HALBERD: return IP_CONST_FEAT_WEAPON_FOCUS_HALBERD; + case FEAT_WEAPON_FOCUS_HAND_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_HAND_AXE; + case FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW: return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW; + case FEAT_WEAPON_FOCUS_HEAVY_FLAIL: return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_FLAIL; + case FEAT_WEAPON_FOCUS_KAMA: return IP_CONST_FEAT_WEAPON_FOCUS_KAMA; + case FEAT_WEAPON_FOCUS_KATANA: return IP_CONST_FEAT_WEAPON_FOCUS_KATANA; + case FEAT_WEAPON_FOCUS_KUKRI: return IP_CONST_FEAT_WEAPON_FOCUS_KUKRI; + case FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW: return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW; + case FEAT_WEAPON_FOCUS_LIGHT_FLAIL: return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_FLAIL; + case FEAT_WEAPON_FOCUS_LIGHT_HAMMER: return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_HAMMER; + case FEAT_WEAPON_FOCUS_LIGHT_MACE: return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_MACE; + case FEAT_WEAPON_FOCUS_LONG_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_LONG_SWORD; + case FEAT_WEAPON_FOCUS_LONGBOW: return IP_CONST_FEAT_WEAPON_FOCUS_LONGBOW; + case FEAT_WEAPON_FOCUS_MORNING_STAR: return IP_CONST_FEAT_WEAPON_FOCUS_MORNING_STAR; + case FEAT_WEAPON_FOCUS_STAFF: return IP_CONST_FEAT_WEAPON_FOCUS_STAFF; + case FEAT_WEAPON_FOCUS_RAPIER: return IP_CONST_FEAT_WEAPON_FOCUS_RAPIER; + case FEAT_WEAPON_FOCUS_SCIMITAR: return IP_CONST_FEAT_WEAPON_FOCUS_SCIMITAR; + case FEAT_WEAPON_FOCUS_SCYTHE: return IP_CONST_FEAT_WEAPON_FOCUS_SCYTHE; + case FEAT_WEAPON_FOCUS_SHORTBOW: return IP_CONST_FEAT_WEAPON_FOCUS_SHORTBOW; + case FEAT_WEAPON_FOCUS_SPEAR: return IP_CONST_FEAT_WEAPON_FOCUS_SPEAR; + case FEAT_WEAPON_FOCUS_SHORT_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_SHORT_SWORD; + case FEAT_WEAPON_FOCUS_SHURIKEN: return IP_CONST_FEAT_WEAPON_FOCUS_SHURIKEN; + case FEAT_WEAPON_FOCUS_SICKLE: return IP_CONST_FEAT_WEAPON_FOCUS_SICKLE; + case FEAT_WEAPON_FOCUS_SLING: return IP_CONST_FEAT_WEAPON_FOCUS_SLING; + case FEAT_WEAPON_FOCUS_THROWING_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_THROWING_AXE; + case FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD; + case FEAT_WEAPON_FOCUS_WAR_HAMMER: return IP_CONST_FEAT_WEAPON_FOCUS_WAR_HAMMER; + case FEAT_WEAPON_FOCUS_WHIP: return IP_CONST_FEAT_WEAPON_FOCUS_WHIP; + case FEAT_WEAPON_FOCUS_EAGLE_CLAW: return IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW; + case FEAT_WEAPON_FOCUS_FALCHION: return IP_CONST_FEAT_WEAPON_FOCUS_FALCHION; + case FEAT_WEAPON_FOCUS_HEAVY_MACE: return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_MACE; + case FEAT_WEAPON_FOCUS_LIGHT_PICK: return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_PICK; + case FEAT_WEAPON_FOCUS_HEAVY_PICK: return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_PICK; + case FEAT_WEAPON_FOCUS_KATAR: return IP_CONST_FEAT_WEAPON_FOCUS_KATAR; + case FEAT_WEAPON_FOCUS_SAI: return IP_CONST_FEAT_WEAPON_FOCUS_SAI; + case FEAT_WEAPON_FOCUS_NUNCHAKU: return IP_CONST_FEAT_WEAPON_FOCUS_NUNCHAKU; + case FEAT_WEAPON_FOCUS_MAUL: return IP_CONST_FEAT_WEAPON_FOCUS_MAUL; + case FEAT_WEAPON_FOCUS_SAP: return IP_CONST_FEAT_WEAPON_FOCUS_SAP; + case FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR: return IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR; + case FEAT_WEAPON_FOCUS_GOAD: return IP_CONST_FEAT_WEAPON_FOCUS_GOAD; + case FEAT_WEAPON_FOCUS_TRIDENT: return IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT; + case FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE: return IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE; + case FEAT_WEAPON_FOCUS_ELVEN_THINBLADE: return IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE; + case FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE: return IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE; + + //:: Weapon Specialization + case FEAT_WEAPON_SPECIALIZATION_CLUB: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_CLUB; + case FEAT_WEAPON_SPECIALIZATION_DAGGER: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DAGGER; + case FEAT_WEAPON_SPECIALIZATION_DART: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DART; + case FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW; + case FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW; + case FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE; + case FEAT_WEAPON_SPECIALIZATION_MORNING_STAR: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_MORNING_STAR; + case FEAT_WEAPON_SPECIALIZATION_STAFF: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_STAFF; + case FEAT_WEAPON_SPECIALIZATION_SPEAR: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SPEAR; + case FEAT_WEAPON_SPECIALIZATION_SICKLE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SICKLE; + case FEAT_WEAPON_SPECIALIZATION_SLING: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SLING; + case FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE; + case FEAT_WEAPON_SPECIALIZATION_LONGBOW: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LONGBOW; + case FEAT_WEAPON_SPECIALIZATION_SHORTBOW: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHORTBOW; + case FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD; + case FEAT_WEAPON_SPECIALIZATION_RAPIER: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_RAPIER; + case FEAT_WEAPON_SPECIALIZATION_SCIMITAR: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SCIMITAR; + case FEAT_WEAPON_SPECIALIZATION_LONG_SWORD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LONG_SWORD; + case FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD; + case FEAT_WEAPON_SPECIALIZATION_HAND_AXE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_HAND_AXE; + case FEAT_WEAPON_SPECIALIZATION_THROWING_AXE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_THROWING_AXE; + case FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE; + case FEAT_WEAPON_SPECIALIZATION_GREAT_AXE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_GREAT_AXE; + case FEAT_WEAPON_SPECIALIZATION_HALBERD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_HALBERD; + case FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER; + case FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL; + case FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER; + case FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL; + case FEAT_WEAPON_SPECIALIZATION_KAMA: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_KAMA; + case FEAT_WEAPON_SPECIALIZATION_KUKRI: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_KUKRI; + case FEAT_WEAPON_SPECIALIZATION_SHURIKEN: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHURIKEN; + case FEAT_WEAPON_SPECIALIZATION_SCYTHE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SCYTHE; + case FEAT_WEAPON_SPECIALIZATION_KATANA: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_KATANA; + case FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD; + case FEAT_WEAPON_SPECIALIZATION_DIRE_MACE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DIRE_MACE; + case FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE; + case FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD; + case FEAT_WEAPON_SPECIALIZATION_DWAXE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DWAXE; + case FEAT_WEAPON_SPECIALIZATION_WHIP: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_WHIP; + + case FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW; + case FEAT_WEAPON_SPECIALIZATION_FALCHION: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_FALCHION; + case FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE; + case FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK; + case FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK; + case FEAT_WEAPON_SPECIALIZATION_KATAR: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_KATAR; + case FEAT_WEAPON_SPECIALIZATION_SAI: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SAI; + case FEAT_WEAPON_SPECIALIZATION_NUNCHAKU: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_NUNCHAKU; + case FEAT_WEAPON_SPECIALIZATION_MAUL: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_MAUL; + case FEAT_WEAPON_SPECIALIZATION_SAP: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_SAP; + case FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR; + case FEAT_WEAPON_SPECIALIZATION_GOAD: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_GOAD; + case FEAT_WEAPON_SPECIALIZATION_TRIDENT: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_TRIDENT; + + case FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE; + case FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE; + case FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE: return IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE; + + + //:: Sanctify Martial Strike + case FEAT_SANCTIFY_MARTIAL_CLUB: return IP_CONST_FEAT_SANCTIFY_MARTIAL_CLUB; + case FEAT_SANCTIFY_MARTIAL_DAGGER: return IP_CONST_FEAT_SANCTIFY_MARTIAL_DAGGER; + case FEAT_SANCTIFY_MARTIAL_DART: return IP_CONST_FEAT_SANCTIFY_MARTIAL_DART; + case FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW: return IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW; + case FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW; + case FEAT_SANCTIFY_MARTIAL_LIGHTMACE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTMACE; + case FEAT_SANCTIFY_MARTIAL_MORNINGSTAR: return IP_CONST_FEAT_SANCTIFY_MARTIAL_MORNINGSTAR; + case FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF: return IP_CONST_FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF; + case FEAT_SANCTIFY_MARTIAL_SHORTSPEAR: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SHORTSPEAR; + case FEAT_SANCTIFY_MARTIAL_SICKLE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SICKLE; + case FEAT_SANCTIFY_MARTIAL_SLING: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SLING; + case FEAT_SANCTIFY_MARTIAL_LONGBOW: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LONGBOW; + case FEAT_SANCTIFY_MARTIAL_SHORTBOW: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SHORTBOW; + case FEAT_SANCTIFY_MARTIAL_SHORTSWORD: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SHORTSWORD; + case FEAT_SANCTIFY_MARTIAL_RAPIER: return IP_CONST_FEAT_SANCTIFY_MARTIAL_RAPIER; + case FEAT_SANCTIFY_MARTIAL_SCIMITAR: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SCIMITAR; + case FEAT_SANCTIFY_MARTIAL_LONGSWORD: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LONGSWORD; + case FEAT_SANCTIFY_MARTIAL_GREATSWORD: return IP_CONST_FEAT_SANCTIFY_MARTIAL_GREATSWORD; + case FEAT_SANCTIFY_MARTIAL_HANDAXE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_HANDAXE; + case FEAT_SANCTIFY_MARTIAL_BATTLEAXE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_BATTLEAXE; + case FEAT_SANCTIFY_MARTIAL_GREATAXE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_GREATAXE; + case FEAT_SANCTIFY_MARTIAL_HALBERD: return IP_CONST_FEAT_SANCTIFY_MARTIAL_HALBERD; + case FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER; + case FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL; + case FEAT_SANCTIFY_MARTIAL_WARHAMMER: return IP_CONST_FEAT_SANCTIFY_MARTIAL_WARHAMMER; + case FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL: return IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL; + case FEAT_SANCTIFY_MARTIAL_KAMA: return IP_CONST_FEAT_SANCTIFY_MARTIAL_KAMA; + case FEAT_SANCTIFY_MARTIAL_KUKRI: return IP_CONST_FEAT_SANCTIFY_MARTIAL_KUKRI; + case FEAT_SANCTIFY_MARTIAL_SHURIKEN: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SHURIKEN; + case FEAT_SANCTIFY_MARTIAL_SCYTHE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SCYTHE; + case FEAT_SANCTIFY_MARTIAL_KATANA: return IP_CONST_FEAT_SANCTIFY_MARTIAL_KATANA; + case FEAT_SANCTIFY_MARTIAL_BASTARDSWORD: return IP_CONST_FEAT_SANCTIFY_MARTIAL_BASTARDSWORD; + case FEAT_SANCTIFY_MARTIAL_DIREMACE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_DIREMACE; + case FEAT_SANCTIFY_MARTIAL_DOUBLEAXE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_DOUBLEAXE; + case FEAT_SANCTIFY_MARTIAL_TWOBLADED: return IP_CONST_FEAT_SANCTIFY_MARTIAL_TWOBLADEDSWORD; + case FEAT_SANCTIFY_MARTIAL_DWAXE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_DWARVENAXE; + case FEAT_SANCTIFY_MARTIAL_WHIP: return IP_CONST_FEAT_SANCTIFY_MARTIAL_WHIP; + + case FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW: return IP_CONST_FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW; + case FEAT_SANCTIFY_MARTIAL_FALCHION: return IP_CONST_FEAT_SANCTIFY_MARTIAL_FALCHION; + case FEAT_SANCTIFY_MARTIAL_HEAVY_MACE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVY_MACE; + case FEAT_SANCTIFY_MARTIAL_LIGHT_PICK: return IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHT_PICK; + case FEAT_SANCTIFY_MARTIAL_HEAVY_PICK: return IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVY_PICK; + case FEAT_SANCTIFY_MARTIAL_KATAR: return IP_CONST_FEAT_SANCTIFY_MARTIAL_KATAR; + case FEAT_SANCTIFY_MARTIAL_SAI: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SAI; + case FEAT_SANCTIFY_MARTIAL_NUNCHAKU: return IP_CONST_FEAT_SANCTIFY_MARTIAL_NUNCHAKU; + case FEAT_SANCTIFY_MARTIAL_MAUL: return IP_CONST_FEAT_SANCTIFY_MARTIAL_MAUL; + case FEAT_SANCTIFY_MARTIAL_SAP: return IP_CONST_FEAT_SANCTIFY_MARTIAL_SAP; + case FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR: return IP_CONST_FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR; + case FEAT_SANCTIFY_MARTIAL_GOAD: return IP_CONST_FEAT_SANCTIFY_MARTIAL_GOAD; + case FEAT_SANCTIFY_MARTIAL_TRIDENT: return IP_CONST_FEAT_SANCTIFY_MARTIAL_TRIDENT; + + case FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE; + case FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE; + case FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE: return IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE; + } + + return - 1; +} + +// This maps the Weapon Focus IPRP constants to actual Weapon Focus Feat constants +int WF_IpropToFeat(int nIPFeat) +{ + switch (nIPFeat) + { + case IP_CONST_FEAT_WEAPON_FOCUS_BASTARD_SWORD: return FEAT_WEAPON_FOCUS_BASTARD_SWORD; + case IP_CONST_FEAT_WEAPON_FOCUS_BATTLE_AXE: return FEAT_WEAPON_FOCUS_BATTLE_AXE; + case IP_CONST_FEAT_WEAPON_FOCUS_CLUB: return FEAT_WEAPON_FOCUS_CLUB; + case IP_CONST_FEAT_WEAPON_FOCUS_DAGGER: return FEAT_WEAPON_FOCUS_DAGGER; + case IP_CONST_FEAT_WEAPON_FOCUS_DART: return FEAT_WEAPON_FOCUS_DART; + case IP_CONST_FEAT_WEAPON_FOCUS_DIRE_MACE: return FEAT_WEAPON_FOCUS_DIRE_MACE; + case IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_AXE: return FEAT_WEAPON_FOCUS_DOUBLE_AXE; + case IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR: return FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR; + case IP_CONST_FEAT_WEAPON_FOCUS_DWAXE: return FEAT_WEAPON_FOCUS_DWAXE; + case IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW: return FEAT_WEAPON_FOCUS_EAGLE_CLAW; + case IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE: return FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE; + case IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE: return FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE; + case IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE: return FEAT_WEAPON_FOCUS_ELVEN_THINBLADE; + case IP_CONST_FEAT_WEAPON_FOCUS_FALCHION: return FEAT_WEAPON_FOCUS_FALCHION; + case IP_CONST_FEAT_WEAPON_FOCUS_GOAD: return FEAT_WEAPON_FOCUS_GOAD; + case IP_CONST_FEAT_WEAPON_FOCUS_GREAT_AXE: return FEAT_WEAPON_FOCUS_GREAT_AXE; + case IP_CONST_FEAT_WEAPON_FOCUS_GREAT_SWORD: return FEAT_WEAPON_FOCUS_GREAT_SWORD; + case IP_CONST_FEAT_WEAPON_FOCUS_HALBERD: return FEAT_WEAPON_FOCUS_HALBERD; + case IP_CONST_FEAT_WEAPON_FOCUS_HAND_AXE: return FEAT_WEAPON_FOCUS_HAND_AXE; + case IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW: return FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW; + case IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_FLAIL: return FEAT_WEAPON_FOCUS_HEAVY_FLAIL; + case IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_MACE: return FEAT_WEAPON_FOCUS_HEAVY_MACE; + case IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_PICK: return FEAT_WEAPON_FOCUS_HEAVY_PICK; + case IP_CONST_FEAT_WEAPON_FOCUS_KAMA: return FEAT_WEAPON_FOCUS_KAMA; + case IP_CONST_FEAT_WEAPON_FOCUS_KATANA: return FEAT_WEAPON_FOCUS_KATANA; + case IP_CONST_FEAT_WEAPON_FOCUS_KATAR: return FEAT_WEAPON_FOCUS_KATAR; + case IP_CONST_FEAT_WEAPON_FOCUS_KUKRI: return FEAT_WEAPON_FOCUS_KUKRI; + case IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW: return FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW; + case IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_FLAIL: return FEAT_WEAPON_FOCUS_LIGHT_FLAIL; + case IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_HAMMER: return FEAT_WEAPON_FOCUS_LIGHT_HAMMER; + case IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_LANCE: return FEAT_WEAPON_FOCUS_LIGHT_LANCE; + case IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_MACE: return FEAT_WEAPON_FOCUS_LIGHT_MACE; + case IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_PICK: return FEAT_WEAPON_FOCUS_LIGHT_PICK; + case IP_CONST_FEAT_WEAPON_FOCUS_LONGBOW: return FEAT_WEAPON_FOCUS_LONGBOW; + case IP_CONST_FEAT_WEAPON_FOCUS_LONG_SWORD: return FEAT_WEAPON_FOCUS_LONG_SWORD; + case IP_CONST_FEAT_WEAPON_FOCUS_MAUL: return FEAT_WEAPON_FOCUS_MAUL; + case IP_CONST_FEAT_WEAPON_FOCUS_MINDBLADE: return FEAT_WEAPON_FOCUS_MINDBLADE; + case IP_CONST_FEAT_WEAPON_FOCUS_MORNING_STAR: return FEAT_WEAPON_FOCUS_MORNING_STAR; + case IP_CONST_FEAT_WEAPON_FOCUS_NUNCHAKU: return FEAT_WEAPON_FOCUS_NUNCHAKU; + case IP_CONST_FEAT_WEAPON_FOCUS_RAPIER: return FEAT_WEAPON_FOCUS_RAPIER; + case IP_CONST_FEAT_WEAPON_FOCUS_RAY: return FEAT_WEAPON_FOCUS_RAY; + case IP_CONST_FEAT_WEAPON_FOCUS_SAI: return FEAT_WEAPON_FOCUS_SAI; + case IP_CONST_FEAT_WEAPON_FOCUS_SAP: return FEAT_WEAPON_FOCUS_SAP; + case IP_CONST_FEAT_WEAPON_FOCUS_SCIMITAR: return FEAT_WEAPON_FOCUS_SCIMITAR; + case IP_CONST_FEAT_WEAPON_FOCUS_SCYTHE: return FEAT_WEAPON_FOCUS_SCYTHE; + case IP_CONST_FEAT_WEAPON_FOCUS_SHORTBOW: return FEAT_WEAPON_FOCUS_SHORTBOW; + case IP_CONST_FEAT_WEAPON_FOCUS_SHORT_SWORD: return FEAT_WEAPON_FOCUS_SHORT_SWORD; + case IP_CONST_FEAT_WEAPON_FOCUS_SHURIKEN: return FEAT_WEAPON_FOCUS_SHURIKEN; + case IP_CONST_FEAT_WEAPON_FOCUS_SICKLE: return FEAT_WEAPON_FOCUS_SICKLE; + case IP_CONST_FEAT_WEAPON_FOCUS_SLING: return FEAT_WEAPON_FOCUS_SLING; + case IP_CONST_FEAT_WEAPON_FOCUS_SPEAR: return FEAT_WEAPON_FOCUS_SPEAR; + case IP_CONST_FEAT_WEAPON_FOCUS_STAFF: return FEAT_WEAPON_FOCUS_STAFF; + case IP_CONST_FEAT_WEAPON_FOCUS_THROWING_AXE: return FEAT_WEAPON_FOCUS_THROWING_AXE; + case IP_CONST_FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD: return FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD; + case IP_CONST_FEAT_WEAPON_FOCUS_UNARMED_STRIKE: return FEAT_WEAPON_FOCUS_UNARMED_STRIKE; + case IP_CONST_FEAT_WEAPON_FOCUS_WAR_HAMMER: return FEAT_WEAPON_FOCUS_WAR_HAMMER; + } + + return -1; // Invalid or unmapped +} + +int FocusToWeapProf(int nFeat) +{ + switch(nFeat) + { + case FEAT_WEAPON_FOCUS_CLUB: return BASE_ITEM_CLUB; + case FEAT_WEAPON_FOCUS_DAGGER: return BASE_ITEM_DAGGER; + case FEAT_WEAPON_FOCUS_DART: return BASE_ITEM_DART; + case FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW: return BASE_ITEM_HEAVYCROSSBOW; + case FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW: return BASE_ITEM_LIGHTCROSSBOW; + case FEAT_WEAPON_FOCUS_LIGHT_MACE: return BASE_ITEM_LIGHTMACE; + case FEAT_WEAPON_FOCUS_MORNING_STAR: return BASE_ITEM_MORNINGSTAR; + case FEAT_WEAPON_FOCUS_STAFF: return BASE_ITEM_QUARTERSTAFF; + case FEAT_WEAPON_FOCUS_SPEAR: return BASE_ITEM_SHORTSPEAR; + case FEAT_WEAPON_FOCUS_SICKLE: return BASE_ITEM_SICKLE; + case FEAT_WEAPON_FOCUS_SLING: return BASE_ITEM_SLING; + case FEAT_WEAPON_FOCUS_LONGBOW: return BASE_ITEM_LONGBOW; + case FEAT_WEAPON_FOCUS_SHORTBOW: return BASE_ITEM_SHORTBOW; + case FEAT_WEAPON_FOCUS_SHORT_SWORD: return BASE_ITEM_SHORTSWORD; + case FEAT_WEAPON_FOCUS_RAPIER: return BASE_ITEM_RAPIER; + case FEAT_WEAPON_FOCUS_SCIMITAR: return BASE_ITEM_SCIMITAR; + case FEAT_WEAPON_FOCUS_LONG_SWORD: return BASE_ITEM_LONGSWORD; + case FEAT_WEAPON_FOCUS_GREAT_SWORD: return BASE_ITEM_GREATSWORD; + case FEAT_WEAPON_FOCUS_HAND_AXE: return BASE_ITEM_HANDAXE; + case FEAT_WEAPON_FOCUS_THROWING_AXE: return BASE_ITEM_THROWINGAXE; + case FEAT_WEAPON_FOCUS_BATTLE_AXE: return BASE_ITEM_BATTLEAXE; + case FEAT_WEAPON_FOCUS_GREAT_AXE: return BASE_ITEM_GREATAXE; + case FEAT_WEAPON_FOCUS_HALBERD: return BASE_ITEM_HALBERD; + case FEAT_WEAPON_FOCUS_LIGHT_HAMMER: return BASE_ITEM_LIGHTHAMMER; + case FEAT_WEAPON_FOCUS_LIGHT_FLAIL: return BASE_ITEM_LIGHTFLAIL; + case FEAT_WEAPON_FOCUS_WAR_HAMMER: return BASE_ITEM_WARHAMMER; + case FEAT_WEAPON_FOCUS_HEAVY_FLAIL: return BASE_ITEM_HEAVYFLAIL; + case FEAT_WEAPON_FOCUS_KAMA: return BASE_ITEM_KAMA; + case FEAT_WEAPON_FOCUS_KUKRI: return BASE_ITEM_KUKRI; + case FEAT_WEAPON_FOCUS_SHURIKEN: return BASE_ITEM_SHURIKEN; + case FEAT_WEAPON_FOCUS_SCYTHE: return BASE_ITEM_SCYTHE; + case FEAT_WEAPON_FOCUS_KATANA: return BASE_ITEM_KATANA; + case FEAT_WEAPON_FOCUS_BASTARD_SWORD: return BASE_ITEM_BASTARDSWORD; + case FEAT_WEAPON_FOCUS_DIRE_MACE: return BASE_ITEM_DIREMACE; + case FEAT_WEAPON_FOCUS_DOUBLE_AXE: return BASE_ITEM_DOUBLEAXE; + case FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD: return BASE_ITEM_TWOBLADEDSWORD; + case FEAT_WEAPON_FOCUS_DWAXE: return BASE_ITEM_DWARVENWARAXE; + case FEAT_WEAPON_FOCUS_WHIP: return BASE_ITEM_WHIP; + case FEAT_WEAPON_FOCUS_EAGLE_CLAW: return BASE_ITEM_EAGLE_CLAW; + case FEAT_WEAPON_FOCUS_FALCHION: return BASE_ITEM_FALCHION; + case FEAT_WEAPON_FOCUS_GOAD: return BASE_ITEM_GOAD; + case FEAT_WEAPON_FOCUS_HEAVY_MACE: return BASE_ITEM_HEAVY_MACE; + case FEAT_WEAPON_FOCUS_LIGHT_PICK: return BASE_ITEM_LIGHT_PICK; + case FEAT_WEAPON_FOCUS_HEAVY_PICK: return BASE_ITEM_HEAVY_PICK; + case FEAT_WEAPON_FOCUS_KATAR: return BASE_ITEM_KATAR; + case FEAT_WEAPON_FOCUS_SAI: return BASE_ITEM_SAI; + case FEAT_WEAPON_FOCUS_NUNCHAKU: return BASE_ITEM_NUNCHAKU; + case FEAT_WEAPON_FOCUS_MAUL: return BASE_ITEM_MAUL; + case FEAT_WEAPON_FOCUS_SAP: return BASE_ITEM_SAP; + case FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR: return BASE_ITEM_DOUBLE_SCIMITAR; + case FEAT_WEAPON_FOCUS_TRIDENT: return BASE_ITEM_TRIDENT; + case FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE: return BASE_ITEM_ELVEN_LIGHTBLADE; + case FEAT_WEAPON_FOCUS_ELVEN_THINBLADE: return BASE_ITEM_ELVEN_THINBLADE; + case FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE: return BASE_ITEM_ELVEN_COURTBLADE; + + } + return - 1; +} + +void AddSkinFeat(int nFeat, int IPFeat, object oSkin, object oPC = OBJECT_SELF, float fDuration = 0.0f) +{ + // Already has that feat - do nothing + if(GetHasFeat(nFeat, oPC)) + return; + + int nDurType = fDuration == 0.0f ? DURATION_TYPE_PERMANENT : DURATION_TYPE_TEMPORARY; + + AddItemProperty(nDurType, ItemPropertyBonusFeat(IPFeat), oSkin, fDuration); +} \ No newline at end of file diff --git a/src/include/inc_logmessage.nss b/src/include/inc_logmessage.nss new file mode 100644 index 0000000..4729cc6 --- /dev/null +++ b/src/include/inc_logmessage.nss @@ -0,0 +1,689 @@ +/** @file +Log Message 1.06 - versatile send message wrapper function +by OldManWhistler + +This script is available as a individual download on NWN Vault: +http://nwvault.ign.com/Files/scripts/data/1057281729727.shtml + +Also check out my portfolio: +http://nwvault.ign.com/portfolios/data/1054937958.shtml + +It is easiest to think of it as a wrapper for SendMessageToPC, +SendMessageToAllDMs, FloatingTextStringOnCreature, ActionSpeakString, +PrintString, and WriteTimeStampedLogEntry. You can use log message instead +of any of those functions. + +The name is a misnomer, it is not a tool for "logging" text, rather a tool +for "sending" text. In retrospect I should have called it SendMessage instead +of LogMessage. My bad. + +LogMessage is a generic function for sending a message to a target (PC, DM, +Party, Server log file). There is a parameter that controls how the function +will behave. + +ie: +LogMessage (LOG_DISABLED, oPC, "Do nothing"); +LogMessage (LOG_PC, oPC, "Send only to the oPC who activated it (floating text)"); +LogMessage (LOG_PARTY, oPC, "Send to the oPC and all of their party members (floating text)"); +LogMessage (LOG_PC | LOG_DM_ALL, oPC, "Send only to the oPC who activated it (floating text) and the DM channel"); +LogMessage (LOG_DM_ALL, oPC, "Send to all the DMs on the server"); +LogMessage (LOG_DM_20, oPC, "Send to all DMs within distance 20' of oPC"); +LogMessage (LOG_TO_SERVER_LOG | LOG_PC, oPC, "Send only to the oPC who activated it (floating text) and the server log file"); +LogMessage (LOG_TO_SERVER_LOG, oPC, "Send only to the server log file"); +LogMessage (LOG_PARTY_PERC, oPC, "Send to oPC and all of their party members who can see them (floating text)"); +LogMessage (LOG_PC_ALL, oPC, "Send to all players who have a local variable called 'Filter' with value 3", "Filter", 3); + +I prefer using LogMessage because then I only have to remember one interface +for how to send a message instead of five. It is also useful because I can +make a message go multiple places just by bit wise ORing several constants +together. See more on bit wise operators: +http://www.reapers.org/nwn/reference/compiled/primer.BitwiseOperators.html + +Note: Through one LogMessage function call you can send a string in any +combination of 30 different ways. + +It is useful when you want to build a script that could either be used with a +persistent world or might be used with a single party and only one DM. + +Say you write a script with several lines sending messages to the PCs using +SendMessageToPC and copying it on the DM channel using SendMessageToAllDMs. +Modifying your script would be a really big headache for the person trying to +use it with their PW, since the DM channel would be flooded by the actions of +the various players. + +Instead of using SendMessageToPC and SendMessageToAllDMs you could write +your messages as: +LogMessage (MY_LOG, oPC, "blah blah blah"); + +and then have a section in your main include file that has: +#include "inc_LogMessage" +int MY_LOG = LOGPC | LOG_DM_40; + +That way the person who is running a persistent world could set +MY_LOG as LOG_PC | LOG_DM_20 while the person who is running a single +party DMed game could set MY_LOG as LOG_PC | LOG_DM_ALL. + +All they would have to do is change that value of the constant you used and +recompile the scripts and then it would work the way they wanted it to with +very little hassle. + +The other benefit is when the person modifying your script wants to find +a specific string to change. All they have to do is do a FindInFiles and +search for LogMessage. FindInFiles will then generate a complete list of all +your strings and the filename/line number the string is generated on. + +Using SetLocalInt to create filters: +There are optional parameters to the LogMessage function called sLocalFilter +and iLocalValue. The way these work is that they require the recipient of the +message to have a LocalInt called sLocalFilter with a value of iLocalValue. +ie: +I could put a local int on specific players with SetLocalInt(oPC, "Filter1", 4); +Then LogMessage(LOG_PC_ALL, "This is a filtered message.", "", "Filter1", 4) +would send that message only to the players who have +GetLocalInt(oPC, "Filter1") == 4. +The filter does not work for the following types of logs. They will always +work irregardless of the setting of the filter variable. +LOG_DM_ALL +LOG_TO_SERVER_LOG +LOG_TIME_SERVER_LOG +LOG_PARTY_30 + +Changelog: + 1.01 -> 1.02 + Changed LOG_TO_SERVER_LOG so it has no time stamp + Added LOG_PC_ALL, LOG_TIME_SERVER_LOG + Changed so that one LogMessage call could send several messages using + the bitwise OR operator. + 1.02 -> 1.03 + Added another parameter to LogMessage for a DM only message that is only + added to the DM channels and the server log channels. + 1.03 -> 1.04 + Fixed a bug with LOG_PARTY_SERVER that was sending all the messages to only + one player in the party instead of the entire party. + 1.04 -> 1.05 + Added local int filters to control who gets the message and who doesn't. + Added some more log types: + LOG_PARTY_10, LOG_PARTY_20, LOG_PARTY_40, LOG_PARTY_80 + - like LOG_PARTY_30 except floating text is over the head of the person + - receiving the message. + LOG_DM_10_SERVER, LOG_DM_20_SERVER, LOG_DM_40_SERVER, LOG_DM_80_SERVER + - like LOG_DM_XX but with server messages instead of floating text. + LOG_PARTY_PERC, LOG_PARTY_PERC_SERVER + - like LOG_PARTY except it does a perception check to see if the party + member can see oPC. + 1.05 -> 1.06 + Added LOG_DM_ALL_SERVER + - like LOG_DM_ALL except it is sent as a server message instead of on DM channel + Added LOG_PARTY_ONLY + - Sends a server message to everyone in the party EXCEPT for the person who + triggered the message (oPC) + Added LOG_PARTY_PERC_ONLY + - Sends a server message to everyone in the party EXCEPT for the person who + triggered the message (oPC) and the members of the party who cannot perceive oPC +*/ + +// **************************************************************************** +// ** CONSTANTS +// **************************************************************************** + +// Logging Level Constants - Do not change these values! +// Note: every constant is a multiple of 2. If you want to send something to +// multiple log locations then use the bitwise OR operator. +// ie: using "LOG_PC | LOG_DM_ALL" will send the message to the player and to +// the DM channel. + +/// Do not send a message. +const int LOG_DISABLED = 0x0; +/// Send only to the oPC who activated it (floating text) +const int LOG_PC = 0x1; +/// Send only to the oPC who activated it (server message window) +const int LOG_PC_SERVER = 0x2; +/// Send to all players on the server (server message window) +const int LOG_PC_ALL = 0x4; +/// Send to the oPC and all of their party members (floating text) +const int LOG_PARTY = 0x8; +/// Send to the oPC and all of their party members (server message window) +const int LOG_PARTY_SERVER = 0x10; +/// Send to the oPC and their nearby (30m) party members (floating text) +const int LOG_PARTY_30 = 0x20; +/// Send to the DM channel (DM channel) +const int LOG_DM_ALL = 0x40; +/// Send to all DMs within distance 10m of oPC (floating text) +const int LOG_DM_10 = 0x80; +/// Send to all DMs within distance 20m of oPC (floating text) +const int LOG_DM_20 = 0x100; +/// Send to all DMs within distance 40m of oPC (floating text) +const int LOG_DM_40 = 0x200; +/// Send to all DMs within distance 80m of oPC (floating text) +const int LOG_DM_80 = 0x400; +/// Make oPC whisper the message (chat message window) +const int LOG_WHISPER = 0x800; +/// Make oPC talk the message (chat message window) +const int LOG_TALK = 0x1000; +/// Make oPC shout the message (chat message window) +const int LOG_SHOUT = 0x2000; +/// Send to the server log file +const int LOG_TO_SERVER_LOG = 0x4000; +/// Send to the server log file with time stamp +const int LOG_TIME_SERVER_LOG = 0x8000; +/// Send to all DMs within distance 10m of oPC (server message window) +const int LOG_DM_10_SERVER = 0x10000; +/// Send to all DMs within distance 20m of oPC (server message window) +const int LOG_DM_20_SERVER = 0x20000; +/// Send to all DMs within distance 40m of oPC (server message window) +const int LOG_DM_40_SERVER = 0x40000; +/// Send to all DMs within distance 80m of oPC (server message window) +const int LOG_DM_80_SERVER = 0x80000; +/// Send to the oPC and all of their party members who percieve oPC (floating text) +const int LOG_PARTY_PERC = 0x100000; +/// Send to the oPC and all of their party members who percieve oPC (server message window) +const int LOG_PARTY_PERC_SERVER = 0x200000; +/// Send to the oPC and their nearby (10m) party members (floating text) +const int LOG_PARTY_10 = 0x400000; +/// Send to the oPC and their nearby (20m) party members (floating text) +const int LOG_PARTY_20 = 0x800000; +/// Send to the oPC and their nearby (40m) party members (floating text) +const int LOG_PARTY_40 = 0x1000000; +/// Send to the oPC and their nearby (80m) party members (floating text) +const int LOG_PARTY_80 = 0x2000000; +/// Send to all DMs as a server message +const int LOG_DM_ALL_SERVER = 0x4000000; +/// Send to all party EXCEPT for the player who triggered as a server message +const int LOG_PARTY_ONLY = 0x8000000; +/// Send to all party EXCEPT for the player *and people who can't see the player) who triggered as a server message +const int LOG_PARTY_PERC_ONLY = 0x10000000; + +// **************************************************************************** +// ** BACKWARDS COMPATIBILITY +// **************************************************************************** + +// These globals exist purely for backwards compatibility with older versions +// of LogMessage. They aren't recommended. + +/// Send only to the oPC who activated it (floating text) and the server log file +int LOG_FILE_PC = LOG_TO_SERVER_LOG | LOG_PC; +/// Send only to the oPC who activated it (server message window) and the server log file +int LOG_FILE_PC_SERVER = LOG_TO_SERVER_LOG | LOG_PC_SERVER; +/// Send to the oPC and all of their party members (floating text) and the server log file +int LOG_FILE_PARTY = LOG_TO_SERVER_LOG | LOG_PARTY; +/// Send to the oPC and all of their party members (server message window) and the server log file +int LOG_FILE_PARTY_SERVER = LOG_TO_SERVER_LOG | LOG_PARTY_SERVER; +/// Send to the oPC and their nearby (30m) faction members (floating text) and the server log file +int LOG_FILE_PARTY_30 = LOG_TO_SERVER_LOG | LOG_PARTY_30; +/// Send to the DM channel and the server log file +int LOG_FILE_DM_ALL = LOG_TO_SERVER_LOG | LOG_DM_ALL; +/// Send to all DMs within distance 10m of oPC and the server log file +int LOG_FILE_DM_10 = LOG_TO_SERVER_LOG | LOG_DM_10; +/// Send to all DMs within distance 20m of oPC and the server log file +int LOG_FILE_DM_20 = LOG_TO_SERVER_LOG | LOG_DM_20; +/// Send to all DMs within distance 40m of oPC and the server log file +int LOG_FILE_DM_40 = LOG_TO_SERVER_LOG | LOG_DM_40; +/// Send to all DMs within distance 80m of oPC and the server log file +int LOG_FILE_DM_80 = LOG_TO_SERVER_LOG | LOG_DM_80; +/// Make oPC whisper the message (chat message window) and the server log file +int LOG_FILE_WHISPER = LOG_TO_SERVER_LOG | LOG_WHISPER; +/// Make oPC talk the message (chat message window) and the server log file +int LOG_FILE_TALK = LOG_TO_SERVER_LOG | LOG_TALK; +/// Make oPC shout the message (chat message window) and the server log file +int LOG_FILE_SHOUT = LOG_TO_SERVER_LOG | LOG_SHOUT; + +// **************************************************************************** +// ** CONSTANTS +// **************************************************************************** + +// Set this to FALSE to include henchmen as part of the party for sending messages. +// Useful when trying to test MP messages when in SP. +const int LOG_MESSAGE_PARTY_PLAYERS_ONLY = TRUE; + +// Set this to TRUE to debug how messages are being decoded. +const int LOG_MESSAGE_DEBUG = FALSE; + +// Set this to TRUE to debug where messages are coming from. +const int LOG_MESSAGE_SOURCE_DEBUG = FALSE; + +// This can be used to display which version of LogMessage you are using. +const string LOG_MESSAGE_VERSION = "LogMessage v1.06"; + +// **************************************************************************** +// ** FUNCTION DECLARATIONS +// **************************************************************************** + +/** + * This function is used to a log a message in several different ways depending + * the value of iLogType. + * + * @param iLogType This is the level of logging to use. It should be one of the + * LOG_* constants. + * @param oPC This is the player who is triggering the log message. + * @param sMessage This is the message to be logged. + * @param sDMMessage This a message to be appended only for DM/log file messages. + * @param sLocalFilter The message will only be sent to people who have + * a LocalInt with this name and a specified value. + * @param iLocalValue The value the LocalInt must have. + */ +void LogMessage (int iLogType, object oPC, string sMessage, string sDMMessage = "", string sLocalFilter = "", int iLocalValue = 0); + +/** + * + * This function is used to send a message to every player on the server using the + * server message window. + * + * @param oPC A member of the party to send the message to. + * @param sMessage The message to send. + * @param sLocalFilter The message will only be sent to people who have + * a LocalInt with this name and a specified value. + * @param iLocalValue The value the LocalInt must have. + */ +void SendMessageToAllPCs (string sMessage, string sLocalFilter = "", int iLocalValue = 0); + +/** + * This function is used to send a message to every player in a party using the + * server message window. + * + * @param oPC A member of the party to send the message to. + * @param sMessage The message to send. + * @param bSkipOrigin Skip the player who originated the message. + * @param bPerceptionCheck If this is true, only send the message if the party member can see oPC. + * @param sLocalFilter The message will only be sent to people who have + * a LocalInt with this name and a specified value. + * @param iLocalValue The value the LocalInt must have. + */ +void SendMessageToParty (object oPC, string sMessage, int bSkipOrigin = FALSE, int bPerceptionCheck = FALSE, string sLocalFilter = "", int iLocalValue = 0); + +/** + * This function is used to send a message to every player in a party using + * floating text. + * + * @param oPC A member of the party to send the message to. + * @param sMessage The message to send. + * @param bPerceptionCheck If this is true, only send the message if the party member can see oPC. + * @param sLocalFilter The message will only be sent to people who have + * a LocalInt with this name and a specified value. + * @param iLocalValue The value the LocalInt must have. +void FloatingTextStringOnParty (object oPC, string sMessage, int bPerceptionCheck = FALSE, string sLocalFilter = "", int iLocalValue = 0); + +/** + * This function is used to send a message to every player in a party using + * floating text. + * + * @param oPC A member of the party to send the message to. + * @param iDistance The maximum distance the party member can be from oPC. + * @param sMessage The message to send. + * @param sLocalFilter The message will only be sent to people who have + * a LocalInt with this name and a specified value. + * @param iLocalValue The value the LocalInt must have. + */ +void FloatingTextStringOnPartyByDistance (object oPC, int iDistance, string sMessage, string sLocalFilter = "", int iLocalValue = 0); + +/** + * This function is used to send a message to all of the DMs near a particular + * location. + * + * @param lLocation The center of the sphere to search for DMs. + * @param iDistance The maximum distance the DM can be from lLocation. + * @param sMessage The message to send. + * @param bFloating If TRUE, send as a floating text string, if FALSE, send as a server message. + * @param sLocalFilter The message will only be sent to people who have + * a LocalInt with this name and a specified value. + * @param iLocalValue The value the LocalInt must have. + */ +void SendMessageToDMsByDistance (location lLocation, int iDistance, string sMessage, int bFloating = TRUE, string sLocalFilter = "", int iLocalValue = 0); + +// **************************************************************************** +// ** FUNCTION DEFINITIONS +// **************************************************************************** + +void SendMessageToAllPCs (string sMessage, string sLocalFilter = "", int iLocalValue = 0) +{ + object oPC = GetFirstPC(); + while (GetIsObjectValid(oPC)) + { + if ((sLocalFilter == "") || + (GetLocalInt(oPC, sLocalFilter) == iLocalValue)) + { + // Check for DM possessed NPCs. + if (!GetIsDM(GetMaster(oPC))) + SendMessageToPC(oPC, sMessage); + } + oPC = GetNextPC(); + } +} + +void SendMessageToParty (object oPC, string sMessage, int bSkipOrigin = FALSE, int bPerceptionCheck = FALSE, string sLocalFilter = "", int iLocalValue = 0) +{ + object oParty = GetFirstFactionMember(oPC, LOG_MESSAGE_PARTY_PLAYERS_ONLY); + while ( GetIsObjectValid(oParty) ) + { + if ( + // if filter is not set, or filter is true + ((sLocalFilter == "") || + (GetLocalInt(oParty, sLocalFilter) == iLocalValue)) && + // if perception check not set, or perception is true + ((bPerceptionCheck == FALSE) || + (GetObjectSeen(oPC, oParty) == TRUE)) && + // if bSkipOrigin is not set, or this is not the originating player + ((bSkipOrigin == FALSE) || + (oPC != oParty)) + ) + { + SendMessageToPC(oParty, sMessage); + } + oParty = GetNextFactionMember(oPC, LOG_MESSAGE_PARTY_PLAYERS_ONLY); + } + return; +} + +void FloatingTextStringOnParty (object oPC, string sMessage, int bPerceptionCheck = FALSE, string sLocalFilter = "", int iLocalValue = 0) +{ + object oParty = GetFirstFactionMember(oPC, LOG_MESSAGE_PARTY_PLAYERS_ONLY); + while ( GetIsObjectValid(oParty) ) + { + if ( + // if filter is not set, or filter is true + ((sLocalFilter == "") || + (GetLocalInt(oParty, sLocalFilter) == iLocalValue)) && + // if perception check not set, or perception is true + ((bPerceptionCheck == FALSE) || + (GetObjectSeen(oPC, oParty) == TRUE)) + ) { + FloatingTextStringOnCreature(sMessage, oParty, FALSE); + } + oParty = GetNextFactionMember(oPC, LOG_MESSAGE_PARTY_PLAYERS_ONLY); + } + return; +} + + +void FloatingTextStringOnPartyByDistance (object oPC, int iDistance, string sMessage, string sLocalFilter = "", int iLocalValue = 0) +{ + float fMaxDist = IntToFloat(iDistance); + object oParty = GetFirstFactionMember(oPC, LOG_MESSAGE_PARTY_PLAYERS_ONLY); + while ( GetIsObjectValid(oParty) ) + { + float fDistance = GetDistanceBetween(oPC, oParty); + if ( + // if filter is not set, or filter is true + ( (sLocalFilter == "") || + (GetLocalInt(oParty, sLocalFilter) == iLocalValue) ) && + // Check that the party member is close enough to the player + ( (oParty == oPC) || + ( (fDistance > 0.0) && (fDistance <= fMaxDist) ) ) + ) + { + FloatingTextStringOnCreature(sMessage, oParty, FALSE); + } + oParty = GetNextFactionMember(oPC, LOG_MESSAGE_PARTY_PLAYERS_ONLY); + } +} + +void SendMessageToDMsByDistance (location lLocation, int iDistance, string sMessage, int bFloating = TRUE, string sLocalFilter = "", int iLocalValue = 0) +{ + object oDM; + // If distance is zero, then send the message to all DMs on the server + if (iDistance == 0) + { + oDM = GetFirstPC(); + while (GetIsObjectValid(oDM)) + { + if ((sLocalFilter == "") || + (GetLocalInt(oDM, sLocalFilter) == iLocalValue)) + { + // Note: DM possessed NPCs do not return TRUE for GetIsPC anymore. + if ( GetIsDM(oDM) || GetIsDMPossessed(oDM) ) + { + if (bFloating) FloatingTextStringOnCreature(sMessage, oDM, FALSE); + else SendMessageToPC(oDM, sMessage); + } + } + oDM = GetNextPC(); + } + return; + } + // Normal operation + float fSize = IntToFloat(iDistance); + // Using ObjectInShape with CREATURE only catches DMs possessing NPCs but not DM avatars. + oDM = GetFirstObjectInShape (SHAPE_SPHERE, fSize, lLocation, FALSE, OBJECT_TYPE_ALL); + while ( GetIsObjectValid(oDM) ) + { + if ((sLocalFilter == "") || + (GetLocalInt(oDM, sLocalFilter) == iLocalValue)) + { + if ( GetIsDM(oDM) || GetIsDMPossessed(oDM) ) + { + if (bFloating) FloatingTextStringOnCreature(sMessage, oDM, FALSE); + else SendMessageToPC(oDM, sMessage); + } + } + oDM = GetNextObjectInShape (SHAPE_SPHERE, fSize, lLocation, FALSE, OBJECT_TYPE_ALL); + } + return; +} + +void LogMessage (int iLogType, object oPC, string sMessage, string sDMMessage = "", string sLocalFilter = "", int iLocalValue = 0) +{ + + object oParty; + object oArea; + object oDM; + int iDistance = 0; + + if (LOG_MESSAGE_SOURCE_DEBUG) SpeakString("Debug: "+GetName(OBJECT_SELF)+" in area: "+GetName(GetArea(OBJECT_SELF))+" is originating "+IntToHexString(iLogType)+" type log message: "+sMessage, TALKVOLUME_SHOUT); + + // Handle the various different types of log levels. + + // Messages that do not require oPC to be valid. + if (iLogType & LOG_DISABLED) + return; + + if (iLogType & LOG_PC_ALL) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PC_ALL"); + // Does not use the sLocalFilter because oPC does not have to + // be valid. + SendMessageToAllPCs(sMessage, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_ALL) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_ALL"); + // Does not use the sLocalFilter because oPC does not have to + // be valid. + SendMessageToAllDMs(sMessage + " " + sDMMessage); + } + + if (iLogType & LOG_DM_ALL_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_ALL_SERVER"); + // Does not use the sLocalFilter because oPC does not have to + // be valid. + SendMessageToDMsByDistance (GetLocation(OBJECT_SELF), 0, sMessage + " " + sDMMessage, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_TO_SERVER_LOG) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_TO_SERVER_LOG"); + // Does not use the sLocalFilter because oPC does not have to + // be valid. + PrintString(sMessage + " " + sDMMessage); + } + + if (iLogType & LOG_TIME_SERVER_LOG) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_TIME_SERVER_LOG"); + // Does not use the sLocalFilter because oPC does not have to + // be valid. + WriteTimestampedLogEntry(sMessage + " " + sDMMessage); + } + + // Messages that do require oPC to be valid. + if (GetIsObjectValid(oPC)) + { + if (iLogType & LOG_PC) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PC"); + if ((sLocalFilter == "") || + (GetLocalInt(oPC, sLocalFilter) == iLocalValue)) + { + FloatingTextStringOnCreature(sMessage, oPC, FALSE); + } + } + + if (iLogType & LOG_PC_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PC_SERVER"); + if ((sLocalFilter == "") || + (GetLocalInt(oPC, sLocalFilter) == iLocalValue)) + { + SendMessageToPC(oPC, sMessage); + } + } + + if (iLogType & LOG_PARTY) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY"); + FloatingTextStringOnParty(oPC, sMessage, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_ONLY) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_ONLY"); + SendMessageToParty(oPC, sMessage, TRUE, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_10) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_10"); + FloatingTextStringOnPartyByDistance(oPC, 10, sMessage, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_20) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_20"); + FloatingTextStringOnPartyByDistance(oPC, 20, sMessage, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_40) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_40"); + FloatingTextStringOnPartyByDistance(oPC, 40, sMessage, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_80) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_80"); + FloatingTextStringOnPartyByDistance(oPC, 80, sMessage, sLocalFilter, iLocalValue); + } + + + if (iLogType & LOG_PARTY_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_SERVER"); + SendMessageToParty(oPC, sMessage, FALSE, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_PERC) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_PERC"); + FloatingTextStringOnParty(oPC, sMessage, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_PERC_ONLY) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_ONLY_PERC"); + SendMessageToParty(oPC, sMessage, TRUE, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_PERC_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_PERC_SERVER"); + SendMessageToParty(oPC, sMessage, FALSE, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_PARTY_30) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_PARTY_30"); + // Note: does not use sLocalFilter + FloatingTextStringOnCreature(sMessage, oPC, TRUE); + } + + if (iLogType & LOG_DM_10) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_10"); + SendMessageToDMsByDistance(GetLocation(oPC), 10, sMessage + " " + sDMMessage, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_20) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_20"); + SendMessageToDMsByDistance(GetLocation(oPC), 20, sMessage + " " + sDMMessage, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_40) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_40"); + SendMessageToDMsByDistance(GetLocation(oPC), 40, sMessage + " " + sDMMessage, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_80) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_80"); + SendMessageToDMsByDistance(GetLocation(oPC), 80, sMessage + " " + sDMMessage, TRUE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_10_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_10_SERVER"); + SendMessageToDMsByDistance(GetLocation(oPC), 10, sMessage + " " + sDMMessage, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_20_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_20_SERVER"); + SendMessageToDMsByDistance(GetLocation(oPC), 20, sMessage + " " + sDMMessage, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_40_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_40_SERVER"); + SendMessageToDMsByDistance(GetLocation(oPC), 40, sMessage + " " + sDMMessage, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_DM_80_SERVER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_DM_80_SERVER"); + SendMessageToDMsByDistance(GetLocation(oPC), 80, sMessage + " " + sDMMessage, FALSE, sLocalFilter, iLocalValue); + } + + if (iLogType & LOG_WHISPER) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_WHISPER"); + if ((sLocalFilter == "") || + (GetLocalInt(oPC, sLocalFilter) == iLocalValue)) + { + AssignCommand(oPC, ActionSpeakString(sMessage, TALKVOLUME_WHISPER)); + } + } + + if (iLogType & LOG_TALK) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_TALK"); + if ((sLocalFilter == "") || + (GetLocalInt(oPC, sLocalFilter) == iLocalValue)) + { + AssignCommand(oPC, ActionSpeakString(sMessage, TALKVOLUME_TALK)); + } + } + + if (iLogType & LOG_SHOUT) + { + if (LOG_MESSAGE_DEBUG) SendMessageToPC(oPC, "LOG_SHOUT"); + if ((sLocalFilter == "") || + (GetLocalInt(oPC, sLocalFilter) == iLocalValue)) + { + AssignCommand(oPC, ActionSpeakString(sMessage, TALKVOLUME_SHOUT)); + } + } + } + return; +} + diff --git a/src/include/inc_lookups.nss b/src/include/inc_lookups.nss new file mode 100644 index 0000000..e5a5124 --- /dev/null +++ b/src/include/inc_lookups.nss @@ -0,0 +1,598 @@ +/* + + This file is used for lookup functions for psionics and newspellbooks + It is supposed to reduce the need for large loops that may result in + TMI errors. + It does this by creating arrays in advance and the using those as direct + lookups. +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + + +void MakeLookupLoop(int nClass, string sFile, int nMin, int nMax, int nLoopSize = 100, string sTemp = ""); +void SetupLookupStage(object oMod, int n); + + +//this returns the real SpellID of "wrapper" spells cast by psionic or the new spellbooks +int GetPowerFromSpellID(int nSpellID); + +/** + * Maps spells.2da rows of the class-specific entries to corresponding cls_psipw_*.2da rows. + * + * @param nSpellID Spells.2da row to determine cls_psipw_*.2da row for + * @return The mapped value + */ +int GetPowerfileIndexFromSpellID(int nSpellID); + +/** + * Maps spells.2da rows of the real entries to corresponding cls_psipw_*.2da rows. + * + * @param nSpellID Spells.2da row to determine cls_psipw_*.2da row for + * @return The mapped value + */ +int GetPowerfileIndexFromRealSpellID(int nRealSpellID); + +//this retuns the featID of the class-specific feat for a spellID +//useful for psionics GetHasPower function +int GetClassFeatFromPower(int nPowerID, int nClass); + +/** + * Determines cls_spell_*.2da index from a given new spellbook class-specific + * spell spells.2da index. + * + * @param nSpell The class-specific spell to find cls_spell_*.2da index for + * @return The cls_spell_*.2da index in whichever class's file that the + * given spell belongs to. + * If the spell at nSpell isn't a newspellbook class-specific spell, + * returns -1 instead. + */ +int SpellToSpellbookID(int nSpell); + +/** + * Determines cls_spell_*.2da index from a given spells.2da index. + * + * @param nClass The class in whose spellbook to search in + * @param nSpell The spell to search for + * @return The cls_spell_*.2da index in whichever class's file that the + * given spell belongs to. + * If nSpell does not exist within the spellbook, + * returns -1 instead. + */ +int RealSpellToSpellbookID(int nClass, int nSpell); + +/** + * Determines number of metamagics from a given spells.2da index. + * + * @param nClass The class in whose spellbook to search in + * @param nSpell The spell to search for + * @return The number of metamagics in cls_spell_*.2da + * for a particular spell. + */ +int RealSpellToSpellbookIDCount(int nClass, int nSpell); + +/** + * Determines the name of the 2da file that defines the number of alternate magic + * system powers/spells/whathaveyou known, maximum level of such known and + * number of uses at each level of the given class. And possibly related things + * that apply to that specific system. + * + * @param nClass CLASS_TYPE_* of the class to determine the powers known 2da name of + * @return The name of the given class's powers known 2da + */ +string GetAMSKnownFileName(int nClass); + +/** + * Determines the name of the 2da file that lists the powers/spells/whathaveyou + * on the given class's list of such. + * + * @param nClass CLASS_TYPE_* of the class to determine the power list 2da name of + * @return The name of the given class's power list 2da + */ +string GetAMSDefinitionFileName(int nClass); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_2dacache" +#include "prc_inc_array" +#include "prc_class_const" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +object _inc_lookups_GetCacheObject(string sTag) +{ + object oWP = GetObjectByTag(sTag); + if(!GetIsObjectValid(oWP)) + { + object oChest = GetObjectByTag("Bioware2DACache"); + if(!GetIsObjectValid(oChest)) + { + //has to be an object, placeables cant go through the DB + oChest = CreateObject(OBJECT_TYPE_CREATURE, "prc_2da_cache", + GetLocation(GetObjectByTag("HEARTOFCHAOS")), FALSE, "Bioware2DACache"); + } + if(!GetIsObjectValid(oChest)) + { + //has to be an object, placeables cant go through the DB + oChest = CreateObject(OBJECT_TYPE_CREATURE, "prc_2da_cache", + GetStartingLocation(), FALSE, "Bioware2DACache"); + } + + int nContainer = 0; + string sContainerName = "Bio2DACacheTokenContainer_Lkup_"; + object oContainer = GetObjectByTag(sContainerName + IntToString(nContainer)); + + // Find last existing container + if(GetIsObjectValid(oContainer)) + { + nContainer = GetLocalInt(oContainer, "ContainerCount"); + oContainer = GetObjectByTag(sContainerName + IntToString(nContainer)); + + // Make sure it's not full + if(GetLocalInt(oContainer, "NumTokensInside") >= 34) // Container has 35 slots. Attempt to not use them all, just in case + { + oContainer = OBJECT_INVALID; + ++nContainer; // new container is 1 higher than last one + } + } + + // We need to create a container + if(!GetIsObjectValid(oContainer)) + { + oContainer = CreateObject(OBJECT_TYPE_ITEM, "nw_it_contain001", GetLocation(oChest), FALSE, sContainerName + IntToString(nContainer)); + DestroyObject(oContainer); + oContainer = CopyObject(oContainer, GetLocation(oChest), oChest, sContainerName + IntToString(nContainer)); + // store the new number of containers in this series + if(nContainer) + SetLocalInt(GetObjectByTag(sContainerName + "0"), "ContainerCount", nContainer); + // else this is the first container - do nothing as this is the same as storing 0 on it. + // Also here we still have 2 objects with the same tag so above code may get + // the object destroyed at the end of the function if this is the first container. + } + + // Create the new token + oWP = CreateItemOnObject("hidetoken", oContainer, 1, sTag); + + // Increment token count tracking variable + SetLocalInt(oContainer, "NumTokensInside", GetLocalInt(oContainer, "NumTokensInside") + 1); + } + + if(!GetIsObjectValid(oWP)) + { + DoDebug("ERROR: Failed to create lookup storage token for " + sTag); + return OBJECT_INVALID; + } + + return oWP; +} + +void SetLkupStage(int nStage, object oModule, int nClass, string sFile) +{ + SetLocalInt(oModule, "PRCLookup_Stage", nStage + 1); + SetLocalInt(oModule, "PRCLookup_Class", nClass); + SetLocalString(oModule, "PRCLookup_File", sFile); +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void RunLookupLoop() +{ + // check if we run lookup before + if(GetXP(GetObjectByTag("Bioware2DACache")) & 0x01) + { + if (DEBUG) DoDebug("RunLookupLoop() - marker found - exiting"); + return; + } + + object oModule = GetModule(); + SetupLookupStage(oModule, 1); + int nClass = GetLocalInt(oModule, "PRCLookup_Class"); + string sFile = GetLocalString(oModule, "PRCLookup_File"); + if (DEBUG) DoDebug("RunLookupLoop(): Looking in "+sFile+" for nClass "+IntToString(nClass)); + MakeLookupLoop(nClass, sFile, 1, PRCGetDynamicFileEnd(sFile)); +} + +void RunNextLoop() +{ + object oModule = GetModule(); + int nStage = GetLocalInt(oModule, "PRCLookup_Stage"); + SetupLookupStage(oModule, nStage); + int nClass = GetLocalInt(oModule, "PRCLookup_Class"); + if(nClass) + { + string sFile = GetLocalString(oModule, "PRCLookup_File"); + if (DEBUG) DoDebug("RunNextLoop(): Looking in "+sFile+" for nClass "+IntToString(nClass)); + MakeLookupLoop(nClass, sFile, 1, PRCGetDynamicFileEnd(sFile)); + } + else + { + DeleteLocalInt(oModule, "PRCLookup_Stage"); + DeleteLocalInt(oModule, "PRCLookup_Class"); + DeleteLocalString(oModule, "PRCLookup_File"); + + //mark lookup as done, tell hb to save the DB + object oCache = GetObjectByTag("Bioware2DACache"); + SetXP(oCache, GetXP(oCache) | 0x01); + SetLocalInt(oModule, "Bioware2dacacheCount", GetPRCSwitch(PRC_USE_BIOWARE_DATABASE) - 5); + } +} + +void SetupLookupStage(object oMod, int n) +{ + switch(n) + { + case 1: SetLkupStage(n, oMod, CLASS_TYPE_PSION, "cls_psipw_psion"); break; + case 2: SetLkupStage(n, oMod, CLASS_TYPE_PSYWAR, "cls_psipw_psywar"); break; + case 3: SetLkupStage(n, oMod, CLASS_TYPE_WILDER, "cls_psipw_wilder"); break; + case 4: SetLkupStage(n, oMod, CLASS_TYPE_FIST_OF_ZUOKEN, "cls_psipw_foz"); break; + case 5: SetLkupStage(n, oMod, CLASS_TYPE_WARMIND, "cls_psipw_warmnd"); break; + case 6: SetLkupStage(n, oMod, CLASS_TYPE_TRUENAMER, "cls_true_utter"); break; + case 7: SetLkupStage(n, oMod, CLASS_TYPE_CRUSADER, "cls_move_crusdr"); break; + case 8: SetLkupStage(n, oMod, CLASS_TYPE_SWORDSAGE, "cls_move_swdsge"); break; + case 9: SetLkupStage(n, oMod, CLASS_TYPE_WARBLADE, "cls_move_warbld"); break; + case 10: SetLkupStage(n, oMod, CLASS_TYPE_DRAGONFIRE_ADEPT, "cls_inv_dfa"); break; + case 11: SetLkupStage(n, oMod, CLASS_TYPE_DRAGON_SHAMAN, "cls_inv_drgshm"); break; + case 12: SetLkupStage(n, oMod, CLASS_TYPE_WARLOCK, "cls_inv_warlok"); break; + case 13: SetLkupStage(n, oMod, CLASS_TYPE_ARCHIVIST, "cls_spell_archv"); break; + case 14: SetLkupStage(n, oMod, CLASS_TYPE_ASSASSIN, "cls_spell_asasin"); break; + case 15: SetLkupStage(n, oMod, CLASS_TYPE_BARD, "cls_spell_bard"); break; + case 16: SetLkupStage(n, oMod, CLASS_TYPE_BEGUILER, "cls_spell_beguil"); break; + case 17: SetLkupStage(n, oMod, CLASS_TYPE_DREAD_NECROMANCER, "cls_spell_dnecro"); break; + case 18: SetLkupStage(n, oMod, CLASS_TYPE_DUSKBLADE, "cls_spell_duskbl"); break; + case 19: SetLkupStage(n, oMod, CLASS_TYPE_FAVOURED_SOUL, "cls_spell_favsol"); break; + case 20: SetLkupStage(n, oMod, CLASS_TYPE_HARPER, "cls_spell_harper"); break; + case 21: SetLkupStage(n, oMod, CLASS_TYPE_HEXBLADE, "cls_spell_hexbl"); break; + case 22: SetLkupStage(n, oMod, CLASS_TYPE_JUSTICEWW, "cls_spell_justww"); break; + case 23: SetLkupStage(n, oMod, CLASS_TYPE_SORCERER, "cls_spell_sorc"); break; + case 24: SetLkupStage(n, oMod, CLASS_TYPE_SUBLIME_CHORD, "cls_spell_schord"); break; + case 25: SetLkupStage(n, oMod, CLASS_TYPE_SUEL_ARCHANAMACH, "cls_spell_suel"); break; + case 26: SetLkupStage(n, oMod, CLASS_TYPE_VIGILANT, "cls_spell_vigil"); break; + case 27: SetLkupStage(n, oMod, CLASS_TYPE_WARMAGE, "cls_spell_wrmage"); break; + case 28: SetLkupStage(n, oMod, CLASS_TYPE_KNIGHT_WEAVE, "cls_spell_kngtwv"); break; + case 29: SetLkupStage(n, oMod, CLASS_TYPE_PSYCHIC_ROGUE, "cls_psipw_psyrog"); break; + case 30: SetLkupStage(n, oMod, CLASS_TYPE_SHADOWCASTER, "cls_myst_shdcst"); break; + case 31: SetLkupStage(n, oMod, CLASS_TYPE_SHADOWSMITH, "cls_myst_shdsmt"); break; + case 32: SetLkupStage(n, oMod, CLASS_TYPE_CELEBRANT_SHARESS, "cls_spell_sharss"); break; + //case 46: SetLkupStage(n, oMod, CLASS_TYPE_CULTIST_SHATTERED_PEAK, "cls_spell_cultst"); break; + //case 40: SetLkupStage(n, oMod, CLASS_TYPE_NENTYAR_HUNTER, "cls_spell_hunter"); break; + //case 28: SetLkupStage(n, oMod, CLASS_TYPE_SHADOWLORD, "cls_spell_tfshad"); break; + //case 29: SetLkupStage(n, oMod, CLASS_TYPE_SLAYER_OF_DOMIEL, "cls_spell_sod"); break; + //case 29: SetLkupStage(n, oMod, CLASS_TYPE_SOHEI, "cls_spell_sohei"); break; + //case 33: SetLkupStage(n, oMod, CLASS_TYPE_VASSAL, "cls_spell_vassal"); break; + //case 17: SetLkupStage(n, oMod, CLASS_TYPE_BLACKGUARD, "cls_spell_blkgrd"); break; + //case 24: SetLkupStage(n, oMod, CLASS_TYPE_KNIGHT_CHALICE, "cls_spell_kchal"); break; + //case 25: SetLkupStage(n, oMod, CLASS_TYPE_KNIGHT_MIDDLECIRCLE, "cls_spell_kotmc"); break; + //case 26: SetLkupStage(n, oMod, CLASS_TYPE_SOLDIER_OF_LIGHT, "cls_spell_sol"); break; + //case 24: SetLkupStage(n, oMod, CLASS_TYPE_OCULAR, "cls_spell_ocu"); break; + //case 30: SetLkupStage(n, oMod, CLASS_TYPE_BLIGHTER, "cls_spell_blight"); break; + //case 21: SetLkupStage(n, oMod, CLASS_TYPE_HEALER, "cls_spell_healer"); break; + //case 23: SetLkupStage(n, oMod, CLASS_TYPE_SHAMAN, "cls_spell_shaman"); break; + + default: SetLkupStage(n, oMod, 0, ""); break; + } +} +/* +void LookupSpells(int nRealSpellID, string sClass, string sLevel); +{ + int nDescriptor = Get2DACache("prc_spells", "Descriptor", nRealSpellID);//should already be in cache, just read the value + +DESCRIPTOR_ACID +DESCRIPTOR_AIR +DESCRIPTOR_COLD +DESCRIPTOR_LIGHT +DESCRIPTOR_ELECTRICITY +DESCRIPTOR_DARKNESS +DESCRIPTOR_FIRE +DESCRIPTOR_SONIC + + +SUBSCHOOL_HEALING +SUBSCHOOL_SUMMONING +SUBSCHOOL_POLYMORPH + +SPELL_SCHOOL_ENCHANTMENT +SPELL_SCHOOL_NECROMANCY +SPELL_SCHOOL_ABJURATION + + string sLevel = Get2DACache("spells", "Cleric", nRealSpellID); + if(sLevel != "") + { + oLevelToken = _inc_lookups_GetCacheObject("SpellLvl_2_Level_"+sLevel); + // check if array exist, if not create one + if(!array_exists(oLevelToken, "Lkup")) + array_create(oLevelToken, "Lkup"); + array_set_int(oLevelToken, "Lkup", array_get_size(oLevelToken, "Lkup"), i); + } + sLevel = Get2DACache("spells", "Druid", nRealSpellID); + if(sLevel != "") + { + oLevelToken = _inc_lookups_GetCacheObject("SpellLvl_3_Level_"+sLevel); + // check if array exist, if not create one + if(!array_exists(oLevelToken, "Lkup")) + array_create(oLevelToken, "Lkup"); + array_set_int(oLevelToken, "Lkup", array_get_size(oLevelToken, "Lkup"), i); + } + sLevel = Get2DACache("spells", "Paladin", nRealSpellID); + if(sLevel != "") + { + oLevelToken = _inc_lookups_GetCacheObject("SpellLvl_6_Level_"+sLevel); + // check if array exist, if not create one + if(!array_exists(oLevelToken, "Lkup")) + array_create(oLevelToken, "Lkup"); + array_set_int(oLevelToken, "Lkup", array_get_size(oLevelToken, "Lkup"), i); + } + sLevel = Get2DACache("spells", "Ranger", nRealSpellID); + if(sLevel != "") + { + oLevelToken = _inc_lookups_GetCacheObject("SpellLvl_7_Level_"+sLevel); + // check if array exist, if not create one + if(!array_exists(oLevelToken, "Lkup")) + array_create(oLevelToken, "Lkup"); + array_set_int(oLevelToken, "Lkup", array_get_size(oLevelToken, "Lkup"), i); + } + sLevel = Get2DACache("spells", "Wiz_Sorc", nRealSpellID); + if(sLevel != "") + { + oLevelToken = _inc_lookups_GetCacheObject("SpellLvl_10_Level_"+sLevel); + // check if array exist, if not create one + if(!array_exists(oLevelToken, "Lkup")) + array_create(oLevelToken, "Lkup"); + array_set_int(oLevelToken, "Lkup", array_get_size(oLevelToken, "Lkup"), i); + } + +*/ + + +void MakeLookupLoop(int nClass, string sFile, int nMin, int nMax, int nLoopSize = 100, string sTemp = "") +{ + // Tell the function to skip before reaching nMax + int bSkipLoop = FALSE; + int i; + + if(DEBUG) DoDebug("MakeLookupLoop(" + +IntToString(nClass)+", " + +sFile+", " + +IntToString(nMin)+", " + +IntToString(nMax)+", " + +IntToString(nLoopSize)+", " + +") : sTemp = "+sTemp); + + // psionic, tob, truenameing and ivocation using classes have slightly different handling + // new AMS classes should be added here + int bAMS = FALSE; + + if(nClass == CLASS_TYPE_PSION + || nClass == CLASS_TYPE_PSYWAR + || nClass == CLASS_TYPE_PSYCHIC_ROGUE + || nClass == CLASS_TYPE_WILDER + || nClass == CLASS_TYPE_FIST_OF_ZUOKEN + || nClass == CLASS_TYPE_WARMIND + || nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_WARBLADE + || nClass == CLASS_TYPE_TRUENAMER + || nClass == CLASS_TYPE_SHADOWCASTER + || nClass == CLASS_TYPE_SHADOWSMITH + || nClass == CLASS_TYPE_DRAGONFIRE_ADEPT + || nClass == CLASS_TYPE_DRAGON_SHAMAN + || nClass == CLASS_TYPE_WARLOCK) + bAMS = TRUE; + + object oSpellIDToken = bAMS ? _inc_lookups_GetCacheObject("PRC_SpellIDToClsPsipw"): + _inc_lookups_GetCacheObject("PRC_GetRowFromSpellID"); + + // Failed to obtain a valid token - nothing to store on + if(!GetIsObjectValid(oSpellIDToken)) + bSkipLoop = TRUE; + + // Starting a new run and the data is present already. Assume the whole thing is present and abort + if(nMin == 1 + && GetLocalInt(oSpellIDToken, Get2DACache(sFile, "SpellID", 1))) + { + if(DEBUG) DoDebug("MakeLookupLoop("+sFile+") restored from database"); + bSkipLoop = TRUE; + } + + if(!bSkipLoop) + { + string sClass = IntToString(nClass); + + object oLevelToken; + object oPowerToken = _inc_lookups_GetCacheObject("PRC_GetPowerFromSpellID"); + object oRealSpellIDToken = bAMS ? _inc_lookups_GetCacheObject("PRC_GetClassFeatFromPower_"+sClass): + _inc_lookups_GetCacheObject("PRC_GetRowFromRealSpellID"); + + int nRealSpellID, nFeatID, nCount; + string sSpellID, sRealSID; + for(i = nMin; i < nMin + nLoopSize; i++) + { + // None of the relevant 2da files have blank Label entries on rows other than 0. We can assume i is greater than 0 at this point + if(Get2DAString(sFile, "Label", i) == "") // Using Get2DAString() instead of Get2DACache() to avoid caching useless data + { + bSkipLoop = TRUE; + break;// exit the loop + } + + sSpellID = Get2DACache(sFile, "SpellID", i); + sRealSID = Get2DACache(sFile, "RealSpellID", i); + nRealSpellID = StringToInt(sRealSID); + + //GetPowerfileIndexFromSpellID + //SpellToSpellbookID + SetLocalInt(oSpellIDToken, sSpellID, i); + + //GetPowerfileIndexFromRealSpellID + SetLocalInt(oSpellIDToken, sRealSID, i); + + //GetPowerFromSpellID + SetLocalInt(oPowerToken, sSpellID, nRealSpellID); + + //Spell level lookup + if(!bAMS || nClass == CLASS_TYPE_WARLOCK) + { + string sReqFt = bAMS ? "" : Get2DACache(sFile, "ReqFeat", i);// Only new spellbooks have the ReqFeat column. No sense in caching it for other stuff + if(sReqFt == "") + { + string sLevel = Get2DACache(sFile, "Level", i); + oLevelToken = _inc_lookups_GetCacheObject("SpellLvl_"+sClass+"_Level_"+sLevel); + // check if array exist, if not create one + if(!array_exists(oLevelToken, "Lkup")) + array_create(oLevelToken, "Lkup"); + + array_set_int(oLevelToken, "Lkup", array_get_size(oLevelToken, "Lkup"), i); + + //LookupSpellDescriptor(nRealSpellID, sClass, sLevel); + } + } + + //GetClassFeatFromPower + if(bAMS) + { + nFeatID = StringToInt(Get2DACache(sFile, "FeatID", i)); + if(nFeatID != 0) + { + SetLocalInt(oRealSpellIDToken, sRealSID, nFeatID); + } + } + //RealSpellToSpellbookID + //RealSpellToSpellbookIDCount + else + { + if(sRealSID == sTemp) + { + nCount += 1; + continue; + } + else + { + SetLocalInt(oRealSpellIDToken, sClass+"_"+sTemp+"_Count", nCount); + SetLocalInt(oRealSpellIDToken, sClass+"_"+sRealSID+"_Start", i); + sTemp = sRealSID; + nCount = 0; + } + } + } + } + + // And delay continuation to avoid TMI + if(i < nMax && !bSkipLoop) + DelayCommand(0.0, MakeLookupLoop(nClass, sFile, i, nMax, nLoopSize, sTemp)); + else + DelayCommand(0.0, RunNextLoop()); +} + +int GetPowerFromSpellID(int nSpellID) +{ + object oWP = GetObjectByTag("PRC_GetPowerFromSpellID"); + int nPower = GetLocalInt(oWP, /*"PRC_GetPowerFromSpellID_" + */IntToString(nSpellID)); + if(nPower == 0) + nPower = -1; + return nPower; +} + +int GetPowerfileIndexFromSpellID(int nSpellID) +{ + object oWP = GetObjectByTag("PRC_SpellIDToClsPsipw"); + int nIndex = GetLocalInt(oWP, /*"PRC_SpellIDToClsPsipw_" + */IntToString(nSpellID)); + return nIndex; +} + +int GetPowerfileIndexFromRealSpellID(int nRealSpellID) +{ + object oWP = GetObjectByTag("PRC_SpellIDToClsPsipw"); + int nIndex = GetLocalInt(oWP, /*"PRC_SpellIDToClsPsipw_" + */IntToString(nRealSpellID)); + return nIndex; +} + +int GetClassFeatFromPower(int nPowerID, int nClass) +{ + string sLabel = "PRC_GetClassFeatFromPower_" + IntToString(nClass); + object oWP = GetObjectByTag(sLabel); + int nPower = GetLocalInt(oWP, /*sLabel+"_" + */IntToString(nPowerID)); + if(nPower == 0) + nPower = -1; + return nPower; +} + +int SpellToSpellbookID(int nSpell) +{ + object oWP = GetObjectByTag("PRC_GetRowFromSpellID"); + int nOutSpellID = GetLocalInt(oWP, /*"PRC_GetRowFromSpellID_" + */IntToString(nSpell)); + if(nOutSpellID == 0) + nOutSpellID = -1; + //if(DEBUG) DoDebug("SpellToSpellbookID(" + IntToString(nSpell) + ", " + sFile + ") = " + IntToString(nOutSpellID)); + return nOutSpellID; +} + +int RealSpellToSpellbookID(int nClass, int nSpell) +{ + object oWP = GetObjectByTag("PRC_GetRowFromRealSpellID"); + int nOutSpellID = GetLocalInt(oWP, IntToString(nClass) + "_" + IntToString(nSpell) + "_Start"); + if(nOutSpellID == 0) + nOutSpellID = -1; + return nOutSpellID; +} + +int RealSpellToSpellbookIDCount(int nClass, int nSpell) +{ + return GetLocalInt(GetObjectByTag("PRC_GetRowFromRealSpellID"), IntToString(nClass) + "_" + IntToString(nSpell) + "_Count"); +} + +string GetAMSKnownFileName(int nClass) +{ + // Get the class-specific base + string sFile = Get2DACache("classes", "FeatsTable", nClass); + + // Various naming schemes based on system + if(nClass == CLASS_TYPE_TRUENAMER) + sFile = "cls_true_known"; + // ToB + else if(nClass == CLASS_TYPE_CRUSADER || nClass == CLASS_TYPE_SWORDSAGE || nClass == CLASS_TYPE_WARBLADE) + sFile = "cls_mvkn" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + // Shadowcasting + else if(nClass == CLASS_TYPE_SHADOWCASTER || nClass == CLASS_TYPE_SHADOWSMITH) + sFile = "cls_mykn" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + // Invocations + else if(nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || nClass == CLASS_TYPE_WARLOCK || nClass == CLASS_TYPE_DRAGON_SHAMAN) + sFile = "cls_invkn" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + // Assume psionics if no other match + else + sFile = "cls_psbk" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + + return sFile; +} + +string GetAMSDefinitionFileName(int nClass) +{ + // Get the class-specific base + string sFile = Get2DACache("classes", "FeatsTable", nClass); + + // Various naming schemes based on system + if(nClass == CLASS_TYPE_TRUENAMER) + sFile = "cls_true_utter"; + // ToB + else if(nClass == CLASS_TYPE_CRUSADER || nClass == CLASS_TYPE_SWORDSAGE || nClass == CLASS_TYPE_WARBLADE) + sFile = "cls_move" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + // Shadowcasting + else if(nClass == CLASS_TYPE_SHADOWCASTER || nClass == CLASS_TYPE_SHADOWSMITH) + sFile = "cls_myst" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + // Invoc + else if(nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || nClass == CLASS_TYPE_WARLOCK || nClass == CLASS_TYPE_DRAGON_SHAMAN) + sFile = "cls_inv" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + // Assume psionics if no other match + else + sFile = "cls_psipw" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061210 + + return sFile; +} + +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/inc_metalocation.nss b/src/include/inc_metalocation.nss new file mode 100644 index 0000000..15a9751 --- /dev/null +++ b/src/include/inc_metalocation.nss @@ -0,0 +1,618 @@ +//:://///////////////////////////////////////////// +//:: Metalocation include +//:: inc_metalocation +//::////////////////////////////////////////////// +/** @file + A metalocation is intended for reliable (independent + of object locations in memory) storage of location + data across module boundaries for possible eventual + re-entry to the same module. For example, to + carry location data over server resets. + + This file specifies the metalocation structure, + which contains data equivalent to a standard + location, and in addition name of the module the + metalocation resides in and, if defined, the name + of the metalocation. + The area reference is built of two strings and + two integers instead of a memory pointer. + First string specifies the tag of the area containing + the metalocation. The second string specifies the name + of the area and the integers specify it's height and + width. They are used obtain exact match in cases where + there are several areas with the same tag. + + The metalocation invariant: + All valid metalocations are such that they may be + uniquely matched to a location. + + That is, a valid metalocation is one where the area + can be identified will full certainty using the + tag, name, height and width of the area. + + + In addition, this file contains a group of functions + for abstracted handling of metalocation data. + + + @author Ornedan + @date Created - 23.05.2005 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * A dismantled version of location with some metadata attached. + * Intended for use of persistent storage of location data over + * module boundaries. For example over server resets + */ +struct metalocation{ + /// Tag of the area the location is in. + string sAreaTag; + /* + /// Resref of the area the location is in. Used to differentiate between + /// areas with the same tag. + string sAreaResRef; + */ + /// Name of the area. Used to differentiate between areas with the same tag. + string sAreaName; + /// Height of the area, in tiles. Used if tag and name are not enough to + /// uniquely identify an area. + int nAreaHeight; + /// Width of the area, in tiles. Used if tag and name are not enough to + /// uniquely identify an area. + int nAreaWidth; + /// The X coordinate of the location within the area. + float fX; + /// The Y coordinate of the location within the area. + float fY; + /// The Z coordinate of the location within the area. + float fZ; + /// The direction the location is facing. + float fFacing; + /// The metalocation may be named and the name is stored in this member. + string sName; + /// Name of the module containing the location. + string sModule; + }; + +/** + * Converts a standard location to equivalent metalocation. If area of the + * location cannot be uniquely identified using it's tag, name, height and + * width, a null metalocation is returned to preserve invariant of all + * valid metalocations being uniquely identifiable. + * + * @param locL The location to convert. + * @param sName The name of the created metalocation, if any. + * @return The metalocation created from locL. Or a null + * metalocation on failure. + */ +struct metalocation LocationToMetalocation(location locL, string sName = ""); + +/** + * Convert a metalocation to equivalent standard location. + * + * NOTE! + * If the metalocation is not in current module, the current module's starting + * location will be returned. As such, it is recommended that you run + * GetIsMetalocationInModule() on the metalocation before using this. + * + * If the metalocation is not valid, the returned location will also + * not be valid. + * + * @param mlocL The metalocation to convert. + * @return The location created from mlocL. + */ +location MetalocationToLocation(struct metalocation mlocL); + +/** + * Checks whether the given metalocation is in the module currently being run. + * + * @param mlocL The metalocation to test. + * @return TRUE if mlocL refers to a location within current module, + * FALSE otherwise. + */ +int GetIsMetalocationInModule(struct metalocation mlocL); + +/** + * Extracts an area reference from the given metalocation. If the metalocation + * is not in the current module, does not refere to a valid area, or the area + * cannot be uniquely identified, OBJECT_INVALID is returned. + * + * @param mlocL The metalocation from which to extract the area reference. + * @return An object reference to the area containing the metalocation or + * OBJECT_INVALID in case of error. + */ +object GetAreaFromMetalocation(struct metalocation mlocL); + +/** + * Stores the given metalocation on the given object. Behaves as other normal + * local variables do. + * + * @param oObject The object to store the metalocation on. + * @param sName The local variable name the metalocation will be stored as. + * @param mlocL The metalocation to store. + */ +void SetLocalMetalocation(object oObject, string sName, struct metalocation mlocL); + +/** + * Stores the given metalocation persistantly, so that it will remain in the + * character data over character exports. + * + * @param oCreature The creature to store the metalocation on. + * @param sName The local variable name the metalocation will be stored as. + * @param mlocL The metalocation to store. + * + * @see inc_persist_loca + */ +void SetPersistantLocalMetalocation(object oCreature, string sName, struct metalocation mlocL); + +/** + * Retrieves the metalocation stored on the given object under the given name. + * NOTE! If there was no metalocation stored with the given name, the returned + * value will have all it's fields contain null-equivalents. + * + * @param oObject The object the metalocation was stored on. + * @param sName The name the metalocation was stored under. + * @return A copy of the stored metalocation. + */ +struct metalocation GetLocalMetalocation(object oObject, string sName); + +/** + * Retrieves the metalocation persistantly stored on the given creature under + * the given name. + * NOTE! If there was no metalocation stored with the given name, the returned + * value will have all it's fields contain null-equivalents. + * + * @param oCreature The creature the metalocation was stored on. + * @param sName The name the metalocation was stored under. + * @return A copy of the stored metalocation. + * + * @see inc_persist_loca + */ +struct metalocation GetPersistantLocalMetalocation(object oCreature, string sName); + +/** + * Deletes the metalocation stored with the given name on the given object. + * + * @param oObject The object the metalocation was stored on. + * @param sName The name the metalocation was stored under. + */ +void DeleteLocalMetalocation(object oObject, string sName); + +/** + * Deletes the metalocation persistantly stored with the given name on + * the given creature. + * + * @param oCreature The creature the metalocation was stored on. + * @param sName The name the metalocation was stored under. + * + * @see inc_persist_loca + */ +void DeletePersistantLocalMetalocation(object oCreature, string sName); + +/** + * Creates a map pin based on the given metalocation. It will be created at the + * end of the map pin array, with name equal to the metalocation's. + * + * @param mlocL The metalocation to create a map pin from. + * @param oPC The player character in whose map pin array to create the map pin in. + */ +void CreateMapPinFromMetalocation(struct metalocation mlocL, object oPC); + +/** + * Creates a metalocation based on the given map pin. + * + * @param oPC The player character in whose map pin array to use + * @param nPinNo The position of the map pin to use + */ +struct metalocation CreateMetalocationFromMapPin(object oPC, int nPinNo); + +/** + * Creates a metalocation with all constituents having null-equivalent values. + * Used when there is a need to return an invalid metalocation. + * + * @return A metalocation that has a null-equivalent in each field. + */ +struct metalocation GetNullMetalocation(); + +/** + * Checks whether the given metalocation is valid. That is, not null and + * in the current module. + * + * @param mlocL The metalocation to test. + * @return TRUE if the metalocation is valid, FALSE otherwise. + */ +int GetIsMetalocationValid(struct metalocation mlocL); + +/** + * Gets the size of a players map pin array + * + * @param oPC The player character in whose map pin array to get the size of. + */ +int GetNumberOfMapPins(object oPC); + +/** + * Gets the area of a players specific map pin + * + * @param oPC The player character in whose map pin array to get the area of. + * @param nPinNo The number of the map pin to remove. + */ +object GetAreaOfMapPin(object oPC, int nPinNo); + +/** + * Deletes a players specific map pin + * + * @param oPC The player character in whose map pin array to get the size of. + */ +void DeleteMapPin(object oPC, int nPinNo); + +/** + * Creates a string from a metalocation that is of the following format: + * NameOfMetalocation - NameOfMetalocationArea (Xcoord, Ycoord) + * + * @param mlocL The metalocation to make a string from. + * @return The created string. + */ +string MetalocationToString(struct metalocation mlocL); + + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "inc_utility" +#include "inc_area" // Area functions seperated from inc_itility +#include "inc_persist_loca" // changed include as these are the only other + // functions required from inc_utilities + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +struct metalocation LocationToMetalocation(location locL, string sName = "") +{ + struct metalocation mlocL; + object oArea = GetAreaFromLocation(locL); + vector vCoords = GetPositionFromLocation(locL); + mlocL.sAreaTag = GetTag(oArea); + mlocL.sAreaName = GetName(oArea); + mlocL.nAreaHeight = GetAreaHeight(oArea); + mlocL.nAreaWidth = GetAreaWidth(oArea); + mlocL.fX = vCoords.x; + mlocL.fY = vCoords.y; + mlocL.fZ = vCoords.z; + mlocL.fFacing = GetFacingFromLocation(locL); + mlocL.sName = sName; + mlocL.sModule = GetName(GetModule()); + + // Check that the area can be uniquely identified. + if(GetAreaFromMetalocation(mlocL) == OBJECT_INVALID) + return GetNullMetalocation(); // It can't, return null. + + return mlocL; +} + +location MetalocationToLocation(struct metalocation mlocL) +{ + // Check whether the metalocation is in this module + if(!GetIsMetalocationInModule(mlocL)) + return GetStartingLocation(); // Must return a valid location, so return starting location. + + // Get the area + object oArea = GetAreaFromMetalocation(mlocL); + + // Construct and return the location + return Location(oArea, Vector(mlocL.fX, mlocL.fY, mlocL.fZ), mlocL.fFacing); +} + +int GetIsMetalocationInModule(struct metalocation mlocL) +{ + return GetName(GetModule()) == mlocL.sModule; +} + +object GetAreaFromMetalocation(struct metalocation mlocL) +{ + if(!GetIsMetalocationInModule(mlocL)) return OBJECT_INVALID; + + object oArea = GetObjectByTag(mlocL.sAreaTag, 0); + // Multiple areas with same tag? + if(GetName(oArea) != mlocL.sAreaName) + { + int i = 1; + oArea = GetObjectByTag(mlocL.sAreaTag, i); + while(GetIsObjectValid(oArea) && + GetName(oArea) != mlocL.sAreaName && + GetAreaHeight(oArea) != mlocL.nAreaHeight && + GetAreaWidth(oArea) != mlocL.nAreaWidth + ) + oArea = GetObjectByTag(mlocL.sAreaTag, ++i); + + // Make sure that if the object reference is not valid, it is OBJECT_INVALID + if(!GetIsObjectValid(oArea)) return OBJECT_INVALID; + + // We have a valid area reference. Now check that it is the only one matching the parameters. + object oAreaCheck = GetObjectByTag(mlocL.sAreaTag, ++i); + while(GetIsObjectValid(oAreaCheck)) + { + if(GetName(oAreaCheck) == mlocL.sAreaName && + GetAreaHeight(oAreaCheck) == mlocL.nAreaHeight && + GetAreaWidth(oAreaCheck) == mlocL.nAreaWidth + ) + return OBJECT_INVALID; // Found a second match, return OBJECT_INVALID to preserve invariant + } + } + + return oArea; +} + +void SetLocalMetalocation(object oObject, string sName, struct metalocation mlocL) +{ + SetLocalString(oObject, "Metalocation_" + sName + "_AreaTag", mlocL.sAreaTag); + //SetLocalString(oObject, "Metalocation_" + sName + "_AreaResRef", mlocL.sAreaResRef); + SetLocalString(oObject, "Metalocation_" + sName + "_AreaName", mlocL.sAreaName); + SetLocalInt (oObject, "Metalocation_" + sName + "_AreaHeight", mlocL.nAreaHeight); + SetLocalInt (oObject, "Metalocation_" + sName + "_AreaWidth", mlocL.nAreaWidth); + SetLocalFloat (oObject, "Metalocation_" + sName + "_X", mlocL.fX); + SetLocalFloat (oObject, "Metalocation_" + sName + "_Y", mlocL.fY); + SetLocalFloat (oObject, "Metalocation_" + sName + "_Z", mlocL.fZ); + SetLocalFloat (oObject, "Metalocation_" + sName + "_Facing", mlocL.fFacing); + SetLocalString(oObject, "Metalocation_" + sName + "_Name", mlocL.sName); + SetLocalString(oObject, "Metalocation_" + sName + "_Module", mlocL.sModule); +} + +void SetPersistantLocalMetalocation(object oCreature, string sName, struct metalocation mlocL) +{ + // Persistant operations fail on non-creatures. + if(GetObjectType(oCreature) != OBJECT_TYPE_CREATURE) return; + + SetPersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaTag", mlocL.sAreaTag); + //SetPersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaResRef", mlocL.sAreaResRef); + SetPersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaName", mlocL.sAreaName); + SetPersistantLocalInt (oCreature, "Metalocation_" + sName + "_AreaHeight", mlocL.nAreaHeight); + SetPersistantLocalInt (oCreature, "Metalocation_" + sName + "_AreaWidth", mlocL.nAreaWidth); + SetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_X", mlocL.fX); + SetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Y", mlocL.fY); + SetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Z", mlocL.fZ); + SetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Facing", mlocL.fFacing); + SetPersistantLocalString(oCreature, "Metalocation_" + sName + "_Name", mlocL.sName); + SetPersistantLocalString(oCreature, "Metalocation_" + sName + "_Module", mlocL.sModule); +} + +struct metalocation GetLocalMetalocation(object oObject, string sName) +{ + struct metalocation mlocL; + mlocL.sAreaTag = GetLocalString(oObject, "Metalocation_" + sName + "_AreaTag"); + //mlocL.sAreaResRef = GetLocalString(oObject, "Metalocation_" + sName + "_AreaResRef"); + mlocL.sAreaName = GetLocalString(oObject, "Metalocation_" + sName + "_AreaName"); + mlocL.nAreaHeight = GetLocalInt (oObject, "Metalocation_" + sName + "_AreaHeight"); + mlocL.nAreaWidth = GetLocalInt (oObject, "Metalocation_" + sName + "_AreaWidth"); + mlocL.fX = GetLocalFloat (oObject, "Metalocation_" + sName + "_X"); + mlocL.fY = GetLocalFloat (oObject, "Metalocation_" + sName + "_Y"); + mlocL.fZ = GetLocalFloat (oObject, "Metalocation_" + sName + "_Z"); + mlocL.fFacing = GetLocalFloat (oObject, "Metalocation_" + sName + "_Facing"); + mlocL.sName = GetLocalString(oObject, "Metalocation_" + sName + "_Name"); + mlocL.sModule = GetLocalString(oObject, "Metalocation_" + sName + "_Module"); + + return mlocL; +} + +struct metalocation GetPersistantLocalMetalocation(object oCreature, string sName) +{ + // Persistant operations fail on non-creatures. + if(GetObjectType(oCreature) != OBJECT_TYPE_CREATURE) return GetNullMetalocation(); + + struct metalocation mlocL; + mlocL.sAreaTag = GetPersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaTag"); + //mlocL.sAreaResRef = GetPersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaResRef"); + mlocL.sAreaName = GetPersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaName"); + mlocL.nAreaHeight = GetPersistantLocalInt (oCreature, "Metalocation_" + sName + "_AreaHeight"); + mlocL.nAreaWidth = GetPersistantLocalInt (oCreature, "Metalocation_" + sName + "_AreaWidth"); + mlocL.fX = GetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_X"); + mlocL.fY = GetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Y"); + mlocL.fZ = GetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Z"); + mlocL.fFacing = GetPersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Facing"); + mlocL.sName = GetPersistantLocalString(oCreature, "Metalocation_" + sName + "_Name"); + mlocL.sModule = GetPersistantLocalString(oCreature, "Metalocation_" + sName + "_Module"); + + return mlocL; +} + +void DeleteLocalMetalocation(object oObject, string sName) +{ + DeleteLocalString(oObject, "Metalocation_" + sName + "_AreaTag"); + //DeleteLocalString(oObject, "Metalocation_" + sName + "_AreaResRef"); + DeleteLocalString(oObject, "Metalocation_" + sName + "_AreaName"); + DeleteLocalInt (oObject, "Metalocation_" + sName + "_AreaHeight"); + DeleteLocalInt (oObject, "Metalocation_" + sName + "_AreaWidth"); + DeleteLocalFloat (oObject, "Metalocation_" + sName + "_X"); + DeleteLocalFloat (oObject, "Metalocation_" + sName + "_Y"); + DeleteLocalFloat (oObject, "Metalocation_" + sName + "_Z"); + DeleteLocalFloat (oObject, "Metalocation_" + sName + "_Facing"); + DeleteLocalString(oObject, "Metalocation_" + sName + "_Name"); + DeleteLocalString(oObject, "Metalocation_" + sName + "_Module"); +} + +void DeletePersistantLocalMetalocation(object oCreature, string sName) +{ + // Persistant operations fail on non-creatures. + if(GetObjectType(oCreature) != OBJECT_TYPE_CREATURE) return; + + DeletePersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaTag"); + //DeletePersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaResRef"); + DeletePersistantLocalString(oCreature, "Metalocation_" + sName + "_AreaName"); + DeletePersistantLocalInt (oCreature, "Metalocation_" + sName + "_AreaHeight"); + DeletePersistantLocalInt (oCreature, "Metalocation_" + sName + "_AreaWidth"); + DeletePersistantLocalFloat (oCreature, "Metalocation_" + sName + "_X"); + DeletePersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Y"); + DeletePersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Z"); + DeletePersistantLocalFloat (oCreature, "Metalocation_" + sName + "_Facing"); + DeletePersistantLocalString(oCreature, "Metalocation_" + sName + "_Name"); + DeletePersistantLocalString(oCreature, "Metalocation_" + sName + "_Module"); +} + + +/* +Map pin data: +Local int "NW_TOTAL_MAP_PINS" + - Number of existing map pins. + +Local string "NW_MAP_PIN_NRTY_#" + - Name of the nth map pin. + - # is string representation of the map pin's index number, base 1. + +Local float "NW_MAP_PIN_XPOS_#" + - The map pin's X coordinate in the area. + - # is string representation of the map pin's index number, base 1. + +Local float "NW_MAP_PIN_YPOS_#" + - The map pin's Y coordinate in the area. + - # is string representation of the map pin's index number, base 1. + +Local object "NW_MAP_PIN_AREA_#" + - Object reference to the area where the map pin is located. + - # is string representation of the map pin's index number, base 1. +*/ +void CreateMapPinFromMetalocation(struct metalocation mlocL, object oPC) +{ + if(!GetIsObjectValid(oPC)) + return; + //check no other map pins at that location + int nPinCount = GetNumberOfMapPins(oPC); + int i; + for(i=1;i*": "") + + (GetIsMetalocationInModule(mlocL) ? "" : (" " + GetStringByStrRef(16825269)/*" Not in module "*/)); +} + +//void main(){} // Test main diff --git a/src/include/inc_newspellbook.nss b/src/include/inc_newspellbook.nss new file mode 100644 index 0000000..909b93d --- /dev/null +++ b/src/include/inc_newspellbook.nss @@ -0,0 +1,1522 @@ + + +/* Steps for adding a new spellbook + +Prepared: +Make cls_spgn_*.2da +Make cls_spcr_*.2da +Make blank cls_spell_*.2da +Add cls_spgn_*.2da to classes.2da +Add class entry in prc_classes.2da +Add the spellbook feat (#1999) to cls_feat_*.2da at the appropriate level +Add class to PRCGetSpellSaveDC() in prc_add_spell_dc +Add class to GetSpellbookTypeForClass() below +Add class to GetAbilityScoreForClass() below +Add class to bKnowsAllClassSpells() below if necessary +Add class to GetIsArcaneClass() or GetIsDivineClass() in prc_inc_castlvl as appropriate +Add class to GetCasterLevelModifier() in prc_inc_castlvl if necessary +Add class to SetupLookupStage() in inc_lookups +Add class to GetCasterLvl() in prc_inc_spells +Add Practiced Spellcaster feat to feat.2da and to PracticedSpellcasting() in prc_inc_castlvl +Run the assemble_spellbooks.bat file +Make the prc_* scripts in newspellbook. The filenames can be found under the spell entries for the class in spells.2da. + +Spont: +Make cls_spgn_*.2da +Make cls_spkn_*.2da +Make cls_spcr_*.2da +Make blank cls_spell_*.2da +Add cls_spkn_*.2da and cls_spgn_*.2da to classes.2da +Add class entry in prc_classes.2da +Add class to PRCGetSpellSaveDC() in prc_add_spell_dc +Add class to GetSpellbookTypeForClass() below +Add class to GetAbilityScoreForClass() below +Add class to bKnowsAllClassSpells() below if necessary +Add class to GetIsArcaneClass() or GetIsDivineClass() in prc_inc_castlvl as appropriate +Add class to GetCasterLevelModifier() in prc_inc_castlvl if necessary +Add class to SetupLookupStage() in inc_lookups +Add class to GetCasterLvl() in prc_inc_spells +Add Practiced Spellcaster feat to feat.2da and to PracticedSpellcasting() in prc_inc_castlvl +Add class to prc_amagsys_gain if(CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, MinimumSpellLevel, MaximumSpellLevel)) +Add class to ExecuteScript("prc_amagsys_gain", oPC) list in EvalPRCFeats in prc_inc_function +Run the assemble_spellbooks.bat file +Make the prc_* scripts in newspellbook + +prc_classes.2da entry: +Label - name for the class +Name - tlk file strref +SpellCaster - does the class cast spells? 0 = No, 1 = Yes (used for bonus spellslot item properties) +SBType - S = spontaneous, P = prepared +AL - does the class use Advanced Learning of any type? 0 = No, 1 = Yes +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int GetSpellbookTypeForClass(int nClass); +int GetAbilityScoreForClass(int nClass, object oPC); + +/** + * Determines the given character's DC-modifying ability modifier for + * the given class' spells. Handles split-score casters. + * + * @param nClass The spellcasting class for whose spells to determine ability mod to DC for + * @param oPC The character whose abilities to examine + * @return The DC-modifying ability score's ability modifier value + */ +int GetDCAbilityModForClass(int nClass, object oPC); + +string GetFileForClass(int nClass); +int GetSpellslotLevel(int nClass, object oPC); +int GetItemBonusSlotCount(object oPC, int nClass, int nSpellLevel); +int GetSlotCount(int nLevel, int nSpellLevel, int nAbilityScore, int nClass, object oItemPosessor = OBJECT_INVALID); +int bKnowsAllClassSpells(int nClass); +int GetSpellKnownMaxCount(int nLevel, int nSpellLevel, int nClass, object oPC); +int GetSpellKnownCurrentCount(object oPC, int nSpellLevel, int nClass); +int GetSpellUnknownCurrentCount(object oPC, int nSpellLevel, int nClass); +void AddSpellUse(object oPC, int nSpellbookID, int nClass, string sFile, string sArrayName, int nSpellbookType, object oSkin, int nFeatID, int nIPFeatID, string sIDX = ""); +void RemoveSpellUse(object oPC, int nSpellID, int nClass); +// int GetSpellUses(object oPC, int nSpellID, int nClass); +int GetSpellLevel(int nSpellID, int nClass); +void SetupSpells(object oPC, int nClass); +void CheckAndRemoveFeat(object oHide, itemproperty ipFeat); +void WipeSpellbookHideFeats(object oPC); +void CheckNewSpellbooks(object oPC); +void NewSpellbookSpell(int nClass, int nSpellbookType, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE); +void CastSpontaneousSpell(int nClass, int bInstantSpell = FALSE); +void CastPreparedSpell(int nClass, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE); +void ProcessPreparedSpellLevel(object oPC, int nClass, int nSpellLevel, int nLevel, int nAbility, string sClass, string sFile, string sArrayName, int nSpellbookType, object oSkin); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/* stored in "prc_inc_sb_const" + Accessed via "prc_inc_core" */ + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +// ** THIS ORDER IS IMPORTANT ** + +//#include "prc_effect_inc" //access via prc_inc_core +//#include "inc_lookups" //access via prc_inc_core +#include "prc_inc_core" +#include "inc_sp_gain_mem" //providing child access to prc_inc_core + //Must load in this order. +//#include "prc_inc_castlvl" //access via prc_inc_core +//#include "prc_inc_descrptr" //access via prc_inc_core + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetSpellbookTypeForClass(int nClass) +{ + switch(nClass) + { + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOHEI: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + case CLASS_TYPE_VIGILANT: + case CLASS_TYPE_WIZARD: + return SPELLBOOK_TYPE_PREPARED; + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BARD: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HARPER: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_WEAVE: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + return SPELLBOOK_TYPE_SPONTANEOUS; + // shapechanger HD count as sorcerer for aranea. + case CLASS_TYPE_SHAPECHANGER: + return SPELLBOOK_TYPE_SPONTANEOUS; + // Multiple races + case CLASS_TYPE_MONSTROUS: + return SPELLBOOK_TYPE_SPONTANEOUS; + // Gloura as Bard + case CLASS_TYPE_FEY: + return SPELLBOOK_TYPE_SPONTANEOUS; + // Drider as Sorc + case CLASS_TYPE_ABERRATION: + return SPELLBOOK_TYPE_SPONTANEOUS; + //outsider HD count as sorc for raks + case CLASS_TYPE_OUTSIDER: { + /// @todo Will eventually need to add a check here to differentiate between races. Not all are sorcerers, just most + return SPELLBOOK_TYPE_SPONTANEOUS; + } + } + return SPELLBOOK_TYPE_INVALID; +} + +int GetAbilityScoreForClass(int nClass, object oPC) +{ + switch(nClass) + { + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_FIST_OF_ZUOKEN: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_PSYWAR: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOHEI: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + case CLASS_TYPE_VIGILANT: + case CLASS_TYPE_WARMIND: + return GetAbilityScore(oPC, ABILITY_WISDOM); + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_PSION: + case CLASS_TYPE_PSYCHIC_ROGUE: + case CLASS_TYPE_SHADOWCASTER: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_WIZARD: + return GetAbilityScore(oPC, ABILITY_INTELLIGENCE); + case CLASS_TYPE_BARD: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HARPER: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_KNIGHT_WEAVE: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + case CLASS_TYPE_WILDER: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + //shapeshifter HD count as sorc for aranea + case CLASS_TYPE_SHAPECHANGER: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Multiple races + case CLASS_TYPE_MONSTROUS: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Gloura as Bard + case CLASS_TYPE_FEY: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Drider as Sorc + case CLASS_TYPE_ABERRATION: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + //outsider HD count as sorc for raks + case CLASS_TYPE_OUTSIDER: { + /// @todo Will eventually need to add a check here to differentiate between races. Not all are sorcerers, just most + return GetAbilityScore(oPC, ABILITY_CHARISMA); + } + } + return GetAbilityScore(oPC, ABILITY_CHARISMA); //default for SLAs? +} + +int GetDCAbilityModForClass(int nClass, object oPC) +{ + switch(nClass) + { + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_FIST_OF_ZUOKEN: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_PSYWAR: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOHEI: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + case CLASS_TYPE_VIGILANT: + case CLASS_TYPE_WARMIND: + return GetAbilityModifier(ABILITY_WISDOM, oPC); + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_PSION: + case CLASS_TYPE_PSYCHIC_ROGUE: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_WIZARD: + return GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + case CLASS_TYPE_BARD: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_HARPER: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_SHADOWCASTER: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + case CLASS_TYPE_WILDER: + return GetAbilityModifier(ABILITY_CHARISMA, oPC); + //shapechanger HD count as sorc for aranea + case CLASS_TYPE_SHAPECHANGER: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Multiple races + case CLASS_TYPE_MONSTROUS: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Gloura as Bard + case CLASS_TYPE_FEY: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + // Drider as Sorc + case CLASS_TYPE_ABERRATION: + return GetAbilityScore(oPC, ABILITY_CHARISMA); + //outsider HD count as sorc for raks + case CLASS_TYPE_OUTSIDER: { + // @todo Will eventually need to add a check here to differentiate between races. Not all are sorcerers, just most + return GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + } + return GetAbilityModifier(ABILITY_CHARISMA, oPC); //default for SLAs? +} + +string GetFileForClass(int nClass) +{ + string sFile = Get2DACache("classes", "FeatsTable", nClass); + sFile = "cls_spell" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061231 + //if(DEBUG) DoDebug("GetFileForClass(" + IntToString(nClass) + ") = " + sFile); + return sFile; +} + +int GetSpellslotLevel(int nClass, object oPC) +{ + int nBaseLevel = GetLevelByClass(nClass, oPC); + + // Custom racial casting + int nRacialLevel = 0; + int nRace = GetRacialType(oPC); + + if (nClass == CLASS_TYPE_SORCERER) + { + if(nRace == RACIAL_TYPE_RAKSHASA) + nRacialLevel = GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC); + else if(nRace == RACIAL_TYPE_ARKAMOI) + nRacialLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC); + else if(nRace == RACIAL_TYPE_DRIDER) + nRacialLevel = GetLevelByClass(CLASS_TYPE_ABERRATION, oPC); + else if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + nRacialLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC) * 3 / 4; + else if(nRace == RACIAL_TYPE_MARRUTACT) + nRacialLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC) * 6 / 7; + else if(nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + nRacialLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC); + else if(nRace == RACIAL_TYPE_ARANEA) + nRacialLevel = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oPC); + } + else if (nClass == CLASS_TYPE_BARD && nRace == RACIAL_TYPE_GLOURA) + { + nRacialLevel = GetLevelByClass(CLASS_TYPE_FEY, oPC); + } + + // Add base and racial class levels + int nLevel = nBaseLevel + nRacialLevel; + + // Spellcasting PRC progression + int nArcSpellslotLevel = 0; + int nDivSpellslotLevel = 0; + int i; + for(i = 1; i <= 8; i++) + { + int nTempClass = GetClassByPosition(i, oPC); + int nArcSpellMod = StringToInt(Get2DACache("classes", "ArcSpellLvlMod", nTempClass)); + int nDivSpellMod = StringToInt(Get2DACache("classes", "DivSpellLvlMod", nTempClass)); + + if(nArcSpellMod > 0) + nArcSpellslotLevel += (GetLevelByClass(nTempClass, oPC) + (nArcSpellMod - 1)) / nArcSpellMod; + + if(nDivSpellMod > 0) + nDivSpellslotLevel += (GetLevelByClass(nTempClass, oPC) + (nDivSpellMod - 1)) / nDivSpellMod; + } + + // Add PRC spellcasting progression + if(GetPrimaryArcaneClass(oPC) == nClass) + nLevel += nArcSpellslotLevel; + if(GetPrimaryDivineClass(oPC) == nClass) + nLevel += nDivSpellslotLevel; + + // Ultimate Magus override (only include Sorcerer + UM) + if (nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oPC)) + { + nLevel = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + } + + if(DEBUG) DoDebug("GetSpellslotLevel(" + IntToString(nClass) + ", " + GetName(oPC) + ") = " + IntToString(nLevel)); + return nLevel; +} + +/* int GetSpellslotLevel(int nClass, object oPC) +{ + int nLevel = GetLevelByClass(nClass, oPC); + +//:: Rakshasa cast as sorcerers + if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + nLevel = GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC); + + if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + nLevel = GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + +//:: Arkamoi cast as sorcerers + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC); + + if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + +//:: Driders cast as sorcerers + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) + nLevel = GetLevelByClass(CLASS_TYPE_ABERRATION, oPC); + + if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) + nLevel = GetLevelByClass(CLASS_TYPE_ABERRATION, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + +//:: Redspawn Arcaniss cast as 3/4 sorcerers + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC)*3/4; + + else if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) + nLevel = GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + (GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC)*3/4); + +//:: Marrutact cast as 6/7 sorcerers + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT) + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC)*6/7; + + else if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT) + nLevel = GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + (GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC)*6/7); + +//:: Hobgoblin Warsouls cast as sorcerers + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + nLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC); + + else if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + nLevel = GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + GetLevelByClass(CLASS_TYPE_MONSTROUS, oPC); + +//:: Aranea cast as sorcerers + else if(nClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) + nLevel = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oPC); + + else if(nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) + nLevel = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + +//:: Gloura cast as bards + else if(nClass == CLASS_TYPE_BARD && !GetLevelByClass(CLASS_TYPE_BARD, oPC) && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) + nLevel = GetLevelByClass(CLASS_TYPE_FEY, oPC); + + else if(nClass == CLASS_TYPE_BARD && GetLevelByClass(CLASS_TYPE_BARD, oPC) && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) + { + nLevel = GetLevelByClass(CLASS_TYPE_FEY, oPC) + GetLevelByClass(CLASS_TYPE_BARD, oPC); + } + + int nArcSpellslotLevel; + int nDivSpellslotLevel; + int i; + for(i = 1; i <= 8; i++) + { + int nTempClass = GetClassByPosition(i, oPC); + //spellcasting prc + int nArcSpellMod = StringToInt(Get2DACache("classes", "ArcSpellLvlMod", nTempClass)); + int nDivSpellMod = StringToInt(Get2DACache("classes", "DivSpellLvlMod", nTempClass)); + //special case for combat medic class + //if(nTempClass == CLASS_TYPE_COMBAT_MEDIC && (nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_WITCH)) + // nArcSpellMod = 1; + + if(nArcSpellMod == 1) + nArcSpellslotLevel += GetLevelByClass(nTempClass, oPC); + else if(nArcSpellMod > 1) + nArcSpellslotLevel += (GetLevelByClass(nTempClass, oPC) + 1) / nArcSpellMod; + if(nDivSpellMod == 1) + nDivSpellslotLevel += GetLevelByClass(nTempClass, oPC); + else if(nDivSpellMod > 1) + nDivSpellslotLevel += (GetLevelByClass(nTempClass, oPC) + 1) / nDivSpellMod; + } + + if(GetPrimaryArcaneClass(oPC) == nClass) + nLevel += nArcSpellslotLevel; + if(GetPrimaryDivineClass(oPC) == nClass) + nLevel += nDivSpellslotLevel; + + // For this special instance, we know that this is the only prestige class + if (nClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oPC)) + nLevel = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oPC) + GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + + if(DEBUG) DoDebug("GetSpellslotLevel(" + IntToString(nClass) + ", " + GetName(oPC) + ") = " + IntToString(nLevel)); + return nLevel; +} + */ + +int GetItemBonusSlotCount(object oPC, int nClass, int nSpellLevel) +{ + // Value maintained by CheckPRCLimitations() + return GetLocalInt(oPC, "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel)); +} + +int GetSlotCount(int nLevel, int nSpellLevel, int nAbilityScore, int nClass, object oItemPosessor = OBJECT_INVALID) +{ + // Ability score limit rule: Must have casting ability score of at least 10 + spel level to be able to cast spells of that level at all + if(nAbilityScore < nSpellLevel + 10) + return 0; + int nSlots; + string sFile; + /*// Bioware casters use their classes.2da-specified tables + if( nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER) + {*/ + sFile = Get2DACache("classes", "SpellGainTable", nClass); + /*} + // New spellbook casters use the cls_spbk_* tables + else + { + sFile = Get2DACache("classes", "FeatsTable", nClass); + sFile = "cls_spbk" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061231 + }*/ + + string sSlots = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nLevel - 1); + if(sSlots == "") + { + nSlots = -1; + //if(DEBUG) DoDebug("GetSlotCount: Problem getting slot numbers for " + IntToString(nSpellLevel) + " " + IntToString(nLevel) + " " + sFile); + } + else + nSlots = StringToInt(sSlots); + if(nSlots == -1) + return 0; + + // Add spell slots from items + if(GetIsObjectValid(oItemPosessor)) + nSlots += GetItemBonusSlotCount(oItemPosessor, nClass, nSpellLevel); + + // Add spell slots from high ability score. Level 0 spells are exempt + if(nSpellLevel == 0) + return nSlots; + else + { + int nAbilityMod = nClass == CLASS_TYPE_ARCHIVIST ? GetAbilityModifier(ABILITY_WISDOM, oItemPosessor) : (nAbilityScore - 10) / 2; + if(nAbilityMod >= nSpellLevel) // Need an ability modifier at least equal to the spell level to gain bonus slots + nSlots += ((nAbilityMod - nSpellLevel) / 4) + 1; + return nSlots; + } +} + +//if the class doesn't learn all available spells on level-up add it here +int bKnowsAllClassSpells(int nClass) +{ + switch(nClass) + { + //case CLASS_TYPE_WIZARD: + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_BARD: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_JUSTICEWW: + case CLASS_TYPE_KNIGHT_WEAVE: + case CLASS_TYPE_SORCERER: + case CLASS_TYPE_SUBLIME_CHORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + return FALSE; + + // Everyone else + default: + return TRUE; + } + return TRUE; +} + +int GetSpellKnownMaxCount(int nLevel, int nSpellLevel, int nClass, object oPC) +{ + // If the character doesn't have any spell slots available on for this level, it can't know any spells of that level either + // @todo Check rules. There might be cases where this doesn't hold + if(!GetSlotCount(nLevel, nSpellLevel, GetAbilityScoreForClass(nClass, oPC), nClass)) + return 0; + int nKnown; + string sFile; + // Bioware casters use their classes.2da-specified tables + /*if( nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER) + {*/ + sFile = Get2DACache("classes", "SpellKnownTable", nClass); + /*} + else + { + sFile = Get2DACache("classes", "FeatsTable", nClass); + sFile = "cls_spkn" + GetStringRight(sFile, GetStringLength(sFile) - 8); // Hardcoded the cls_ part. It's not as if any class uses some other prefix - Ornedan, 20061231 + }*/ + + string sKnown = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nLevel - 1); + if(DEBUG) DoDebug("GetSpellKnownMaxCount(" + IntToString(nLevel) + ", " + IntToString(nSpellLevel) + ", " + IntToString(nClass) + ", " + GetName(oPC) + ") = " + sKnown); + if(sKnown == "") + { + nKnown = -1; + //if(DEBUG) DoDebug("GetSpellKnownMaxCount: Problem getting known numbers for " + IntToString(nSpellLevel) + " " + IntToString(nLevel) + " " + sFile); + } + else + nKnown = StringToInt(sKnown); + if(nKnown == -1) + return 0; + + // Bard and Sorcerer only have new spellbook spells known if they have taken prestige classes that increase spellcasting + if(nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_BARD) + { + if((GetLevelByClass(nClass) == nLevel) //no PrC + && !(GetHasFeat(FEAT_DRACONIC_GRACE, oPC) || GetHasFeat(FEAT_DRACONIC_BREATH, oPC))) //no Draconic feats that apply + return 0; + } + return nKnown; +} + +int GetSpellKnownCurrentCount(object oPC, int nSpellLevel, int nClass) +{ + // Check short-term cache + string sClassNum = IntToString(nClass); + if(GetLocalInt(oPC, "GetSKCCCache_" + IntToString(nSpellLevel) + "_" + sClassNum)) + return GetLocalInt(oPC, "GetSKCCCache_" + IntToString(nSpellLevel) + "_" + sClassNum) - 1; + + // Loop over all spells known and count the number of spells of each level known + int i; + int nKnown; + int nKnown0, nKnown1, nKnown2, nKnown3, nKnown4; + int nKnown5, nKnown6, nKnown7, nKnown8, nKnown9; + string sFile = GetFileForClass(nClass); + for(i = 0; i < persistant_array_get_size(oPC, "Spellbook" + sClassNum); i++) + { + int nNewSpellbookID = persistant_array_get_int(oPC, "Spellbook" + sClassNum, i); + int nLevel = StringToInt(Get2DACache(sFile, "Level", nNewSpellbookID)); + switch(nLevel) + { + case 0: nKnown0++; break; case 1: nKnown1++; break; + case 2: nKnown2++; break; case 3: nKnown3++; break; + case 4: nKnown4++; break; case 5: nKnown5++; break; + case 6: nKnown6++; break; case 7: nKnown7++; break; + case 8: nKnown8++; break; case 9: nKnown9++; break; + } + } + + // Pick the level requested for returning + switch(nSpellLevel) + { + case 0: nKnown = nKnown0; break; case 1: nKnown = nKnown1; break; + case 2: nKnown = nKnown2; break; case 3: nKnown = nKnown3; break; + case 4: nKnown = nKnown4; break; case 5: nKnown = nKnown5; break; + case 6: nKnown = nKnown6; break; case 7: nKnown = nKnown7; break; + case 8: nKnown = nKnown8; break; case 9: nKnown = nKnown9; break; + } + if(DEBUG) DoDebug("GetSpellKnownCurrentCount(" + GetName(oPC) + ", " + IntToString(nSpellLevel) + ", " + sClassNum + ") = " + IntToString(nKnown)); + if(DEBUG) DoDebug("GetSpellKnownCurrentCount(i " + IntToString(i) + ", nKnown0 " + IntToString(nKnown0) + ", nKnown1 " + IntToString(nKnown1) + ", nKnown2 " + IntToString(nKnown2) + ", nKnown3 " + IntToString(nKnown3) + ", nKnown4 " + IntToString(nKnown4) + ", nKnown5 " + IntToString(nKnown5) + ", nKnown6 " + IntToString(nKnown6) + ", nKnown7 " + IntToString(nKnown7) + ", nKnown8 " + IntToString(nKnown8) + ", nKnown9 " + IntToString(nKnown9)); + if(DEBUG) DoDebug("GetSpellKnownCurrentCount(persistant_array_get_size "+IntToString(persistant_array_get_size(oPC, "Spellbook" + sClassNum))); + + // Cache the values for 1 second + SetLocalInt(oPC, "GetSKCCCache_0_" + sClassNum, nKnown0 + 1); + SetLocalInt(oPC, "GetSKCCCache_1_" + sClassNum, nKnown1 + 1); + SetLocalInt(oPC, "GetSKCCCache_2_" + sClassNum, nKnown2 + 1); + SetLocalInt(oPC, "GetSKCCCache_3_" + sClassNum, nKnown3 + 1); + SetLocalInt(oPC, "GetSKCCCache_4_" + sClassNum, nKnown4 + 1); + SetLocalInt(oPC, "GetSKCCCache_5_" + sClassNum, nKnown5 + 1); + SetLocalInt(oPC, "GetSKCCCache_6_" + sClassNum, nKnown6 + 1); + SetLocalInt(oPC, "GetSKCCCache_7_" + sClassNum, nKnown7 + 1); + SetLocalInt(oPC, "GetSKCCCache_8_" + sClassNum, nKnown8 + 1); + SetLocalInt(oPC, "GetSKCCCache_9_" + sClassNum, nKnown9 + 1); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_0_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_1_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_2_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_3_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_4_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_5_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_6_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_7_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_8_" + sClassNum)); + DelayCommand(1.0, DeleteLocalInt(oPC, "GetSKCCCache_9_" + sClassNum)); + + return nKnown; +} + +int GetSpellUnknownCurrentCount(object oPC, int nSpellLevel, int nClass) +{ + // Get the lookup token created by MakeSpellbookLevelLoop() + string sTag = "SpellLvl_" + IntToString(nClass) + "_Level_" + IntToString(nSpellLevel); + object oCache = GetObjectByTag(sTag); + if(!GetIsObjectValid(oCache)) + { + if(DEBUG) DoDebug("GetSpellUnknownCurrentCount: " + sTag + " is not valid"); + return 0; + } + // Read the total number of spells on the given level and determine how many are already known + int nTotal = array_get_size(oCache, "Lkup"); + int nKnown = GetSpellKnownCurrentCount(oPC, nSpellLevel, nClass); + int nUnknown = nTotal - nKnown; + + if(DEBUG) DoDebug("GetSpellUnknownCurrentCount(" + GetName(oPC) + ", " + IntToString(nSpellLevel) + ", " + IntToString(nClass) + ") = " + IntToString(nUnknown)); + return nUnknown; +} + +void AddSpellUse(object oPC, int nSpellbookID, int nClass, string sFile, string sArrayName, int nSpellbookType, object oSkin, int nFeatID, int nIPFeatID, string sIDX = "") +{ + /* + string sFile = GetFileForClass(nClass); + string sArrayName = "NewSpellbookMem_"+IntToString(nClass); + int nSpellbookType = GetSpellbookTypeForClass(nClass); + object oSkin = GetPCSkin(oPC); + int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); + //add the feat only if they dont already have it + int nIPFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); + */ + object oToken = GetHideToken(oPC); + + // Add the spell use feats and set a marker local that tells for CheckAndRemoveFeat() to skip removing this feat + string sIPFeatID = IntToString(nIPFeatID); + SetLocalInt(oSkin, "NewSpellbookTemp_" + sIPFeatID, TRUE); + AddSkinFeat(nFeatID, nIPFeatID, oSkin, oPC); + + // Increase the current number of uses + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + //sanity test + if(!persistant_array_exists(oPC, sArrayName)) + { + if(DEBUG) DoDebug("ERROR: AddSpellUse: " + sArrayName + " array does not exist, creating"); + persistant_array_create(oPC, sArrayName); + } + + int nUses = persistant_array_get_int(oPC, sArrayName, nSpellbookID); + nUses++; + persistant_array_set_int(oPC, sArrayName, nSpellbookID, nUses); + if(DEBUG) DoDebug("AddSpellUse: " + sArrayName + "[" + IntToString(nSpellbookID) + "] = " + IntToString(array_get_int(oPC, sArrayName, nSpellbookID))); + + //Create index array - to avoid duplicates mark only 1st use of nSpellbookID + if(nUses == 1) + { + if(!persistant_array_exists(oPC, sIDX)) + persistant_array_create(oPC, sIDX); + + persistant_array_set_int(oPC, sIDX, array_get_size(oPC, sIDX), nSpellbookID); + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + //sanity test + if(!persistant_array_exists(oPC, sArrayName)) + { + if(DEBUG) DoDebug("ERROR: AddSpellUse: " + sArrayName + " array does not exist, creating"); + persistant_array_create(oPC, sArrayName); + } + /*int nSpellLevel = StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + int nCount = persistant_array_get_int(oPC, sArrayName, nSpellLevel); + if(nCount < 1) + { + int nLevel = GetSpellslotLevel(nClass, oPC); + int nAbility = GetAbilityScoreForClass(nClass, oPC); + nCount = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + array_set_int(oPC, sArrayName, nSpellLevel, nCount); + }*/ + if(DEBUG) DoDebug("AddSpellUse() called on spontaneous spellbook. nIPFeatID = " + sIPFeatID); + } +} + +void RemoveSpellUse(object oPC, int nSpellID, int nClass) +{ + string sFile = GetFileForClass(nClass); + int nSpellbookID = SpellToSpellbookID(nSpellID); + if(nSpellbookID == -1) + { + if(DEBUG) DoDebug("ERROR: RemoveSpellUse: Unable to resolve spell to spellbookID: " + IntToString(nSpellID) + " in file " + sFile); + return; + } + if(!persistant_array_exists(oPC, "NewSpellbookMem_"+IntToString(nClass))) + { + if(DEBUG) DoDebug("RemoveSpellUse: NewSpellbookMem_" + IntToString(nClass) + " does not exist, creating."); + persistant_array_create(oPC, "NewSpellbookMem_"+IntToString(nClass)); + } + + // Reduce the remaining uses of the given spell by 1 (except never reduce uses below 0). + // Spontaneous spellbooks reduce the number of spells of the spell's level remaining + int nSpellbookType = GetSpellbookTypeForClass(nClass); + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + int nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellbookID); + if(nCount > 0) + persistant_array_set_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellbookID, nCount - 1); + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + int nSpellLevel = StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + int nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(nCount > 0) + persistant_array_set_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel, nCount - 1); + } +} + +int GetSpellLevel(int nSpellID, int nClass) +{ + string sFile = GetFileForClass(nClass); + int nSpellbookID = SpellToSpellbookID(nSpellID); + if(nSpellbookID == -1) + { + if(DEBUG) DoDebug("ERROR: GetSpellLevel: Unable to resolve spell to spellbookID: "+IntToString(nSpellID)+" "+sFile); + return -1; + } + + // get spell level + int nSpellLevel = -1; + string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookID); + + if (sSpellLevel != "") + nSpellLevel = StringToInt(sSpellLevel); + + return nSpellLevel; +} + +//called inside for loop in SetupSpells(), delayed to prevent TMI +void SpontaneousSpellSetupLoop(object oPC, int nClass, string sFile, object oSkin, int i) +{ + int nSpellbookID = persistant_array_get_int(oPC, "Spellbook" + IntToString(nClass), i); + string sIPFeatID = Get2DACache(sFile, "IPFeatID", nSpellbookID); + int nIPFeatID = StringToInt(sIPFeatID); + int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); + //int nRealSpellID = StringToInt(Get2DACache(sFile, "RealSpellID", nSpellbookID)); + SetLocalInt(oSkin, "NewSpellbookTemp_" + sIPFeatID, TRUE); + + AddSkinFeat(nFeatID, nIPFeatID, oSkin, oPC); +} + +void SetupSpells(object oPC, int nClass) +{ + string sFile = GetFileForClass(nClass); + string sClass = IntToString(nClass); + string sArrayName = "NewSpellbookMem_" + sClass; + object oSkin = GetPCSkin(oPC); + int nLevel = GetSpellslotLevel(nClass, oPC); + int nAbility = GetAbilityScoreForClass(nClass, oPC); + int nSpellbookType = GetSpellbookTypeForClass(nClass); + + if(DEBUG) DoDebug("SetupSpells\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nSpellslotLevel = " + IntToString(nLevel) + "\n" + + "nAbility = " + IntToString(nAbility) + "\n" + + "nSpellbookType = " + IntToString(nSpellbookType) + "\n" + + "sFile = " + sFile + "\n" + ); + + // For spontaneous spellbooks, set up an array that tells how many spells of each level they can cast + // And add casting feats for each spell known to the caster's hide + if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) //:: Fixed by TiredByFirelight + { + // Spell slots + int nSpellLevel, nSlots; + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + nSlots = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + persistant_array_set_int(oPC, sArrayName, nSpellLevel, nSlots); + } + + int i; + for(i = 0; i < persistant_array_get_size(oPC, "Spellbook" + sClass); i++) + { //adding feats + SpontaneousSpellSetupLoop(oPC, nClass, sFile, oSkin, i); + } + }// end if - Spontaneous spellbook + + // For prepared spellbooks, add spell uses and use feats according to spells memorised list + else if(nSpellbookType == SPELLBOOK_TYPE_PREPARED && !GetIsBioSpellCastClass(nClass)) + { + int nSpellLevel, nSlot, nSlots, nSpellbookID; + string sArrayName2, sIDX; + + // clearing existing spells + persistant_array_delete(oPC, sArrayName); + + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + //Delay of 0.01, to ensure it runs after persistant_array_delete() which is a lot of 0.0 delay commands, but before other spellbook stuff + DelayCommand(0.01, ProcessPreparedSpellLevel(oPC, nClass, nSpellLevel, nLevel, nAbility, sClass, sFile, sArrayName, nSpellbookType, oSkin)); + } + } +} + +/* if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + // Spell slots + int nSpellLevel, nSlots; + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + nSlots = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + persistant_array_set_int(oPC, sArrayName, nSpellLevel, nSlots); + } + + int i; + for(i = 0; i < persistant_array_get_size(oPC, "Spellbook" + sClass); i++) + { //adding feats + SpontaneousSpellSetupLoop(oPC, nClass, sFile, oSkin, i); + } + }// end if - Spontaneous spellbook + + // For prepared spellbooks, add spell uses and use feats according to spells memorised list + else if(nSpellbookType == SPELLBOOK_TYPE_PREPARED && !GetIsBioSpellCastClass(nClass)) + { + int nSpellLevel, nSlot, nSlots, nSpellbookID; + string sArrayName2, sIDX; + + // clearing existing spells + int i; + for(i = 0; i < persistant_array_get_size(oPC, sArrayName); i++) + { + persistant_array_set_int(oPC, sArrayName, i, 0); + } + + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + sArrayName2 = "Spellbook" + IntToString(nSpellLevel) + "_" + sClass; // Minor optimisation: cache the array name string for multiple uses + sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + sClass; + nSlots = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + nSlot; + for(nSlot = 0; nSlot < nSlots; nSlot++) + { + //done when spells are added to it + nSpellbookID = persistant_array_get_int(oPC, sArrayName2, nSlot); + if(nSpellbookID != 0) + { + AddSpellUse(oPC, nSpellbookID, nClass, sFile, sArrayName, nSpellbookType, oSkin, + StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)), + StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)), + sIDX); + } + } + } + } +} */ + +void ProcessPreparedSpellLevel(object oPC, int nClass, int nSpellLevel, int nLevel, int nAbility, string sClass, string sFile, string sArrayName, int nSpellbookType, object oSkin) +{ + string sArrayName2 = "Spellbook" + IntToString(nSpellLevel) + "_" + sClass; + string sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + sClass; + int nSlots = GetSlotCount(nLevel, nSpellLevel, nAbility, nClass, oPC); + int nSlot; + for(nSlot = 0; nSlot < nSlots; nSlot++) + { + int nSpellbookID = persistant_array_get_int(oPC, sArrayName2, nSlot); + if(nSpellbookID != 0) + { + AddSpellUse(oPC, nSpellbookID, nClass, sFile, sArrayName, nSpellbookType, oSkin, + StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)), + StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)), + sIDX); + } + } +} + + +void CheckAndRemoveFeat(object oHide, itemproperty ipFeat) +{ + int nSubType = GetItemPropertySubType(ipFeat); + if(!GetLocalInt(oHide, "NewSpellbookTemp_" + IntToString(nSubType))) + { + RemoveItemProperty(oHide, ipFeat); + DeleteLocalInt(oHide, "NewSpellbookTemp_" + IntToString(nSubType)); + if(DEBUG) DoDebug("CheckAndRemoveFeat: DeleteLocalInt(oHide, NewSpellbookTemp_" + IntToString(nSubType) + ");"); + if(DEBUG) DoDebug("CheckAndRemoveFeat: Removing item property"); + } + else + { + DeleteLocalInt(oHide, "NewSpellbookTemp_" + IntToString(nSubType)); + if(DEBUG) DoDebug("CheckAndRemoveFeat: DeleteLocalInt(oHide, NewSpellbookTemp_" + IntToString(nSubType) + ");"); + } +} + +void WipeSpellbookHideFeats(object oPC) +{ + object oHide = GetPCSkin(oPC); + itemproperty ipTest = GetFirstItemProperty(oHide); + while(GetIsItemPropertyValid(ipTest)) + { + int nSubType = GetItemPropertySubType(ipTest); + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + ((nSubType > SPELLBOOK_IPRP_FEATS_START && nSubType < SPELLBOOK_IPRP_FEATS_END) || + (nSubType > SPELLBOOK_IPRP_FEATS_START2 && nSubType < SPELLBOOK_IPRP_FEATS_END2)) + ) + { + DelayCommand(1.0f, CheckAndRemoveFeat(oHide, ipTest)); + } + ipTest = GetNextItemProperty(oHide); + } +} + +void CheckNewSpellbooks(object oPC) +{ + WipeSpellbookHideFeats(oPC); + int i; + for(i = 1; i <= 8; i++) + { + int nClass = GetClassByPosition(i, oPC); + int nLevel = GetLevelByClass(nClass, oPC); + + if(DEBUG) DoDebug("CheckNewSpellbooks\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + //if bard/sorc newspellbook is disabled after selecting + //remove those from radial + if( (GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK) && nClass == CLASS_TYPE_BARD) + ||(GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK) && nClass == CLASS_TYPE_SORCERER)) + { + //do nothing + } + else if(nLevel) + { + //Aranea cast as sorcs + if(nClass == CLASS_TYPE_SHAPECHANGER + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) + nClass = CLASS_TYPE_SORCERER; + //raks cast as sorcs + if(nClass == CLASS_TYPE_OUTSIDER + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + nClass = CLASS_TYPE_SORCERER; + + //Arkamoi cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) + nClass = CLASS_TYPE_SORCERER; + + //Hobgoblin Warsouls cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + nClass = CLASS_TYPE_SORCERER; + + //Redspawn cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) + nClass = CLASS_TYPE_SORCERER; + + //Marrutact cast as sorcs + if(nClass == CLASS_TYPE_MONSTROUS + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT) + nClass = CLASS_TYPE_SORCERER; + + //Driders cast as sorcs + if(nClass == CLASS_TYPE_ABERRATION + && !GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) + nClass = CLASS_TYPE_SORCERER; + + //Gloura cast as bards + if(nClass == CLASS_TYPE_FEY + && !GetLevelByClass(CLASS_TYPE_BARD, oPC) + && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) + nClass = CLASS_TYPE_BARD; + //remove persistant locals used to track when all spells cast + string sArrayName = "NewSpellbookMem_"+IntToString(nClass); + if(persistant_array_exists(oPC, sArrayName)) + { + if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED) + { + int nSpellLevel, i, Max; + string sIDX, sSpellbookID, sClass = IntToString(nClass); + for(nSpellLevel = 0; nSpellLevel <= 9; nSpellLevel++) + { + sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + sClass; + Max = persistant_array_get_size(oPC, sIDX); + for(i = 0; i < Max; i++) + { + sSpellbookID = persistant_array_get_string(oPC, sIDX, i); + if(sSpellbookID != "") + { + DeletePersistantLocalString(oPC, sArrayName+"_"+sSpellbookID); + } + } + persistant_array_delete(oPC, sIDX); + } + } + else + { + persistant_array_delete(oPC, sArrayName); + persistant_array_create(oPC, sArrayName); + } + } + //delay it so wipespellbookhidefeats has time to start to run + //but before the deletes actually happen + DelayCommand(0.1, SetupSpells(oPC, nClass)); + } + } +} + +//NewSpellbookSpell() helper functions +int bTargetingAllowed(int nSpellID); +void CheckPrepSlots(int nClass, int nSpellID, int nSpellbookID, int bIsAction = FALSE); +void CheckSpontSlots(int nClass, int nSpellID, int nSpellSlotLevel, int bIsAction = FALSE); +void DoCleanUp(int nMetamagic); + +void CastSpontaneousSpell(int nClass, int bInstantSpell = FALSE) +{ + //get the spellbook ID + int nFakeSpellID = GetSpellId(); + int nSpellID = GetPowerFromSpellID(nFakeSpellID); + if(nSpellID == -1) nSpellID = 0; + + //Check the target first + if(!bTargetingAllowed(nSpellID)) + return; + + // if OBJECT_SELF is fighting - stop fighting and cast spell + if(GetCurrentAction() == ACTION_ATTACKOBJECT) + ClearAllActions(); + + //if its a subradial spell, get the master + int nMasterFakeSpellID = StringToInt(Get2DACache("spells", "Master", nFakeSpellID)); + if(!nMasterFakeSpellID) + nMasterFakeSpellID = nFakeSpellID; + + int nSpellbookID = SpellToSpellbookID(nMasterFakeSpellID); + + // Paranoia - It should not be possible to get here without having the spells available array existing + if(!persistant_array_exists(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass))) + { + if(DEBUG) DoDebug("ERROR: NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + " array does not exist"); + persistant_array_create(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass)); + } + + int nSpellLevel = StringToInt(Get2DACache(GetFileForClass(nClass), "Level", nSpellbookID)); + + // Make sure the caster has uses of this spell remaining + // 2009-9-20: Add metamagic feat abilities. -N-S + int nMetamagic = GetLocalInt(OBJECT_SELF, "MetamagicFeatAdjust"); + if(nMetamagic) + { + //Need to check if metamagic can be applied to a spell + int nMetaTest; + int nMetaType = HexToInt(Get2DACache("spells", "MetaMagic", nSpellID)); + + int nSpellSlotLevel = nSpellLevel; + switch(nMetamagic) + { + case METAMAGIC_NONE: nMetaTest = 1; break; //no need to change anything + case METAMAGIC_EMPOWER: nMetaTest = nMetaType & 1; nSpellLevel += 2; break; + case METAMAGIC_EXTEND: nMetaTest = nMetaType & 2; nSpellLevel += 1; break; + case METAMAGIC_MAXIMIZE: nMetaTest = nMetaType & 4; nSpellLevel += 3; break; + case METAMAGIC_QUICKEN: nMetaTest = nMetaType & 8; nSpellLevel += 4; break; + case METAMAGIC_SILENT: nMetaTest = nMetaType & 16; nSpellLevel += 1; break; + case METAMAGIC_STILL: nMetaTest = nMetaType & 32; nSpellLevel += 1; break; + } + + if(!nMetaTest)//can't use selected metamagic with this spell + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(OBJECT_SELF, "You can't use "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)))+"with selected metamagic.")); + nSpellLevel = nSpellSlotLevel; + } + else if(nSpellLevel > 9)//now test the spell level + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(OBJECT_SELF, "Modified spell level is to high! Casting spell without metamagic")); + nSpellLevel = nSpellSlotLevel; + } + else if(GetLocalInt(OBJECT_SELF, "PRC_metamagic_state") == 1) + SetLocalInt(OBJECT_SELF, "MetamagicFeatAdjust", 0); + } + + CheckSpontSlots(nClass, nSpellID, nSpellLevel); + if(GetLocalInt(OBJECT_SELF, "NSB_Cast")) + ActionDoCommand(CheckSpontSlots(nClass, nSpellID, nSpellLevel, TRUE)); + else + return; + + // Calculate DC. 10 + spell level on the casting class's list + DC increasing ability mod + //int nDC = 10 + nSpellLevel + GetDCAbilityModForClass(nClass, OBJECT_SELF); + // This is wrong and is breaking things, and is already calculated in the function it calls anyway - Strat + + //remove any old effects + //seems cheat-casting breaks hardcoded removal + //and cant remove effects because I dont know all the targets! + if(!bInstantSpell) + { + //Handle quicken metamagic and Duskblade's Quick Cast + if((nMetamagic & METAMAGIC_QUICKEN) || GetLocalInt(OBJECT_SELF, "QuickCast")) + { + //Adding Auto-Quicken III - deleted after casting has finished. + object oSkin = GetPCSkin(OBJECT_SELF); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpellID)) + StringToInt(Get2DACache("spells", "CastTime", nSpellID)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + DeleteLocalInt(OBJECT_SELF, "QuickCast"); + } + } + + //cast the spell + //dont need to override level, the spellscript will calculate it + //class is read from "NSB_Class" + ActionCastSpell(nSpellID, 0, -1, 0, nMetamagic, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstantSpell); + + //Clean up + ActionDoCommand(DoCleanUp(nMetamagic)); +} + +void CastPreparedSpell(int nClass, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE) +{ + object oPC = OBJECT_SELF; + + //get the spellbook ID + int nFakeSpellID = GetSpellId(); + int nSpellID = GetPowerFromSpellID(nFakeSpellID); + if(nSpellID == -1) nSpellID = 0; + + //Check the target first + if(!bTargetingAllowed(nSpellID)) + return; + + // if OBJECT_SELF is fighting - stop fighting and cast spell + if(GetCurrentAction() == ACTION_ATTACKOBJECT) + ClearAllActions(); + + //if its a subradial spell, get the master + int nMasterFakeSpellID = StringToInt(Get2DACache("spells", "Master", nFakeSpellID)); + if(!nMasterFakeSpellID) + nMasterFakeSpellID = nFakeSpellID; + + int nSpellbookID = SpellToSpellbookID(nMasterFakeSpellID); + + // Paranoia - It should not be possible to get here without having the spells available array existing + if(!persistant_array_exists(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass))) + { + if(DEBUG) DoDebug("ERROR: NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + " array does not exist"); + persistant_array_create(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass)); + } + + int nSpellLevel = StringToInt(Get2DACache(GetFileForClass(nClass), "Level", nSpellbookID)); + + // Make sure the caster has uses of this spell remaining + CheckPrepSlots(nClass, nSpellID, nSpellbookID); + if(GetLocalInt(OBJECT_SELF, "NSB_Cast")) + ActionDoCommand(CheckPrepSlots(nClass, nSpellID, nSpellbookID, TRUE)); + else + return; + + // Calculate DC. 10 + spell level on the casting class's list + DC increasing ability mod + //int nDC = 10 + nSpellLevel + GetDCAbilityModForClass(nClass, OBJECT_SELF); + // This is wrong and is breaking things, and is already calculated in the function it calls anyway - Strat + + //remove any old effects + //seems cheat-casting breaks hardcoded removal + //and cant remove effects because I dont know all the targets! + if(!bInstantSpell) + { + //Handle quicken metamagic and Duskblade's Quick Cast + if((nMetamagic & METAMAGIC_QUICKEN) || GetLocalInt(OBJECT_SELF, "QuickCast")) + { + //Adding Auto-Quicken III - deleted after casting has finished. + object oSkin = GetPCSkin(OBJECT_SELF); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpellID)) + StringToInt(Get2DACache("spells", "CastTime", nSpellID)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + DeleteLocalInt(OBJECT_SELF, "QuickCast"); + } + else if(nClass == CLASS_TYPE_HEALER) + { + if(GetHasFeat(FEAT_EFFORTLESS_HEALING) + && GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) + { + object oSkin = GetPCSkin(OBJECT_SELF); + //all spells from healing subschool except Close Wounds have casting time of 2.5s + float fCastDur = nSpellID == SPELL_CLOSE_WOUNDS ? 1.0f : 2.5f; + itemproperty ipImpCombatCast = ItemPropertyBonusFeat(IP_CONST_NSB_IMP_COMBAT_CAST); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipImpCombatCast, oSkin, fCastDur)); + } + } + } + + //cast the spell + //dont need to override level, the spellscript will calculate it + //class is read from "NSB_Class" + ActionCastSpell(nSpellID, 0, -1, 0, nMetamagic, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstantSpell); + + //Clean up + ActionDoCommand(DoCleanUp(nMetamagic)); +} + +void NewSpellbookSpell(int nClass, int nSpellbookType, int nMetamagic = METAMAGIC_NONE, int bInstantSpell = FALSE) +{ + object oPC = OBJECT_SELF; + + // if oPC is fighting - stop fighting and cast spell + if(GetCurrentAction(oPC) == ACTION_ATTACKOBJECT) + ClearAllActions(); + + //get the spellbook ID + int nFakeSpellID = GetSpellId(); + int nSpellID = GetPowerFromSpellID(nFakeSpellID); + if(nSpellID == -1) nSpellID = 0; + + //Check the target first + if(!bTargetingAllowed(nSpellID)) + return; + + //if its a subradial spell, get the master + int nMasterFakeSpellID = StringToInt(Get2DACache("spells", "Master", nFakeSpellID)); + if(!nMasterFakeSpellID) + nMasterFakeSpellID = nFakeSpellID; + + int nSpellbookID = SpellToSpellbookID(nMasterFakeSpellID); + + // Paranoia - It should not be possible to get here without having the spells available array existing + if(!persistant_array_exists(oPC, "NewSpellbookMem_" + IntToString(nClass))) + { + if(DEBUG) DoDebug("ERROR: NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + " array does not exist"); + persistant_array_create(oPC, "NewSpellbookMem_" + IntToString(nClass)); + } + + string sFile = GetFileForClass(nClass); + int nSpellLevel = StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + + // Make sure the caster has uses of this spell remaining + // 2009-9-20: Add metamagic feat abilities. -N-S + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + CheckPrepSlots(nClass, nSpellID, nSpellbookID); + if(GetLocalInt(oPC, "NSB_Cast")) + ActionDoCommand(CheckPrepSlots(nClass, nSpellID, nSpellbookID, TRUE)); + else + return; + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nMetamagic = GetLocalInt(oPC, "MetamagicFeatAdjust"); + if(nMetamagic) + { + //Need to check if metamagic can be applied to a spell + int nMetaTest; + int nMetaType = HexToInt(Get2DACache("spells", "MetaMagic", nSpellID)); + + int nSpellSlotLevel = nSpellLevel; + switch(nMetamagic) + { + case METAMAGIC_NONE: nMetaTest = 1; break; //no need to change anything + case METAMAGIC_EMPOWER: nMetaTest = nMetaType & 1; nSpellLevel += 2; break; + case METAMAGIC_EXTEND: nMetaTest = nMetaType & 2; nSpellLevel += 1; break; + case METAMAGIC_MAXIMIZE: nMetaTest = nMetaType & 4; nSpellLevel += 3; break; + case METAMAGIC_QUICKEN: nMetaTest = nMetaType & 8; nSpellLevel += 4; break; + case METAMAGIC_SILENT: nMetaTest = nMetaType & 16; nSpellLevel += 1; break; + case METAMAGIC_STILL: nMetaTest = nMetaType & 32; nSpellLevel += 1; break; + } + + if(!nMetaTest)//can't use selected metamagic with this spell + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "You can't use "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID)))+"with selected metamagic.")); + nSpellLevel = nSpellSlotLevel; + } + else if(nSpellLevel > 9)//now test the spell level + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "Modified spell level is to high! Casting spell without metamagic")); + nSpellLevel = nSpellSlotLevel; + } + else if(GetLocalInt(oPC, "PRC_metamagic_state") == 1) + SetLocalInt(oPC, "MetamagicFeatAdjust", 0); + } + + CheckSpontSlots(nClass, nSpellID, nSpellLevel); + if(GetLocalInt(oPC, "NSB_Cast")) + ActionDoCommand(CheckSpontSlots(nClass, nSpellID, nSpellLevel, TRUE)); + else + return; + } + + // Calculate DC. 10 + spell level on the casting class's list + DC increasing ability mod + //int nDC = 10 + nSpellLevel + GetDCAbilityModForClass(nClass, OBJECT_SELF); + // This is wrong and is breaking things, and is already calculated in the function it calls anyway - Strat + + //remove any old effects + //seems cheat-casting breaks hardcoded removal + //and cant remove effects because I dont know all the targets! + if(!bInstantSpell) + { + //Handle quicken metamagic and Duskblade's Quick Cast + if((nMetamagic & METAMAGIC_QUICKEN) || GetLocalInt(oPC, "QuickCast")) + { + //Adding Auto-Quicken III - deleted after casting has finished. + object oSkin = GetPCSkin(oPC); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpellID)) + StringToInt(Get2DACache("spells", "CastTime", nSpellID)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + DeleteLocalInt(oPC, "QuickCast"); + } + else if(nClass == CLASS_TYPE_HEALER) + { + if(GetHasFeat(FEAT_EFFORTLESS_HEALING) + && GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) + { + object oSkin = GetPCSkin(oPC); + //all spells from healing subschool except Close Wounds have casting time of 2.5s + float fCastDur = nSpellID == SPELL_CLOSE_WOUNDS ? 1.0f : 2.5f; + itemproperty ipImpCombatCast = ItemPropertyBonusFeat(IP_CONST_NSB_IMP_COMBAT_CAST); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipImpCombatCast, oSkin, fCastDur)); + } + } + } + + //cast the spell + //dont need to override level, the spellscript will calculate it + //class is read from "NSB_Class" + ActionCastSpell(nSpellID, 0, -1, 0, nMetamagic, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstantSpell); + + //Clean up + ActionDoCommand(DoCleanUp(nMetamagic)); +} + +int bTargetingAllowed(int nSpellID) +{ + object oTarget = GetSpellTargetObject(); + if(GetIsObjectValid(oTarget)) + { + int nTargetType = ~(HexToInt(Get2DACache("spells", "TargetType", nSpellID))); + + //test targetting self + if(oTarget == OBJECT_SELF) + { + if(nTargetType & 1) + { + if(DEBUG) DoDebug("bTargetingAllowed: You cannot target yourself."); + return FALSE; + } + } + //test targetting others + else if(GetObjectType(oTarget) == OBJECT_TYPE_CREATURE) + { + if(nTargetType & 2) + { + if(DEBUG) DoDebug("bTargetingAllowed: You cannot target creatures."); + return FALSE; + } + } + } + return TRUE; +} + +void CheckPrepSlots(int nClass, int nSpellID, int nSpellbookID, int bIsAction = FALSE) +{ + DeleteLocalInt(OBJECT_SELF, "NSB_Cast"); + int nCount = persistant_array_get_int(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass), nSpellbookID); + if(DEBUG) DoDebug("NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(nSpellbookID) + "] = " + IntToString(nCount)); + if(nCount < 1) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID))); + // "You have no castings of " + sSpellName + " remaining" + string sMessage = ReplaceChars(GetStringByStrRef(16828411), "", sSpellName); + + FloatingTextStringOnCreature(sMessage, OBJECT_SELF, FALSE); + if(bIsAction) + ClearAllActions(); + } + else + { + SetLocalInt(OBJECT_SELF, "NSB_Cast", 1); + if(bIsAction) + { + SetLocalInt(OBJECT_SELF, "NSB_Class", nClass); + SetLocalInt(OBJECT_SELF, "NSB_SpellbookID", nSpellbookID); + } + } +} + +void CheckSpontSlots(int nClass, int nSpellID, int nSpellSlotLevel, int bIsAction = FALSE) +{ + DeleteLocalInt(OBJECT_SELF, "NSB_Cast"); + int nCount = persistant_array_get_int(OBJECT_SELF, "NewSpellbookMem_" + IntToString(nClass), nSpellSlotLevel); + if(DEBUG) DoDebug("NewSpellbookSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(nSpellSlotLevel) + "] = " + IntToString(nCount)); + if(nCount < 1) + { + // "You have no castings of spells of level " + IntToString(nSpellLevel) + " remaining" + string sMessage = ReplaceChars(GetStringByStrRef(16828409), "", IntToString(nSpellSlotLevel)); + FloatingTextStringOnCreature(sMessage, OBJECT_SELF, FALSE); + if(bIsAction) + ClearAllActions(); + } + else + { + SetLocalInt(OBJECT_SELF, "NSB_Cast", 1); + if(bIsAction) + { + SetLocalInt(OBJECT_SELF, "NSB_Class", nClass); + SetLocalInt(OBJECT_SELF, "NSB_SpellLevel", nSpellSlotLevel); + } + } +} + +void DoCleanUp(int nMetamagic) +{ + if(nMetamagic & METAMAGIC_QUICKEN) + { + object oSkin = GetPCSkin(OBJECT_SELF); + RemoveItemProperty(oSkin, ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN)); + } + DeleteLocalInt(OBJECT_SELF, "NSB_Class"); + DeleteLocalInt(OBJECT_SELF, "NSB_SpellLevel"); + DeleteLocalInt(OBJECT_SELF, "NSB_SpellbookID"); +} + +//:: Test Void +//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_npc.nss b/src/include/inc_npc.nss new file mode 100644 index 0000000..6b99a4a --- /dev/null +++ b/src/include/inc_npc.nss @@ -0,0 +1,223 @@ +//:://///////////////////////////////////////////// +//:: NPC associate include +//:: inc_npc +//::////////////////////////////////////////////// +/* +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +// Get the master of oAssociate. +object GetMasterNPC(object oAssociate=OBJECT_SELF); + +// Returns the associate type of the specified creature. +// - Returns ASSOCIATE_TYPE_NONE if the creature is not the associate of anyone. +int GetAssociateTypeNPC( object oAssociate ); + +// Get the henchman belonging to oMaster. +// * Return OBJECT_INVALID if oMaster does not have a henchman. +// -nNth: Which henchman to return. +object GetHenchmanNPC(object oMaster=OBJECT_SELF,int nNth=1); + +// Get the associate of type nAssociateType belonging to oMaster. +// - nAssociateType: ASSOCIATE_TYPE_* +// - nMaster +// - nTh: Which associate of the specified type to return +// * Returns OBJECT_INVALID if no such associate exists. +object GetAssociateNPC(int nAssociateType, object oMaster=OBJECT_SELF, int nTh=1); + +// Returns TRUE if the specified condition flag is set on +// the associate. +int GetAssociateStateNPC(int nCondition, object oAssoc=OBJECT_SELF); + +// Determine if this henchman is currently dying +int GetIsHenchmanDyingNPC(object oHench=OBJECT_SELF); + +// Determine if Should I Heal My Master +int GetAssociateHealMasterNPC(); + +// Create the next AssociateType creature +object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,location loc,string sTag=""); + +// Create a AssociateType creature +object CreateLocalNPC(object oMaster,int nAssociateType,string sTemplate,location loc,int Nth=1,string sTag=""); + + +#include "prc_inc_function" +#include "x0_i0_assoc" + +void SetLocalNPC(object oMaster,object oAssociate,int nAssociateType ,int nNth=1) +{ + SetLocalObject(oAssociate, "oMaster", oMaster); + SetLocalInt(oAssociate, "iAssocType", nAssociateType); + SetLocalObject(oMaster, IntToString(nAssociateType)+"oHench"+IntToString(nNth), oAssociate); + SetLocalInt(oAssociate, "iAssocNth", nNth); + +} + +void DeleteLocalNPC(object oAssociate=OBJECT_SELF) +{ + int nType = GetLocalInt(oAssociate, "iAssocType"); + object oMaster = GetMasterNPC(oAssociate); + int Nth = GetLocalInt(oAssociate, "iAssocNth"); + + DeleteLocalInt(oMaster, IntToString(nType)+"oHench"+IntToString(Nth)); + DeleteLocalInt(oAssociate, "iAssocNth"); + DeleteLocalObject(oAssociate, "oMaster"); + DeleteLocalInt(oAssociate, "iAssocType"); +} + +void DestroySummon(object oSummon) +{ + effect eVis = EffectVisualEffect(VFX_IMP_UNSUMMON); + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eVis, GetLocation(oSummon)); + DeleteLocalNPC(oSummon); + DestroyObject(oSummon); +} + + +object CreateLocalNPC(object oMaster,int nAssociateType,string sTemplate,location loc,int Nth=1,string sTag="") +{ + object oSummon=CreateObject(OBJECT_TYPE_CREATURE,sTemplate,loc,FALSE,sTag); + + SetLocalNPC(oMaster,oSummon,nAssociateType ,Nth); + SetAssociateState(NW_ASC_HAVE_MASTER,TRUE,oSummon); + SetAssociateState(NW_ASC_DISTANCE_2_METERS); + SetAssociateState(NW_ASC_DISTANCE_4_METERS, FALSE); + SetAssociateState(NW_ASC_DISTANCE_6_METERS, FALSE); + + if (nAssociateType == ASSOCIATE_TYPE_FAMILIAR) SetLocalInt(oMaster, "FamiliarToTheDeath", 100); + if (nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION) SetLocalInt(oMaster, "AniCompToTheDeath", 100); + + effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetLocation(oSummon)); + + return oSummon; +} + +object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,location loc,string sTag="") +{ + object oSummon=CreateObject(OBJECT_TYPE_CREATURE,sTemplate,loc,FALSE,sTag); + int nCount=1; + + while (GetIsObjectValid(GetAssociateNPC(ASSOCIATE_TYPE_SUMMONED,OBJECT_SELF,nCount))) + { + nCount++; + SendMessageToPC(OBJECT_SELF," nCount:"+IntToString(nCount)); + } + + SetLocalObject(oSummon, "oMaster", oMaster); + SetLocalInt(oSummon, "iAssocType", nAssociateType); + SetLocalObject(oMaster, IntToString(nAssociateType)+"oHench"+IntToString(nCount), oSummon); + SetLocalInt(oSummon, "iAssocNth", nCount); + + SetAssociateState(NW_ASC_HAVE_MASTER,TRUE,oSummon); + SetAssociateState(NW_ASC_DISTANCE_2_METERS); + SetAssociateState(NW_ASC_DISTANCE_4_METERS, FALSE); + SetAssociateState(NW_ASC_DISTANCE_6_METERS, FALSE); + + if (nAssociateType ==ASSOCIATE_TYPE_FAMILIAR) SetLocalInt(oMaster, "FamiliarToTheDeath", 100); + if (nAssociateType ==ASSOCIATE_TYPE_ANIMALCOMPANION) SetLocalInt(oMaster, "AniCompToTheDeath", 100); + + return oSummon; + +} +object GetMasterNPC(object oAssociate=OBJECT_SELF) +{ + object oMaster = GetLocalObject(oAssociate, "oMaster"); + + if (GetIsObjectValid(oMaster)) + return oMaster; + else + return GetMaster(oAssociate); +} + +int GetAssociateTypeNPC( object oAssociate ) +{ + int iType = GetLocalInt(oAssociate, "iAssocType"); + if (iType) + return iType; + else + return GetAssociateType(oAssociate); +} + +object GetHenchmanNPC(object oMaster=OBJECT_SELF,int nNth=1) +{ + object oAssociate = GetLocalObject(oMaster,IntToString(ASSOCIATE_TYPE_HENCHMAN)+"oHench"+IntToString(nNth)); + + if (GetIsObjectValid(oAssociate)) + return oAssociate; + else + return GetHenchman(oMaster,nNth); +} + +object GetAssociateNPC(int nAssociateType, object oMaster=OBJECT_SELF, int nTh=1) +{ + object oAssociate = GetLocalObject(oMaster,IntToString(nAssociateType)+"oHench"+IntToString(nTh)); + + if (GetIsObjectValid(oAssociate)) + return oAssociate; + else + return GetAssociate(nAssociateType,oMaster,nTh); +} + +int GetAssociateStateNPC(int nCondition, object oAssoc=OBJECT_SELF) +{ + //SpawnScriptDebugger(); + + if(nCondition == NW_ASC_HAVE_MASTER) + { + if(GetIsObjectValid(GetMasterNPC(oAssoc))) + return TRUE; + } + else + { + int nPlot = GetLocalInt(oAssoc, sAssociateMasterConditionVarname); + + if(nPlot & nCondition) + return TRUE; + } + return FALSE; +} + +int GetIsHenchmanDyingNPC(object oHench=OBJECT_SELF) +{ + int bHenchmanDying = GetAssociateStateNPC(NW_ASC_MODE_DYING, oHench); + + if (bHenchmanDying == TRUE) + { + return TRUE; + } + else + { + return FALSE; + } +} + +int GetAssociateHealMasterNPC() +{ + if(GetAssociateStateNPC(NW_ASC_HAVE_MASTER)) + { + object oMaster = GetMasterNPC(); + int nLoss = GetPercentageHPLoss(oMaster); + + if(!GetIsDead(oMaster)) + { + if(GetAssociateStateNPC(NW_ASC_HEAL_AT_75) && nLoss <= 75) + { + return TRUE; + } + else if(GetAssociateStateNPC(NW_ASC_HEAL_AT_50) && nLoss <= 50) + { + return TRUE; + } + else if(GetAssociateStateNPC(NW_ASC_HEAL_AT_25) && nLoss <= 25) + { + return TRUE; + } + } + } + return FALSE; +} + + diff --git a/src/include/inc_nwnx_funcs.nss b/src/include/inc_nwnx_funcs.nss new file mode 100644 index 0000000..ad48c0c --- /dev/null +++ b/src/include/inc_nwnx_funcs.nss @@ -0,0 +1,284 @@ +//////////////////////////////////////////////////////////////////////////////////// +/* Combined wrappers for both Win32 and Linux NWNX funcs */ +//////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +// Used in OnModuleLoad event to auto-detect if NWNX_Funcs plugin is enabled +void PRC_Funcs_Init(object oModule); + +// Sets the amount of hitpoints oObject has currently to nHP +void PRC_Funcs_SetCurrentHitPoints(object oCreature, int nHP); + +// Sets the amount of hitpoints oObject can maximally have to nHP +void PRC_Funcs_SetMaxHitPoints(object oCreature, int nHP); + +// Changes the skill ranks for nSkill on oObject by iValue +void PRC_Funcs_ModSkill(object oCreature, int nSkill, int nValue); + +// Sets a base ability score nAbility (ABILITY_STRENGTH, ABILITY_DEXTERITY, etc) to nValue +// The range of nValue is 3 to 255 +void PRC_Funcs_SetAbilityScore(object oCreature, int nAbility, int nValue); + +// Changes a base ability score nAbility (ABILITY_STRENGTH, ABILITY_DEXTERITY, etc) by nValue +void PRC_Funcs_ModAbilityScore(object oCreature, int nAbility, int nValue); + +// Adds a feat to oObject's general featlist +// If nLevel is greater than 0 the feat is also added to the featlist for that level +void PRC_Funcs_AddFeat(object oCreature, int nFeat, int nLevel=0); + +// Checks if oCreature inherently knows a feat (as opposed to a feat given from an equipped item) +// Returns FALSE if oCreature does not know the feat, TRUE if the feat is known +// The return value (if greater than 0) also denotes the position of the feat in the general feat list offset by +1 +int PRC_Funcs_GetFeatKnown(object oCreature, int nFeat); + +// Changes the saving throw bonus nSavingThrow of oObject by nValue; +void PRC_Funcs_ModSavingThrowBonus(object oCreature, int nSavingThrow, int nValue); + +// Sets the base natural AC +void PRC_Funcs_SetBaseNaturalAC(object oCreature, int nValue); + +// Returns the base natural AC +int PRC_Funcs_GetBaseNaturalAC(object oCreature); + +// Sets the specialist spell school of a Wizard +void PRC_Funcs_SetWizardSpecialization(object oCreature, int iSpecialization); + +// Returns the specialist spell school of a Wizard +int PRC_Funcs_GetWizardSpecialization(object oCreature); + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int _PRC_NWNXFuncsZero(object oObject, string sFunc) { + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + SetLocalString(oObject, sFunc, "-"); + else if (nVersion == 2) + SetLocalString(oObject, sFunc, " "); + int nResult = StringToInt(GetLocalString(oObject, sFunc)); + DeleteLocalString(oObject, sFunc); + return nResult; +} + +int _PRC_NWNXFuncsOne(object oObject, string sFunc, int nVal1) { + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + SetLocalString(oObject, sFunc, IntToString(nVal1)); + else if (nVersion == 2) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " "); + int nResult = StringToInt(GetLocalString(oObject, sFunc)); + DeleteLocalString(oObject, sFunc); + return nResult; +} + +int _PRC_NWNXFuncsTwo(object oObject, string sFunc, int nVal1, int nVal2) { + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " " + IntToString(nVal2)); + else if (nVersion == 2) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " " + IntToString(nVal2) + " "); + int nResult = StringToInt(GetLocalString(oObject, sFunc)); + DeleteLocalString(oObject, sFunc); + return nResult; +} + +int _PRC_NWNXFuncsThree(object oObject, string sFunc, int nVal1, int nVal2, int nVal3) { + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " " + IntToString(nVal2) + " " + IntToString(nVal3)); + else if (nVersion == 2) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " " + IntToString(nVal2) + " " + IntToString(nVal3) + " "); + int nResult = StringToInt(GetLocalString(oObject, sFunc)); + DeleteLocalString(oObject, sFunc); + return nResult; +} + +int _PRC_NWNXFuncsFour(object oObject, string sFunc, int nVal1, int nVal2, int nVal3, int nVal4) { + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " " + IntToString(nVal2) + " " + IntToString(nVal3) + " " + IntToString(nVal4)); + else if (nVersion == 2) + SetLocalString(oObject, sFunc, IntToString(nVal1) + " " + IntToString(nVal2) + " " + IntToString(nVal3) + " " + IntToString(nVal4) + " "); + int nResult = StringToInt(GetLocalString(oObject, sFunc)); + DeleteLocalString(oObject, sFunc); + return nResult; +} + +void PRC_Funcs_Init(object oModule) +{ + //Only NWNX for Win32 implements GetHasLocalVariable, so if this succeeds, that's what we're using + string sTestVariable = "PRC_TEST_NWNX_FUNCS"; + SetLocalString(oModule, sTestVariable, "1"); + SetLocalString(oModule, "NWNX!FUNCS!GETHASLOCALVARIABLE", sTestVariable + " 3"); //3 is the variable type + //NOTE: don't use _PRC_NWNXFuncsX functions here; they depend on the PRC_NWNX_FUNCS that we haven't set yet + int iTest = StringToInt(GetLocalString(oModule, "NWNX!FUNCS!GETHASLOCALVARIABLE")); + DeleteLocalString(oModule, "NWNX!FUNCS!GETHASLOCALVARIABLE"); + DeleteLocalString(oModule, sTestVariable); + + if (iTest) + SetLocalInt(oModule, "PRC_NWNX_FUNCS", 1); //1 == win32 + else + { + //NWNX GetLocalVariableCount behaves differently for win32 and linux, + //but we know we're not using the win32 version (because the above check failed), + //so try the linux version of GetLocalVariableCount. + //Since PRC_VERSION is a module-level variable, and we know it's defined + //by OnLoad before it calls this function, the variable count will be at + //least 1 if we're using NWNX. If not, 0 will be returned to indicate that + //the call failed because NWNX funcs is not present. + string sFunc = "NWNX!FUNCS!GETLOCALVARIABLECOUNT"; + SetLocalString(oModule, sFunc, " "); + //NOTE: don't use _PRC_NWNXFuncsX functions here; they depend on the PRC_NWNX_FUNCS that we haven't set yet + //NOTE: the number being returned by GetLocalVariableCount() on Linux seems bogus to me (it's huge, e.g. 294,654,504), + //but it does seem to be reliably zero when NWNX funcs is not present, and so far has been reliably non-zero + //when it is present. That's all we need here. + //Info: on win32, GetLocalVariableCount() is returning much more reasonable numbers (e.g. 1707). + int nVariables = StringToInt(GetLocalString(oModule, sFunc)); + DeleteLocalString(oModule, sFunc); + if (nVariables) + SetLocalInt(oModule, "PRC_NWNX_FUNCS", 2); //2 == linux + } +} + +void PRC_Funcs_SetMaxHitPoints(object oCreature, int nHP) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1 || nVersion == 2) + { + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETMAXHITPOINTS", nHP); + DeleteLocalString(oCreature, "NWNX!FUNCS!SETMAXHITPOINTS"); + } +} + +void PRC_Funcs_ModSkill(object oCreature, int nSkill, int nValue) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + _PRC_NWNXFuncsThree(oCreature, "NWNX!FUNCS!SETSKILL", nSkill, nValue, 1); //The 1 is a flag specifying modify instead of set + else if (nVersion == 2) + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!MODIFYSKILLRANK", nSkill, nValue); +} + +void PRC_Funcs_SetAbilityScore(object oCreature, int nAbility, int nValue) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + _PRC_NWNXFuncsFour(oCreature, "NWNX!FUNCS!SETABILITYSCORE", nAbility, nValue, 0, 0); //The first 0 is a flag specifying set instead of modify + else if (nVersion == 2) + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!SETABILITYSCORE", nAbility, nValue); +} + +void PRC_Funcs_ModAbilityScore(object oCreature, int nAbility, int nValue) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + _PRC_NWNXFuncsFour(oCreature, "NWNX!FUNCS!SETABILITYSCORE", nAbility, nValue, 1, 0); //The 1 is a flag specifying modify instead of set + else if (nVersion == 2) + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!MODIFYABILITYSCORE", nAbility, nValue); +} + +void PRC_Funcs_AddFeat(object oCreature, int nFeat, int nLevel=0) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + { + if (!nLevel) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!ADDFEAT", nFeat); + else if(nLevel > 0) + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!ADDFEATATLEVEL", nLevel, nFeat); + } + else if (nVersion == 2) + { + if (!nLevel) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!ADDKNOWNFEAT", nFeat); + else if(nLevel > 0) + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!ADDKNOWNFEATATLEVEL", nLevel, nFeat); + } +} + +int PRC_Funcs_GetFeatKnown(object oCreature, int nFeatIndex) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + return _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!GETFEATKNOWN", nFeatIndex); + else if (nVersion == 2) + return _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!GETKNOWNFEAT", nFeatIndex); + return 0; +} + +void PRC_Funcs_ModSavingThrowBonus(object oCreature, int nSavingThrow, int nValue) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + _PRC_NWNXFuncsThree(oCreature, "NWNX!FUNCS!SETSAVINGTHROWBONUS", nSavingThrow, nValue, 1); //The 1 is a flag specifying modify instead of set + else if (nVersion == 2) + { + int nNewValue = _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!GETSAVINGTHROWBONUS", nSavingThrow) + nValue; + if (nNewValue < 0) + nNewValue = 0; + else if (nNewValue > 127) + nNewValue = 127; + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!SETSAVINGTHROWBONUS", nSavingThrow, nNewValue); + } +} + +void PRC_Funcs_SetBaseNaturalAC(object oCreature, int nValue) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + _PRC_NWNXFuncsTwo(oCreature, "NWNX!FUNCS!SETBASEAC", nValue, AC_NATURAL_BONUS); + else if (nVersion == 2) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETACNATURALBASE", nValue); +} + +int PRC_Funcs_GetBaseNaturalAC(object oCreature) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + return _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!GETBASEAC", AC_NATURAL_BONUS); + else if (nVersion == 2) + return _PRC_NWNXFuncsZero(oCreature, "NWNX!FUNCS!GETACNATURALBASE"); + return 0; +} + +void PRC_Funcs_SetCurrentHitPoints(object oCreature, int nHP) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1 || nVersion == 2) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETCURRENTHITPOINTS", nHP); +} + +void PRC_Funcs_SetCreatureSize (object oCreature, int nSize) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1 || nVersion == 2) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETCREATURESIZE", nSize); +} + +void PRC_Funcs_SetRace(object oCreature, int nRace) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETRACE", nRace); + else if (nVersion == 2) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETRACIALTYPE", nRace); +} + +void PRC_Funcs_SetWizardSpecialization(object oCreature, int iSpecialization) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1 || nVersion == 2) + _PRC_NWNXFuncsOne(oCreature, "NWNX!FUNCS!SETWIZARDSPECIALIZATION", iSpecialization); +} + +int PRC_Funcs_GetWizardSpecialization(object oCreature) +{ + int nVersion = GetLocalInt(GetModule(), "PRC_NWNX_FUNCS"); + if (nVersion == 1 || nVersion == 2) + return _PRC_NWNXFuncsZero(oCreature, "NWNX!FUNCS!GETWIZARDSPECIALIZATION"); + return 0; +} \ No newline at end of file diff --git a/src/include/inc_pers_array.nss b/src/include/inc_pers_array.nss new file mode 100644 index 0000000..3e4b5df --- /dev/null +++ b/src/include/inc_pers_array.nss @@ -0,0 +1,340 @@ +//:://///////////////////////////////////////////// +//:: Persistant array simulation include +//:: inc_pers_array +//::////////////////////////////////////////////// +/** @file + Persistant array simulation include + + This file defines a set of functions for creating + and manipulating persistant arrays, which are + implemented as persistant local variables on some + holder creature. + + + Notes: + + * Arrays are dynamic and may be increased in size by just _set_'ing a new + element + * There are no restrictions on what is in the array (can have multiple + types in the same array) + * Arrays start at index 0 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +///////////////////////////////////// +// Functions +///////////////////////////////////// + +/** + * Creates a new persistant array on the given storage creature. + * If an array with the same name already exists, the function + * errors. + * + * @param store The creature to use as holder for the array + * @param name The name of the array + * @return SDL_SUCCESS if the array was successfully created, + * one of SDL_ERROR_* on error. + */ +int persistant_array_create(object store, string name); + +/** + * Deletes a persistant array, erasing all it's entries. + * + * @param store The creature which holds the array to delete + * @param name The name of the array + * @return SDL_SUCCESS if the array was successfully deleted, + * one of SDL_ERROR_* on error + */ +int persistant_array_delete(object store, string name); + +/** + * Stores a string in a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to store the string at + * @param entry The string to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int persistant_array_set_string(object store, string name, int i, string entry); + +/** + * Stores an integer in a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to store the integer at + * @param entry The integer to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int persistant_array_set_int(object store, string name, int i, int entry); + +/** + * Stores a float in a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to store the float at + * @param entry The float to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int persistant_array_set_float(object store, string name, int i, float entry); + +/** + * Stores an object in a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to store the object at + * @param entry The object to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int persistant_array_set_object(object store, string name, int i, object entry); + +/** + * Gets a string from a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * "" on error + */ +string persistant_array_get_string(object store, string name, int i); + +/** + * Gets an integer from a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to retrieve the integer from + * @return The value contained at the index on success, + * 0 on error + */ +int persistant_array_get_int(object store, string name, int i); + +/** + * Gets a float from a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to retrieve the float from + * @return The value contained at the index on success, + * 0.0f on error + */ +float persistant_array_get_float(object store, string name, int i); + +/** + * Gets an object from a persistant array. + * + * @param store The creature holding the array + * @param name The name of the array + * @param i The index to retrieve the object from + * @return The value contained at the index on success, + * OBJECT_INVALID on error + */ +object persistant_array_get_object(object store, string name, int i); + +/** + * Removes all entries in the array with indexes greater than or equal to + * the new size and sets the array size to be equal to the new size. + * + * @param store The creature holding the array + * @param name The name of the array + * @param size_new The new number of entries in the array + * @return SDL_SUCCESS on successful resize, SDL_ERROR_* on + * error + */ +int persistant_array_shrink(object store, string name, int size_new); + +/** + * Gets the current size of the array. This is one greater + * than the index of highest indexed element the array + * has contained since the last array_shrink or the new size + * specified by the last array_shrink, whichever is greater. + * + * @param store The creature holding the array + * @param name The name of the array + * @return The size of the array, or -1 if the specified + * array does not exist. + */ +int persistant_array_get_size(object store, string name); + +/** + * Checks whether the given persistant array exists. + * + * @param store The creature holding the array + * @param name The name of the array + * @return TRUE if the array exists, FALSE otherwise. + */ +int persistant_array_exists(object store, string name); + +///////////////////////////////////// +// Includes +///////////////////////////////////// + +#include "inc_persist_loca" +#include "prc_inc_array" // yes this is also got via inc_persist_loca if rather indirectly + +///////////////////////////////////// +// Implementation +///////////////////////////////////// + +int persistant_array_create(object store, string name) +{ + // error checking + if(!GetIsObjectValid(store)) + return SDL_ERROR_NOT_VALID_OBJECT; + else if(persistant_array_exists(store,name)) + return SDL_ERROR_ALREADY_EXISTS; + else + { + // Initialize the size (always one greater than the actual size) + SetPersistantLocalInt(store,name,1); + return SDL_SUCCESS; + } +} + +void persistant_array_delete_loop(object store, string name, int nMin, int nMax) +{ + int i = nMin; + while(i < nMin + 250 && i < nMax) + { + DeletePersistantLocalString(store,name+"_"+IntToString(i)); + + // just in case, delete possible object names + DeletePersistantLocalObject(store,name+"_"+IntToString(i)+"_OBJECT"); + i++; + } + // delay continuation to avoid TMI + if(i < nMax) + DelayCommand(0.0, persistant_array_delete_loop(store, name, i, nMax)); +} + +int persistant_array_delete(object store, string name) +{ + // error checking + int size=GetPersistantLocalInt(store,name); + if (size==0) + return SDL_ERROR_DOES_NOT_EXIST; + + persistant_array_delete_loop(store, name, 0, size+5); + + DeletePersistantLocalInt(store,name); + + return SDL_SUCCESS; +} + +int persistant_array_set_string(object store, string name, int i, string entry) +{ + int size=GetPersistantLocalInt(store,name); + if(size == 0) + return SDL_ERROR_DOES_NOT_EXIST; + if(i < 0) + return SDL_ERROR_OUT_OF_BOUNDS; + + SetPersistantLocalString(store,name+"_"+IntToString(i),entry); + + // save size if we've enlarged it + if (i+2>size) + SetPersistantLocalInt(store,name,i+2); + + return SDL_SUCCESS; +} + +int persistant_array_set_int(object store, string name, int i, int entry) +{ + return persistant_array_set_string(store,name,i,IntToString(entry)); +} + +int persistant_array_set_float(object store, string name, int i, float entry) +{ + return persistant_array_set_string(store,name,i,FloatToString(entry)); +} + +int persistant_array_set_object(object store, string name, int i, object entry) +{ + // object is a little more complicated. + // we want to create an object as a local variable too + if (!GetIsObjectValid(entry)) + return SDL_ERROR_NOT_VALID_OBJECT; + + int results = persistant_array_set_string(store,name,i,"OBJECT"); + if (results==SDL_SUCCESS) + SetPersistantLocalObject(store,name+"_"+IntToString(i)+"_OBJECT",entry); + + return results; +} + +string persistant_array_get_string(object store, string name, int i) +{ + // error checking + int size=GetPersistantLocalInt(store,name); + if (size==0 || i>size || i < 0) + return ""; + + return GetPersistantLocalString(store,name+"_"+IntToString(i)); +} + +int persistant_array_get_int(object store, string name, int i) +{ + return StringToInt(persistant_array_get_string(store,name,i)); +} + +float persistant_array_get_float(object store, string name, int i) +{ + return StringToFloat(persistant_array_get_string(store,name,i)); +} + +object persistant_array_get_object(object store, string name, int i) +{ + if(persistant_array_get_string(store, name, i) == "OBJECT") + return GetPersistantLocalObject(store,name+"_"+IntToString(i)+"_OBJECT"); + else + return OBJECT_INVALID; +} + +int persistant_array_shrink(object store, string name, int size_new) +{ + // Get the current size value + int size = GetPersistantLocalInt(store, name); + // Error check - non-existent array + if(size == 0) + return SDL_ERROR_DOES_NOT_EXIST; + // If the new number of elements is equal to or greater than the current number of elements, autosuccess + if((size - 1) <= size_new) + return SDL_SUCCESS; + + // Delete entries that are outside the new array bounds + int i; + for(i = size_new; i < size; i++) + { + DeletePersistantLocalString(store, name+"_"+IntToString(i)); + + // just in case, delete possible object names + DeletePersistantLocalObject(store, name+"_"+IntToString(i)+"_OBJECT"); + } + + // Store the new size, with the +1 existence marker + SetPersistantLocalInt(store, name, size_new + 1); + + return SDL_SUCCESS; +} + +int persistant_array_get_size(object store, string name) +{ + return GetPersistantLocalInt(store,name)-1; +} + +int persistant_array_exists(object store, string name) +{ + if (GetPersistantLocalInt(store,name)) + return TRUE; + else + return FALSE; +} + +// Test main +//void main(){} diff --git a/src/include/inc_persist_loca.nss b/src/include/inc_persist_loca.nss new file mode 100644 index 0000000..6eddc6a --- /dev/null +++ b/src/include/inc_persist_loca.nss @@ -0,0 +1,436 @@ +//:://///////////////////////////////////////////// +//:: Persistant local variables include +//:: inc_persist_loca +//::////////////////////////////////////////////// +/** @file + A set of functions for storing local variables + on a token item stored in the creature's skin. + Since local variables on items within containers + are not stripped during serialization, these + variables persist across module changes and + server resets. + + These functions work on NPCs in addition to PCs, + but the persitence is mostly useless for them, + since NPCs are usually not serialized in a way + that would remove normal locals from them, if + they are serialized at all. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Adapted by ebonfowl to fix the Beamdog local variable bug +//:: Functions all still intuitively work the same way +//:: Only difference is the variables all run through SQL rather than via the hide token +//:: Dedicated to Edgar, the real Ebonfowl +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gets the token item inside the given creature's hide, on which the persistant + * variables are stored on. + * If a token does not exist already, one is created. + * WARNING: If called on a non-creature object, returns the object itself. + * + * @param oPC The creature whose storage token to get + * @param bAMS - TRUE will return special token for alternate magic system buckup info + * @return The creature's storage token + * + * GetNSBToken - special token for New Spellbook System information + */ +//object GetHideToken(object oPC, int bAMS = FALSE); + +/** + * Set a local string variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param sValue The value to set the string local to + */ +void SetPersistantLocalString(object oPC, string sName, string sValue); + +/** + * Set a local integer variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the integer local to + */ +void SetPersistantLocalInt(object oPC, string sName, int nValue); + +/** + * Set a local float variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the float local to + */ +void SetPersistantLocalFloat(object oPC, string sName, float fValue); + +/** + * Set a local location variable on the creature's storage token. + * + * CAUTION! See note in SetPersistantLocalObject(). Location also contains an + * object reference, though it will only break across changes to the module, + * not across server resets. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the location local to + */ +void SetPersistantLocalLocation(object oPC, string sName, location lValue); + +/** + * Set a local object variable on the creature's storage token. + * + * CAUTION! Object references are likely (and in some cases, certain) to break + * when transferring across modules or upon server reset. This means that + * persistantly stored local objects may not refer to the same object after such + * a change, if they refer to a valid object at all. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @param nValue The value to set the object local to + */ +void SetPersistantLocalObject(object oPC, string sName, object oValue); + +/** + * Get a local string variable from the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The string local specified. On error, returns "" + */ +string GetPersistantLocalString(object oPC, string sName); + +/** + * Get a local integer variable from the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The integer local specified. On error, returns 0 + */ +int GetPersistantLocalInt(object oPC, string sName); + +/** + * Get a local float variable from the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The float local specified. On error, returns 0.0 + */ +float GetPersistantLocalFloat(object oPC, string sName); + +/** + * Get a local location variable from the creature's storage token. + * + * CAUTION! See note in SetPersistantLocalLocation() + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The location local specified. Return value on error is unknown + */ +location GetPersistantLocalLocation(object oPC, string sName); + +/** + * Get a local object variable from the creature's storage token. + * + * CAUTION! See note in SetPersistantLocalObject() + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + * @return The object local specified. On error, returns OBJECT_INVALID + */ +object GetPersistantLocalObject(object oPC, string sName); + +/** + * Delete a local string variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalString(object oPC, string sName); + +/** + * Delete a local integer variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalInt(object oPC, string sName); + +/** + * Delete a local float variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalFloat(object oPC, string sName); + +/** + * Delete a local location variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalLocation(object oPC, string sName); + +/** + * Delete a local object variable on the creature's storage token. + * + * @param oPC The creature whose local variables to manipulate + * @param sName The name of the local variable to manipulate + */ +void DeletePersistantLocalObject(object oPC, string sName); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_skin" + +// SQL include +#include "inc_persistsql" + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +object GetHideToken(object oPC, int bAMS = FALSE) +{ + string sCache = bAMS ? "PRC_AMSTokenCache" : "PRC_HideTokenCache"; + string sTag = bAMS ? "AMS_Token" : "HideToken"; + + // Creatureness check - non-creatures don't get persistent storage from here + if(!(GetObjectType(oPC) == OBJECT_TYPE_CREATURE)) + return oPC; // Just return a reference to the object itself + + object oHide = GetPCSkin(oPC); + object oToken = GetLocalObject(oPC, sCache); + + if(!GetIsObjectValid(oToken)) + { + object oTest = GetFirstItemInInventory(oHide); + while(GetIsObjectValid(oTest)) + { + if(GetTag(oTest) == sTag) + { + oToken = oTest; + break;//exit while loop + } + oTest = GetNextItemInInventory(oHide); + } + if(!GetIsObjectValid(oToken)) + { + oToken = GetItemPossessedBy(oPC, sTag); + + // Move the token to hide's inventory + if(GetIsObjectValid(oToken)) + AssignCommand(oHide, ActionTakeItem(oToken, oPC)); // Does this work? - Ornedan + else + { + //oToken = CreateItemOnObject("hidetoken", oPC); + //AssignCommand(oHide, ActionTakeItem(oToken, oPC)); + oToken = CreateItemOnObject("hidetoken", oHide, 1, sTag); + } + } + + AssignCommand(oToken, SetIsDestroyable(FALSE)); + // Cache the token so that there needn't be multiple loops over an inventory + SetLocalObject(oPC, sCache, oToken); + //- If the cache reference is found to break under any conditions, uncomment this. + //looks like logging off then back on without the server rebooting breaks it + //I guess because the token gets a new ID, but the local still points to the old one + //Ive changed it to delete the local in OnClientEnter. Primogenitor + //DelayCommand(1.0f, DeleteLocalObject(oPC, "PRC_HideTokenCache")); + } + return oToken; +} + +void SetPersistantLocalString(object oPC, string sName, string sValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetString(oPC, sName, sValue); + } + else + { + SetLocalString(oPC, sName, sValue); + } +} + +/* void SetPersistantLocalString(object oPC, string sName, string sValue) +{ + if(GetIsPC(oPC)) + { + SQLocalsPlayer_SetString(oPC, sName, sValue); + } + else + { + SetLocalString(oPC, sName, sValue); + } +} */ + +void SetPersistantLocalInt(object oPC, string sName, int nValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetInt(oPC, sName, nValue); + } + else + { + SetLocalInt(oPC, sName, nValue); + } +} + +void SetPersistantLocalFloat(object oPC, string sName, float fValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetFloat(oPC, sName, fValue); + } + else + { + SetLocalFloat(oPC, sName, fValue); + } +} + +void SetPersistantLocalLocation(object oPC, string sName, location lValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetLocation(oPC, sName, lValue); + } + else + { + SetLocalLocation(oPC, sName, lValue); + } +} + +void SetPersistantLocalObject(object oPC, string sName, object oValue) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_SetObject(oPC, sName, oValue); + } + else + { + SetLocalObject(oPC, sName, oValue); + } +} + +string GetPersistantLocalString(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetString(oPC, sName); + } + return GetLocalString(oPC, sName); +} + +int GetPersistantLocalInt(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetInt(oPC, sName); + } + return GetLocalInt(oPC, sName); +} + +float GetPersistantLocalFloat(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetFloat(oPC, sName); + } + return GetLocalFloat(oPC, sName); +} + +location GetPersistantLocalLocation(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + return SQLocalsPlayer_GetLocation(oPC, sName); + } + return GetLocalLocation(oPC, sName); +} + +object GetPersistantLocalObject(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + // Additional check since the OID returned may be invalid, but not actually OBJECT_INVALID + object oReturn = SQLocalsPlayer_GetObject(oPC, sName); + if(GetIsObjectValid(oReturn)) return oReturn; + return OBJECT_INVALID; + } + return GetLocalObject(oPC, sName); +} + +void DeletePersistantLocalString(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteString(oPC, sName); + } + else + { + DeleteLocalString(oPC, sName); + } +} + +void DeletePersistantLocalInt(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteInt(oPC, sName); + } + else + { + DeleteLocalInt(oPC, sName); + } +} + +void DeletePersistantLocalFloat(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteFloat(oPC, sName); + } + else + { + DeleteLocalFloat(oPC, sName); + } +} + +void DeletePersistantLocalLocation(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteLocation(oPC, sName); + } + else + { + DeleteLocalLocation(oPC, sName); + } +} + +void DeletePersistantLocalObject(object oPC, string sName) +{ + if(GetIsPC(oPC) == TRUE && GetMaster(oPC) == OBJECT_INVALID && !GetIsDMPossessed(oPC)) + { + SQLocalsPlayer_DeleteObject(oPC, sName); + } + else + { + DeleteLocalObject(oPC, sName); + } +} + +// Test main + +// void main() {} \ No newline at end of file diff --git a/src/include/inc_persistsql.nss b/src/include/inc_persistsql.nss new file mode 100644 index 0000000..3f642a0 --- /dev/null +++ b/src/include/inc_persistsql.nss @@ -0,0 +1,688 @@ +//:://///////////////////////////////////////////// +//:: Utility Include: SQLocalsPlayer +//:: inc_persistsql.nss +//::////////////////////////////////////////////// +/* + Daz wrote these library functions to act as replacements for the usual local + functions: + * GetLocalInt / SetLocalInt / DeleteLocalInt + * GetLocalFloat / SetLocalFloat / DeleteLocalFloat + * GetLocalString / SetLocalString / DeleteLocalString + * GetLocalObject / SetLocalObject / DeleteLocalObject (NB: remember these are references NOT serialised objects) + * GetLocalLocation / SetLocalLocation / DeleteLocalLocation + * Plus a new function for saving just a vector by itself. + Since sometimes iterating over many locals is slow, this might be an excellent way to + speed up large amounts of access, or for debugging, or using regex or whatever else. + + These are functions for PC Object persistence only. See utl_i_sqlocals.nss for + the module saved version. +*/ +//::////////////////////////////////////////////// +//:: Based off of the nwscript_utility_scripts project; see for dates/creator info +//:: https://github.com/Finaldeath/nwscript_utility_scripts +//::////////////////////////////////////////////// + +const string SQLOCALSPLAYER_TABLE_NAME = "sqlocalsplayer_table"; + +const int SQLOCALSPLAYER_TYPE_ALL = 0; +const int SQLOCALSPLAYER_TYPE_INT = 1; +const int SQLOCALSPLAYER_TYPE_FLOAT = 2; +const int SQLOCALSPLAYER_TYPE_STRING = 4; +const int SQLOCALSPLAYER_TYPE_OBJECT = 8; +const int SQLOCALSPLAYER_TYPE_VECTOR = 16; +const int SQLOCALSPLAYER_TYPE_LOCATION = 32; + +// Returns an integer stored on oPlayer, or 0 on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +int SQLocalsPlayer_GetInt(object oPlayer, string sVarName); +// Sets an integer stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nValue - Value to store +void SQLocalsPlayer_SetInt(object oPlayer, string sVarName, int nValue); +// Deletes an integer stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteInt(object oPlayer, string sVarName); + +// Returns a float stored on oPlayer, or 0.0 on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +float SQLocalsPlayer_GetFloat(object oPlayer, string sVarName); +// Sets a float stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * fValue - Value to store +void SQLocalsPlayer_SetFloat(object oPlayer, string sVarName, float fValue); +// Deletes a float stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteFloat(object oPlayer, string sVarName); + +// Returns an string stored on oPlayer, or "" on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +string SQLocalsPlayer_GetString(object oPlayer, string sVarName); +// Sets a string stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * sValue - Value to store +void SQLocalsPlayer_SetString(object oPlayer, string sVarName, string sValue); +// Deletes a string stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteString(object oPlayer, string sVarName); + +// Returns an object identifier stored on oPlayer +// If this is used on a player it might return a "once valid" OID, so check +// with GetIsObjectValid, do not compare to OBJECT_INVALID. +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +object SQLocalsPlayer_GetObject(object oPlayer, string sVarName); +// Sets an object identifier stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * oValue - Value to store +void SQLocalsPlayer_SetObject(object oPlayer, string sVarName, object oValue); +// Deletes an object identifier stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteObject(object oPlayer, string sVarName); + +// Returns a vector stored on oPlayer, or [0.0, 0.0, 0.0] on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +vector SQLocalsPlayer_GetVector(object oPlayer, string sVarName); +// Sets a vector stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * vValue - Value to store +void SQLocalsPlayer_SetVector(object oPlayer, string sVarName, vector vValue); +// Deletes a vector stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteVector(object oPlayer, string sVarName); + +// Returns a location stored on oPlayer, or the starting location of the module on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +location SQLocalsPlayer_GetLocation(object oPlayer, string sVarName); +// Sets a location stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * lValue - Value to store +void SQLocalsPlayer_SetLocation(object oPlayer, string sVarName, location lValue); +// Deletes a location stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteLocation(object oPlayer, string sVarName); + +// Deletes a set of locals stored on oPlayer matching the given criteria +// * oPlayer - a player object to save the variable on +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to remove (default: SQLOCALSPLAYER_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +void SQLocalsPlayer_Delete(object oPlayer, int nType = SQLOCALSPLAYER_TYPE_ALL, string sLike = "", string sEscape = ""); +// Counts a set of locals stored on oPlayer matching the given criteria +// * oPlayer - a player object to save the variable on +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to count (default: SQLOCALSPLAYER_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +int SQLocalsPlayer_Count(object oPlayer, int nType = SQLOCALSPLAYER_TYPE_ALL, string sLike = "", string sEscape = ""); +// Checks a locals stored on oPlayer is set +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to check +int SQLocalsPlayer_IsSet(object oPlayer, string sVarName, int nType); +// Returns the last Unix time the given variable was updated +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to check +int SQLocalsPlayer_GetLastUpdated_UnixEpoch(object oPlayer, string sVarName, int nType); +// Returns the last UTC time the given variable was updated +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to check +string SQLocalsPlayer_GetLastUpdated_UTC(object oPlayer, string sVarName, int nType); + + +/* INTERNAL */ +void SQLocalsPlayer_CreateTable(object oPlayer) +{ + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "CREATE TABLE IF NOT EXISTS " + SQLOCALSPLAYER_TABLE_NAME + " (" + + "type INTEGER, " + + "varname TEXT, " + + "value TEXT, " + + "timestamp INTEGER, " + + "PRIMARY KEY(type, varname));"); + SqlStep(sql); +} + +sqlquery SQLocalsPlayer_PrepareSelect(object oPlayer, int nType, string sVarName) +{ + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT value FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE type = @type AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + return sql; +} + +sqlquery SQLocalsPlayer_PrepareInsert(object oPlayer, int nType, string sVarName) +{ + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "INSERT INTO " + SQLOCALSPLAYER_TABLE_NAME + " " + + "(type, varname, value, timestamp) VALUES (@type, @varname, @value, strftime('%s','now')) " + + "ON CONFLICT (type, varname) DO UPDATE SET value = @value, timestamp = strftime('%s','now');"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + return sql; +} + +sqlquery SQLocalsPlayer_PrepareDelete(object oPlayer, int nType, string sVarName) +{ + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "DELETE FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE type = @type AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + return sql; +} + +string SQLocalsPlayer_LocationToString(location locLocation) +{ + string sAreaId = ObjectToString(GetAreaFromLocation(locLocation)); + vector vPosition = GetPositionFromLocation(locLocation); + float fFacing = GetFacingFromLocation(locLocation); + + return "#A#" + sAreaId + + "#X#" + FloatToString(vPosition.x, 0, 5) + + "#Y#" + FloatToString(vPosition.y, 0, 5) + + "#Z#" + FloatToString(vPosition.z, 0, 5) + + "#F#" + FloatToString(fFacing, 0, 5) + "#"; +} + +location SQLocalsPlayer_StringToLocation(string sLocation) +{ + location locLocation; + + int nLength = GetStringLength(sLocation); + + if(nLength > 0) + { + int nPos, nCount; + + nPos = FindSubString(sLocation, "#A#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + object oArea = StringToObject(GetSubString(sLocation, nPos, nCount)); + + nPos = FindSubString(sLocation, "#X#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fX = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + nPos = FindSubString(sLocation, "#Y#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fY = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + nPos = FindSubString(sLocation, "#Z#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fZ = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + vector vPosition = Vector(fX, fY, fZ); + + nPos = FindSubString(sLocation, "#F#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fOrientation = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + if (GetIsObjectValid(oArea)) + locLocation = Location(oArea, vPosition, fOrientation); + else + locLocation = GetStartingLocation(); + } + + return locLocation; +} +/* **** */ + +/* INT */ + +// Returns an integer stored on oPlayer, or 0 on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +int SQLocalsPlayer_GetInt(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return 0; + + sqlquery sql = SQLocalsPlayer_PrepareSelect(oPlayer, SQLOCALSPLAYER_TYPE_INT, sVarName); + + if (SqlStep(sql)) + return SqlGetInt(sql, 0); + else + return 0; +} + +// Sets an integer stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nValue - Value to store +void SQLocalsPlayer_SetInt(object oPlayer, string sVarName, int nValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareInsert(oPlayer, SQLOCALSPLAYER_TYPE_INT, sVarName); + SqlBindInt(sql, "@value", nValue); + SqlStep(sql); +} + +// Deletes an integer stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteInt(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareDelete(oPlayer, SQLOCALSPLAYER_TYPE_INT, sVarName); + SqlStep(sql); +} +/* **** */ + +/* FLOAT */ + +// Returns a float stored on oPlayer, or 0.0 on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +float SQLocalsPlayer_GetFloat(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return 0.0f; + + sqlquery sql = SQLocalsPlayer_PrepareSelect(oPlayer, SQLOCALSPLAYER_TYPE_FLOAT, sVarName); + + if (SqlStep(sql)) + return SqlGetFloat(sql, 0); + else + return 0.0f; +} + +// Sets a float stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * fValue - Value to store +void SQLocalsPlayer_SetFloat(object oPlayer, string sVarName, float fValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareInsert(oPlayer, SQLOCALSPLAYER_TYPE_FLOAT, sVarName); + SqlBindFloat(sql, "@value", fValue); + SqlStep(sql); +} + +// Deletes a float stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteFloat(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareDelete(oPlayer, SQLOCALSPLAYER_TYPE_FLOAT, sVarName); + SqlStep(sql); +} +/* **** */ + +/* STRING */ + +// Returns an string stored on oPlayer, or "" on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +string SQLocalsPlayer_GetString(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return ""; + + sqlquery sql = SQLocalsPlayer_PrepareSelect(oPlayer, SQLOCALSPLAYER_TYPE_STRING, sVarName); + + if (SqlStep(sql)) + return SqlGetString(sql, 0); + else + return ""; +} + +// Sets a string stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * sValue - Value to store +void SQLocalsPlayer_SetString(object oPlayer, string sVarName, string sValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareInsert(oPlayer, SQLOCALSPLAYER_TYPE_STRING, sVarName); + SqlBindString(sql, "@value", sValue); + SqlStep(sql); +} + +// Deletes a string stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteString(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareDelete(oPlayer, SQLOCALSPLAYER_TYPE_STRING, sVarName); + SqlStep(sql); +} +/* **** */ + +/* OBJECT */ + + +// Returns an object identifier stored on oPlayer +// If this is used on a player it might return a "once valid" OID, so check +// with GetIsObjectValid, do not compare to OBJECT_INVALID. +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +object SQLocalsPlayer_GetObject(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return OBJECT_INVALID; + + sqlquery sql = SQLocalsPlayer_PrepareSelect(oPlayer, SQLOCALSPLAYER_TYPE_OBJECT, sVarName); + + if (SqlStep(sql)) + return StringToObject(SqlGetString(sql, 0)); + else + return OBJECT_INVALID; +} + +// Sets an object identifier stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * oValue - Value to store +void SQLocalsPlayer_SetObject(object oPlayer, string sVarName, object oValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareInsert(oPlayer, SQLOCALSPLAYER_TYPE_OBJECT, sVarName); + SqlBindString(sql, "@value", ObjectToString(oValue)); + SqlStep(sql); +} + +// Deletes an object identifier stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteObject(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareDelete(oPlayer, SQLOCALSPLAYER_TYPE_OBJECT, sVarName); + SqlStep(sql); +} +/* **** */ + +/* VECTOR */ + +// Returns a vector stored on oPlayer, or [0.0, 0.0, 0.0] on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +vector SQLocalsPlayer_GetVector(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return [0.0f, 0.0f, 0.0f]; + + sqlquery sql = SQLocalsPlayer_PrepareSelect(oPlayer, SQLOCALSPLAYER_TYPE_VECTOR, sVarName); + + if (SqlStep(sql)) + return SqlGetVector(sql, 0); + else + return [0.0f, 0.0f, 0.0f]; +} + +// Sets a vector stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * vValue - Value to store +void SQLocalsPlayer_SetVector(object oPlayer, string sVarName, vector vValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareInsert(oPlayer, SQLOCALSPLAYER_TYPE_VECTOR, sVarName); + SqlBindVector(sql, "@value", vValue); + SqlStep(sql); +} + +// Deletes a vector stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteVector(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareDelete(oPlayer, SQLOCALSPLAYER_TYPE_VECTOR, sVarName); + SqlStep(sql); +} +/* **** */ + +/* LOCATION */ + +// Returns a location stored on oPlayer, or the starting location of the module on error +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +location SQLocalsPlayer_GetLocation(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return GetStartingLocation(); + + sqlquery sql = SQLocalsPlayer_PrepareSelect(oPlayer, SQLOCALSPLAYER_TYPE_LOCATION, sVarName); + + if (SqlStep(sql)) + return SQLocalsPlayer_StringToLocation(SqlGetString(sql, 0)); + else + return GetStartingLocation(); +} + +// Sets a location stored on oPlayer to the given value +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * lValue - Value to store +void SQLocalsPlayer_SetLocation(object oPlayer, string sVarName, location lValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareInsert(oPlayer, SQLOCALSPLAYER_TYPE_LOCATION, sVarName); + SqlBindString(sql, "@value", SQLocalsPlayer_LocationToString(lValue)); + SqlStep(sql); +} + +// Deletes a location stored on oPlayer +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to delete +void SQLocalsPlayer_DeleteLocation(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsPlayer_PrepareDelete(oPlayer, SQLOCALSPLAYER_TYPE_LOCATION, sVarName); + SqlStep(sql); +} +/* **** */ + +/* UTILITY */ + +// Deletes a set of locals stored on oPlayer matching the given criteria +// * oPlayer - a player object to save the variable on +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to remove (default: SQLOCALSPLAYER_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +// Note if it is TYPE_ALL and sLike is "" then it will delete all values +void SQLocalsPlayer_Delete(object oPlayer, int nType = SQLOCALSPLAYER_TYPE_ALL, string sLike = "", string sEscape = "") +{ + if (!GetIsPC(oPlayer) || nType < 0) return; + + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql; + if(nType == SQLOCALSPLAYER_TYPE_ALL && sLike == "") + { + sql = SqlPrepareQueryObject(oPlayer, + "DELETE FROM " + SQLOCALSPLAYER_TABLE_NAME + ";"); + } + else + { + sql = SqlPrepareQueryObject(oPlayer, + "DELETE FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE TRUE " + + (nType != SQLOCALSPLAYER_TYPE_ALL ? "AND type & @type " : " ") + + (sLike != "" ? "AND varname LIKE @like " + (sEscape != "" ? "ESCAPE @escape" : "") : "") + + ";"); + + + if (nType != SQLOCALSPLAYER_TYPE_ALL) + SqlBindInt(sql, "@type", nType); + if (sLike != "") + { + SqlBindString(sql, "@like", sLike); + + if (sEscape != "") + SqlBindString(sql, "@escape", sEscape); + } + } + + + SqlStep(sql); +} + +// Counts a set of locals stored on oPlayer matching the given criteria +// * oPlayer - a player object to save the variable on +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to count (default: SQLOCALSPLAYER_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +int SQLocalsPlayer_Count(object oPlayer, int nType = SQLOCALSPLAYER_TYPE_ALL, string sLike = "", string sEscape = "") +{ + if (!GetIsPC(oPlayer) || nType < 0) return 0; + + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql; + if(nType == SQLOCALSPLAYER_TYPE_ALL && sLike == "") + { + sql = SqlPrepareQueryObject(oPlayer, + "SELECT COUNT(*) FROM " + SQLOCALSPLAYER_TABLE_NAME + ";"); + } + else if(nType != SQLOCALSPLAYER_TYPE_ALL && sLike == "") + { + sql = SqlPrepareQueryObject(oPlayer, + "SELECT COUNT(*) FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE type & @type;"); + + SqlBindInt(sql, "@type", nType); + } + else if(nType == SQLOCALSPLAYER_TYPE_ALL && sLike != "") + { + sql = SqlPrepareQueryObject(oPlayer, + "SELECT COUNT(*) FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "varname LIKE @like " + (sEscape != "" ? "ESCAPE @escape" : "") + + ";"); + + SqlBindString(sql, "@like", sLike); + + if (sEscape != "") + SqlBindString(sql, "@escape", sEscape); + } + else + { + sql = SqlPrepareQueryObject(oPlayer, + "SELECT COUNT(*) FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE type & @type " + + "AND varname LIKE @like " + (sEscape != "" ? "ESCAPE @escape" : "") + + ";"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@like", sLike); + + if (sEscape != "") + SqlBindString(sql, "@escape", sEscape); + } + + if (SqlStep(sql)) + return SqlGetInt(sql, 0); + else + return 0; +} + +// Checks a locals stored on oPlayer is set +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to check (default: SQLOCALSPLAYER_TYPE_ALL) +int SQLocalsPlayer_IsSet(object oPlayer, string sVarName, int nType) +{ + if (!GetIsPC(oPlayer) || nType < 0) return 0; + + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT * FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE AND varname = @varname" + + (nType != SQLOCALSPLAYER_TYPE_ALL ? "AND type & @type " : " ") + + ";"); + + if (nType != SQLOCALSPLAYER_TYPE_ALL) + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + return SqlStep(sql); +} + +// Returns the last Unix time the given variable was updated +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to check (default: SQLOCALSPLAYER_TYPE_ALL) +int SQLocalsPlayer_GetLastUpdated_UnixEpoch(object oPlayer, string sVarName, int nType) +{ + if (!GetIsPC(oPlayer) || nType <= 0) return 0; + + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT timestamp FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE type = @type " + + "AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + if (SqlStep(sql)) + return SqlGetInt(sql, 0); + else + return 0; +} + +// Returns the last UTC time the given variable was updated +// * oPlayer - a player object to save the variable on +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSPLAYER_TYPE_* you wish to check (default: SQLOCALSPLAYER_TYPE_ALL) +string SQLocalsPlayer_GetLastUpdated_UTC(object oPlayer, string sVarName, int nType) +{ + if (!GetIsPC(oPlayer) || nType <= 0) return ""; + + SQLocalsPlayer_CreateTable(oPlayer); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT datetime(timestamp, 'unixepoch') FROM " + SQLOCALSPLAYER_TABLE_NAME + " " + + "WHERE type = @type " + + "AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + if (SqlStep(sql)) + return SqlGetString(sql, 0); + else + return ""; +} \ No newline at end of file diff --git a/src/include/inc_poison.nss b/src/include/inc_poison.nss new file mode 100644 index 0000000..13d2c32 --- /dev/null +++ b/src/include/inc_poison.nss @@ -0,0 +1,113 @@ +//:://///////////////////////////////////////////// +//:: Poison System includes +//:: inc_poison +//:://///////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 12.12.2004 +//:: Updated On: 09.01.2005 +//::////////////////////////////////////////////// + + +const int POISONED_WEAPON_CASTERLEVEL = 1; + +const int STRREF_POISON_WORN_OFF = 16826227; +const int STRREF_POISON_APPLY_SUCCESS = 16826228; +const int STRREF_POISON_APPLY_FAILURE = 16826230; +const int STRREF_POISON_CLEAN_OFF_WEAPON = 16826229; +const int STRREF_POISON_NOT_VALID_FOR_WEAPON = 16826231; +const int STRREF_SHATTER_HARMLESS = 16826234; +const int STRREF_POISON_ITEM_USE_1 = 16826236; +const int STRREF_POISON_ITEM_USE_2 = 16826237; +const int STRREF_POISON_FOOD_USE_1 = 16826239; +const int STRREF_POISON_FOOD_USE_2 = 16826240; +const int STRREF_CLEAN_ITEM_SUCCESS = 16826242; +const int STRREF_CLEAN_ITEM_FAIL_1 = 16826243; +const int STRREF_CLEAN_ITEM_FAIL_2 = 16826244; +const int STRREF_INVALID_TARGET = 16826245; +const int STRREF_NOT_CONTACT_POISON = 16826246; +const int STRREF_TARGET_ALREADY_POISONED = 16826247; +const int STRREF_NOT_INGESTED_POISON = 16826251; +const int STRREF_TARGET_NOT_FOOD = 16826252; +const int STRREF_ACQUIRE_SPOT_SUCCESS1 = 16826253; +const int STRREF_ACQUIRE_SPOT_SUCCESS2 = 16826254; +const int STRREF_ONEQUIP_CLEAN_ITEM = 16826255; + +const int POISON_TYPE_CONTACT = 0; +const int POISON_TYPE_INGESTED = 1; +const int POISON_TYPE_INHALED = 2; +const int POISON_TYPE_INJURY = 3; + +/** + * Gets the type of the given poison. + * + * @param nPoison POISON_* constant + * @return POISON_TYPE_* constant + */ +int GetPoisonType(int nPoison); + + +// Poison removal handlers +void DoPoisonRemovalFromWeapon(object oWeapon); +void DoPoisonRemovalFromItem(object oItem); + + +//#include "inc_utility" +//#include "inc_poison_const" +#include "prc_inc_spells" +#include "prc_ipfeat_const" + +/**************************************************** +************** The implementations ****************** +****************************************************/ + +int GetPoisonType(int nPoison) +{ + return StringToInt(Get2DACache("poison", "Poison_Type", nPoison)); +} + +// Handles removing of itemproperties and locals on a poisoned weapon +void DoPoisonRemovalFromWeapon(object oWeapon) +{ + DeleteLocalInt(oWeapon, "pois_wpn_idx"); + DeleteLocalInt(oWeapon, "pois_wpn_uses"); + RemoveEventScript(oWeapon, EVENT_ITEM_ONHIT, "poison_wpn_onhit", TRUE, TRUE); + + // Remove the UniquePower only if poisoning the weapon added it. + if(GetLocalInt(oWeapon, "PoisonedWeapon_DoDelete")) + RemoveSpecificProperty(oWeapon, + ITEM_PROPERTY_ONHITCASTSPELL, + IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, + 0, + 1, + "", + -1, + DURATION_TYPE_PERMANENT); +} + +// Handles removing of itemproperties and locals on a poisoned item +void DoPoisonRemovalFromItem(object oItem) +{ + DeleteLocalInt(oItem, "pois_itm_idx"); + DeleteLocalInt(oItem, "pois_itm_uses"); + DeleteLocalInt(oItem, "pois_itm_trap_dc"); + DeleteLocalObject(oItem, "pois_itm_poisoner"); + + int nSafeCount = GetLocalInt(oItem, "pois_itm_safecount"); + DeleteLocalInt(oItem, "pois_itm_safecount"); + int i; + for(i = 1; i <= nSafeCount; i++) + DeleteLocalObject(oItem, "pois_itm_safe_" + IntToString(i)); + + RemoveSpecificProperty(oItem, + ITEM_PROPERTY_CAST_SPELL, + IP_CONST_CASTSPELL_CLEAN_POISON_OFF, + IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE, + 1, + "", + -1, + DURATION_TYPE_PERMANENT); + + RemoveEventScript(oItem, EVENT_ITEM_ONACQUIREITEM, "poison_onaquire", TRUE, TRUE); + RemoveEventScript(oItem, EVENT_ITEM_ONPLAYEREQUIPITEM, "poison_onequip", TRUE, TRUE); +} diff --git a/src/include/inc_prc_npc.nss b/src/include/inc_prc_npc.nss new file mode 100644 index 0000000..f8d5286 --- /dev/null +++ b/src/include/inc_prc_npc.nss @@ -0,0 +1,181 @@ +//:://///////////////////////////////////////////// +//:: NPC event wrapper include +//:: inc_prc_npc +//::////////////////////////////////////////////// +/** @file + Wrapper functions for getters used in module + events. Used to make the PRC evaluations + happening in events to work for NPCs, too. + + Event currently supported: + OnEquip + OnUnequip + OnDeath + OnRest + +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * A heartbeat function for NPCs. Checks if their equipped items have changed. + * Simulates OnEquip / OnUnEquip firing + * Since this should be fired from an NPCs hearbeat, OBJECT_SELF is assumed to + * be the NPC. + */ +void DoEquipTest(); + + +/* On(Un)Equip wrappers */ + +/** + * Wrapper for GetPCItemLastEquipped + */ +object GetItemLastEquipped(); + +/** + * Wrapper for GetPCItemLastEquippedBy + */ +object GetItemLastEquippedBy(); + +/** + * Wrapper for GetItemLastUnequipped + */ +object GetItemLastUnequipped(); + +/** + * Wrapper for GetPCItemLastUnequippedBy + */ +object GetItemLastUnequippedBy(); + + +/* OnDeath wrappers */ + +/** + * Wrapper for GetLastHostileActor and GetLastKiller + */ +object MyGetLastKiller(); + +/** + * Wrapper for GetLastPlayerDied + */ +object GetLastBeingDied(); + + +/* OnRest wrapper */ + +/** + * Wrapper for GetLastPCRested + */ +object GetLastBeingRested(); + +/** + * Wrapper for GetLastRestEventType + */ +int MyGetLastRestEventType(); + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +object GetItemLastEquippedBy() +{ + if(GetModule() == OBJECT_SELF || GetIsPC(OBJECT_SELF)) // prc_inc_function runs the class scripts on the PC instead of the module + return GetPCItemLastEquippedBy(); + else + return OBJECT_SELF; +} + +object GetItemLastUnequippedBy() +{ + if(GetModule() == OBJECT_SELF || GetIsPC(OBJECT_SELF)) + return GetPCItemLastUnequippedBy(); + else + return OBJECT_SELF; +} + +object GetItemLastEquipped() +{ + if(GetModule() == OBJECT_SELF || GetIsPC(OBJECT_SELF)) + return GetPCItemLastEquipped(); + else + return GetLocalObject(OBJECT_SELF, "oLastEquipped"); +} + +object GetItemLastUnequipped() +{ + if(GetModule() == OBJECT_SELF || GetIsPC(OBJECT_SELF)) + return GetPCItemLastUnequipped(); + else + return GetLocalObject(OBJECT_SELF, "oLastUnequipped"); +} + +void DoEquipTest() +{ + int i; + object oTest; + object oItem; + for(i=1; i=2000) + { + oTarget = CreateObject(OBJECT_TYPE_CREATURE,sResRef,lLimbo); + } + else + { + oTarget = CreateObject(OBJECT_TYPE_CREATURE,sResRef,GetLocation(oPC)); + } + if (!GetIsObjectValid(oTarget)) + { + SendMessageToPC(oPC, "Not a valid creature."); + // Remove the temporary creature + AssignCommand(oTarget,SetIsDestroyable(TRUE,FALSE,FALSE)); + SetPlotFlag(oTarget,FALSE); + SetImmortal(oTarget,FALSE); + DestroyObject(oTarget); + return FALSE; + } + else + { + //get the appearance before changing it + SetLocalInt(oTarget,"Appearance",GetAppearanceType(oTarget)); + //set appearance to invis so it dont show up when scripts run thro + SetCreatureAppearanceType(oTarget,APPEARANCE_TYPE_INVISIBLE_HUMAN_MALE); + //set oTarget for deletion + SetLocalInt(oTarget,"pnp_shifter_deleteme",1); + //Shift the PC to it + if (iExtraAbilitys == TRUE) + SetShiftEpic(oPC, oTarget); + else + SetShift(oPC, oTarget); + return TRUE; + } +} + +int PRC_Polymorph_Object(object oPC, object oTarget, int iExtraAbilitys, int iDeleteTarget, int iUseClone) +{ + StoreAppearance(oPC); + if (!CanShift(oPC)) + { + return FALSE; + } + if (iUseClone == TRUE) + { + string sResRef = GetResRef(oTarget); + int i = 0; + object oLimbo = GetObjectByTag("Limbo", i); + location lLimbo; + while (i < 100) + { + if (GetIsObjectValid(oLimbo)) + { + if (GetName(oLimbo) == "Limbo") + { + i = 2000; + vector vLimbo = Vector(0.0f, 0.0f, 0.0f); + lLimbo = Location(oLimbo, vLimbo, 0.0f); + } + } + i++; + object oLimbo = GetObjectByTag("Limbo", i); + } + if (i>=2000) + { + oTarget = CreateObject(OBJECT_TYPE_CREATURE,sResRef,lLimbo); + } + else + { + oTarget = CreateObject(OBJECT_TYPE_CREATURE,sResRef,GetLocation(oPC)); + } + } + if (!GetIsObjectValid(oTarget)) + { + SendMessageToPC(oPC, "Not a valid creature."); + // Remove the temporary creature + AssignCommand(oTarget,SetIsDestroyable(TRUE,FALSE,FALSE)); + SetPlotFlag(oTarget,FALSE); + SetImmortal(oTarget,FALSE); + DestroyObject(oTarget); + return FALSE; + } + else + { + SetLocalInt(oTarget,"Appearance",GetAppearanceType(oTarget)); + if (iDeleteTarget == TRUE) + { + //set oTarget for deletion + SetLocalInt(oTarget,"pnp_shifter_deleteme",1); + } + //Shift the PC to it + if (iExtraAbilitys == TRUE) + SetShiftEpic(oPC, oTarget); + else + SetShift(oPC, oTarget); + return TRUE; + } +} + +int PRC_Polymorph_Check(object oPC) +{ + return GetPersistantLocalInt(oPC, "nPCShifted"); +} + +void PRC_UnPolymorph(object oPC) +{ + ExecuteScript("pnp_shft_true", oPC); +} + + +// Test main +//void main(){} diff --git a/src/include/inc_rand_equip.nss b/src/include/inc_rand_equip.nss new file mode 100644 index 0000000..814f533 --- /dev/null +++ b/src/include/inc_rand_equip.nss @@ -0,0 +1,4637 @@ +/** @file + Primogenitor's Random Equipment Functions + + This include file contains a group of functions that will create equipment + on the creatures they are called upon. + The equipment will be just the basic bioware defaults, so you will have to + add/replace with enchanted versions yourself. + If the target already has equipment, this will be created and equiped instead. + However, the AI may restore the other if it is better. + + To use, include this file in a script. + Then call EquipWeapon, EquipArmor, and/or EquipMisc from the OnSpawn + Best used if spawned out of combat, otherwise they may not equip + Aslo, call these after any levelling has been done to use gained feats + If used on creatures with claws/bites/slams then the handed weapons will be + used instead of the natural weapons, even if they are inferior. + + + @author Primogenitor + + @todo See what functions can be replaced with stuff from inc_utility (and linked files) +*/ + +#include "prc_misc_const" +#include "prc_feat_const" + + +//Major PREF function +//spawns a unenchanted weapon randomly picked from +//those most suitable. +//takes into acount feats such as martial proficiency, weapon focus, improved critical +//dual weilding, weapon of choice, and shield proficiency +void EquipWeapon(object oObject = OBJECT_SELF); + +//Major PREF function +//spawns a unenchanted armor randomly picked from +//those most suitable +//will spawn clothing as well so you shouldnt have any nudists! +//may not be visible on non-dynamic models such as many monsters +void EquipArmor(object oObject = OBJECT_SELF); + +//Major PREF function +//spawns unenchanted other equipment randomly picked +//also adds potions, modified by level +//also adds scrolls, modified by spellcaster level +//also adds healing kits, modified by heal skill +void EquipMisc(object oObject = OBJECT_SELF); + +object EquipLightArmor(object oObject); +object EquipMediumArmor(object oObject); +object EquipHeavyArmor(object oObject); +object EquipClothes(object oObject); + +void EquipAmmo(object oObject = OBJECT_SELF); +object EquipWeaponOfChoice(object oObject); +object EquipEpicWeaponSpecialization(object oObject); +object EquipWeaponSpecialization(object oObject); +object EquipDevastatingCritical(object oObject); +object EquipEpicWeaponFocus(object oObject); +object EquipOverwhelmingCritical(object oObject); +object EquipImprovedCritical(object oObject); +object EquipWeaponFocus(object oObject); +object EquipWeaponProfDruid(object oObject, int nHands = 1); +object EquipWeaponProfRogue(object oObject, int nHands = 1); +object EquipWeaponProfElf(object oObject, int nHands = 1); +object EquipWeaponProfWizard(object oObject, int nHands = 1); +object EquipWeaponProfSimple(object oObject, int nHands = 1); +object EquipWeaponProfMartial(object oObject, int nHands = 1); +object EquipWeaponProfExotic(object oObject, int nHands = 1); +object EquipShield(object oObject); +void EquipScroll(object oObject); + +int GetCanDualWeild(object oObject); +int GetIsLeftHandFree(object oObject, object oWeapon); + +int IsItemWeapon(object oItem); +int PrimoGetWeaponSize(object oItem); +string GetBaseResRef(int nBaseItemType); + +int EQUIPSPAWNDEBUG = FALSE;//TRUE; + +void EquipDebugString(string sDebug) +{ + if(EQUIPSPAWNDEBUG) + { + SendMessageToPC(GetFirstPC(), sDebug); + SendMessageToAllDMs(sDebug); + WriteTimestampedLogEntry(sDebug); + } +} + +void EquipAmmo(object oObject = OBJECT_SELF) +{ + object oWeapon = oObject; + oObject = GetItemPossessor(oWeapon); + int nBaseItemType = GetBaseItemType(oWeapon); + if (nBaseItemType == BASE_ITEM_DART + ||nBaseItemType == BASE_ITEM_SHURIKEN + ||nBaseItemType == BASE_ITEM_THROWINGAXE) + SetItemStackSize(oWeapon, 99); + else if (nBaseItemType == BASE_ITEM_HEAVYCROSSBOW + ||nBaseItemType ==BASE_ITEM_LIGHTCROSSBOW) + { + oWeapon = CreateItemOnObject("nw_wambo001", oObject, 99); + ActionEquipItem(oWeapon, INVENTORY_SLOT_BOLTS); + } + else if (nBaseItemType == BASE_ITEM_LONGBOW + ||nBaseItemType ==BASE_ITEM_SHORTBOW) + { + oWeapon = CreateItemOnObject("nw_wamar001", oObject, 99); + ActionEquipItem(oWeapon, INVENTORY_SLOT_ARROWS); + } + else if (nBaseItemType == BASE_ITEM_SLING) + { + oWeapon = CreateItemOnObject("nw_wambu001", oObject, 99); + ActionEquipItem(oWeapon, INVENTORY_SLOT_BULLETS); + } + return; +} + +string FilledIntToString2(int nX) +{ + string sReturn = ""; + if (nX < 100) + sReturn = sReturn + "0"; + if (nX < 10) + sReturn = sReturn + "0"; + sReturn = sReturn + IntToString(nX); + return sReturn; +} + +void EquipWeapon(object oObject = OBJECT_SELF) +{ + object oWeaponLH; + object oWeaponRH; + + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK,oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + //dual kamas + oWeaponRH = CreateItemOnObject("nw_wspka001", oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + oWeaponLH = CreateItemOnObject("nw_wspka001", oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + EquipDebugString(GetName(oObject) + "Is dual Kama monk"); + return; + } + EquipDebugString(GetName(oObject) + "is Barefist Monk"); + return;//use fists + //monk weapons + } + else if(GetLevelByClass(CLASS_TYPE_WEAPON_MASTER, oObject) > 0) + { + oWeaponRH = EquipWeaponOfChoice(oObject); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipWeaponOfChoice(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + return; + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + return; + } + else + { + oWeaponLH = EquipWeaponOfChoice(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + return; + } + } + return; + } + else if(GetLevelByClass(CLASS_TYPE_FIGHTER, oObject) > 0) + { + oWeaponRH = EquipEpicWeaponSpecialization(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipEpicWeaponSpecialization(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipEpicWeaponSpecialization(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + oWeaponRH = EquipWeaponSpecialization(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipWeaponSpecialization(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipWeaponSpecialization(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + } + + oWeaponRH = EquipDevastatingCritical(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipDevastatingCritical(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipDevastatingCritical(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + + oWeaponRH = EquipEpicWeaponFocus(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipEpicWeaponFocus(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipEpicWeaponFocus(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + + + oWeaponRH = EquipOverwhelmingCritical(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipOverwhelmingCritical(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipOverwhelmingCritical(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + + oWeaponRH = EquipImprovedCritical(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipImprovedCritical(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipImprovedCritical(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + + oWeaponRH = EquipWeaponFocus(oObject); + if(GetIsObjectValid(oWeaponRH)) + { + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)) + { + if(GetCanDualWeild(oObject)) + { + oWeaponLH = EquipWeaponFocus(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject)) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + else + { + oWeaponLH = EquipWeaponFocus(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + return; + } + + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + oWeaponRH = EquipWeaponProfDruid(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfDruid(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfDruid(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + return; + } + else + { + oWeaponRH = EquipWeaponProfDruid(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + oWeaponRH = EquipWeaponProfExotic(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfExotic(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfExotic(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + return; + } + else + { + oWeaponRH = EquipWeaponProfExotic(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + oWeaponRH = EquipWeaponProfMartial(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfMartial(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfMartial(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else + { + oWeaponRH = EquipWeaponProfMartial(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oObject) == TRUE) + { + //elves only have accss to medium and large weapons + //two medium swords, short and long bows + if(GetCanDualWeild(oObject) == TRUE + && GetCreatureSize(oObject) >= CREATURE_SIZE_MEDIUM) + { + oWeaponRH = EquipWeaponProfElf(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfElf(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && GetCreatureSize(oObject) >= CREATURE_SIZE_MEDIUM + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfElf(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + return; + } + else if(GetCreatureSize(oObject) >= CREATURE_SIZE_MEDIUM) + { + oWeaponRH = EquipWeaponProfElf(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + return; + } + else + { + //fall through + } + } + else if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + oWeaponRH = EquipWeaponProfRogue(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfRogue(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfRogue(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else + { + oWeaponRH = EquipWeaponProfRogue(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + oWeaponRH = EquipWeaponProfSimple(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfSimple(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfSimple(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else + { + oWeaponRH = EquipWeaponProfSimple(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + } + return; + } + else if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_WIZARD, oObject) == TRUE) + { + if(GetCanDualWeild(oObject) == TRUE) + { + oWeaponRH = EquipWeaponProfWizard(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipWeaponProfWizard(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oObject) == TRUE + && Random (2) == 1) + { + oWeaponRH = EquipWeaponProfWizard(oObject,1); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + if(GetIsLeftHandFree(oObject, oWeaponRH)==TRUE) + { + oWeaponLH = EquipShield(oObject); + AssignCommand(oObject, ActionEquipItem(oWeaponLH,INVENTORY_SLOT_LEFTHAND)); + } + } + else + { + oWeaponRH = EquipWeaponProfWizard(oObject,2); + EquipAmmo(oWeaponRH); + AssignCommand(oObject, ActionEquipItem(oWeaponRH,INVENTORY_SLOT_RIGHTHAND)); + } + return; + } +} + +object EquipWeaponProfSimple(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 8; + nHighNumber = 12; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 7; + nHighNumber = 9; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 0; + nHighNumber = 6; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 7; + nHighNumber = 12; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 6; + nHighNumber = 12; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 1; + nHighNumber = 9; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //:: Tiny size + case 0:// Dagger + sResRef = "nw_wswdg001"; + break; + + //:: Small size + case 1:// Spear + sResRef = "nw_wplss001"; + break; + case 2: // Mace + sResRef = "nw_wblml001"; + break; + case 3:// Sickle + sResRef = "nw_wspsc001"; + break; + case 4:// Goad + sResRef = "prc_wspgd001"; + break; + case 5:// Dart + sResRef = "nw_wthdt001"; + break; + case 6:// Sling + sResRef = "nw_wbwsl001"; + break; + case 7:// Light Xbow + sResRef = "nw_wbwxl001"; + break; + + //:: Medium size + case 8:// Club + sResRef = "nw_wblcl001"; + break; + case 9: // Morningstar + sResRef = "nw_wblms001"; + break; + case 10: // Heavy Mace + sResRef = "prc_wxblmh001"; + break; + case 11: // Heavy Xbow + sResRef = "nw_wbwxh001"; + break; + + //:: Large size + case 12:// Quarterstaff + sResRef = "nw_wdbqs001"; + break; + //none + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; +} + +object EquipWeaponProfMartial(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 14; + nHighNumber = 20; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 0; + nHighNumber = 12; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 0; + nHighNumber = 5; + break; + case CREATURE_SIZE_TINY: + //no tiny martials + //use simple instead + oItem = EquipWeaponProfSimple(oObject, nHands); + return oItem; + break; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 13; + nHighNumber = 21; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 13; + nHighNumber = 21; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 0; + nHighNumber = 5; + break; + case CREATURE_SIZE_TINY: + //no tiny martials + //use simple instead + oItem = EquipWeaponProfSimple(oObject, nHands); + return oItem; + break; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //:: Tiny size + // no tiny martials + + //:: Small size + case 0:// Handaxe + sResRef = "nw_waxhn001"; + break; + case 1:// Light Hammer + sResRef = "nw_wblmhl001"; + break; + case 2:// Short Sword + sResRef = "nw_wswss001"; + break; + case 3:// Throwing Axe + sResRef = "nw_wthax001"; + break; + case 4:// Light Pick + sResRef = "prc_wblpl001"; + break; + case 5:// Sap + sResRef = "prc_wspsp001"; + break; + + //:: Medium size + case 6:// Battleaxe + sResRef = "nw_waxbt001"; + break; + case 7:// Light Flail + sResRef = "nw_wblfl001"; + break; + case 8:// Longsword + sResRef = "nw_wswls001"; + break; + case 9:// Rapier + sResRef = "nw_wswrp001"; + break; + case 10:// Scimitar + sResRef = "nw_wswsc001"; + break; + case 11:// Warhammer + sResRef = "nw_wblhw001"; + break; + case 12:// Heavy Pick + sResRef = "prc_wswhp001"; + break; + case 13:// Shortbow + sResRef = "nw_wbwsh001"; + break; + + //:: Large size + case 14:// Greataxe + sResRef = "nw_waxgr001"; + break; + case 15:// Greatsword + sResRef = "nw_wswgs001"; + break; + case 16:// Halberd + sResRef = "nw_wplhb001"; + break; + case 17:// Heavy Flail + sResRef = "nw_wblfh001"; + break; + case 18:// Falchion + sResRef = "prc_wswfa001"; + break; + case 19:// Maul + sResRef = "prc_wxblma001"; + break; + case 20:// Trident + sResRef = "nw_wpltr001"; + break; + case 21:// Longbow + sResRef = "nw_wbwln001"; + break; + //none + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; +} + +object EquipWeaponProfExotic(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 6; + nHighNumber = 10; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 3; + nHighNumber = 5; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 0; + nHighNumber = 7; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 4; + break; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 15; + nHighNumber = 10; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 8; + nHighNumber = 18; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 5; + nHighNumber = 7; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 2; + break; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //:: Tiny size + case 0:// Kama + sResRef = "nw_wspka001"; + break; + case 1:// Kukri + sResRef = "nw_wspku001"; + break; + case 2:// Sai + sResRef = "prc_wswsi001"; + break; + case 3:// Katar + sResRef = "prc_wswdp001"; + break; + case 4:// Shuriken + sResRef = "nw_wthsh001"; + break; + + //:: Small size + case 5:// Nunchaku + sResRef = "prc_wspnn001"; + break; + case 6:// Elven Lightblade + sResRef = "prc_wspel001"; + break; + case 7:// Eagle Claw + sResRef = "prc_wswec001"; + break; + + //:: Medium size + case 8:// Dwarven Waraxe + sResRef = "x2_wdwraxe001"; + break; + case 9:// Katana + sResRef = "nw_wswka001"; + break; + case 10:// Elven Thinblade + sResRef = "prc_wspet001"; + break; + case 11:// Whip + sResRef = "x2_it_wpwhip"; + break; + + //:: Large size + case 12:// Bastard Sword + sResRef = "nw_wswbs001"; + break; + case 13:// Scythe + sResRef = "nw_wplsc001"; + break; + case 14:// Elven Courtblade + sResRef = "prc_wspec001"; + break; + case 15:// Diremace + sResRef = "nw_wdbma001"; + break; + case 16:// Double Axe + sResRef = "nw_wdbax001"; + break; + case 17:// Double Sword + sResRef = "nw_wdbsw001"; + break; + case 18:// Double Scimitar + sResRef = "prc_wxdbsc001"; + break; + //none + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; +} + +void EquipArmor(object oObject = OBJECT_SELF) +{ + object oItem; + if(GetHasFeat(FEAT_ARMOR_PROFICIENCY_HEAVY, oObject) == TRUE + && GetLevelByClass(CLASS_TYPE_WIZARD, oObject) == 0 + && GetLevelByClass(CLASS_TYPE_SORCERER, oObject) == 0 + && GetLevelByClass(CLASS_TYPE_BARD, oObject) == 0) + oItem = EquipHeavyArmor(oObject); + else if(GetHasFeat(FEAT_ARMOR_PROFICIENCY_MEDIUM, oObject) == TRUE + && GetLevelByClass(CLASS_TYPE_WIZARD, oObject) == 0 + && GetLevelByClass(CLASS_TYPE_SORCERER, oObject) == 0) + oItem = EquipMediumArmor(oObject); + else if(GetHasFeat(FEAT_ARMOR_PROFICIENCY_LIGHT, oObject) == TRUE + && GetLevelByClass(CLASS_TYPE_WIZARD, oObject) == 0) + oItem = EquipLightArmor(oObject); + else + oItem = EquipClothes(oObject); + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); +} + +void EquipMisc(object oObject = OBJECT_SELF) +{ + int nLevel = GetHitDice(oObject); + object oItem; + //other stuff + //belt + if((Random(10) + nLevel) > 14) + { + oItem = CreateItemOnObject("belt",oObject); + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_BELT)); + } + //boots + if((Random(10) + nLevel) > 10) + { + oItem = CreateItemOnObject("boots",oObject); + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_BOOTS)); + } + //bracers + if((Random(10) + nLevel) > 15) + { + oItem = CreateItemOnObject("bracers",oObject); + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_ARMS)); + } + //amulet + if((Random(10) + nLevel) > 20) + { + oItem = CreateItemOnObject("amulet",oObject); + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_NECK)); + } + //ring + if((Random(10) + nLevel) > 23) + { + oItem = CreateItemOnObject("ring",oObject); + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_RIGHTRING)); + } + if((Random(10) + nLevel) > 23) + { + oItem = CreateItemOnObject("ring",oObject); + AssignCommand(oObject, ActionEquipItem(oItem,INVENTORY_SLOT_LEFTRING)); + } + //potions + int nCount = nLevel + d6() - 3; + int i; + for (i=1;i<=nCount;i++) + { + oItem = CreateItemOnObject("NW_IT_MPOTION"+FilledIntToString2(Random(23)+1),oObject); + if(GetGoldPieceValue(oItem)>25*nLevel) + DestroyObject(oItem); + if(GetGoldPieceValue(oItem)<5*nLevel) + DestroyObject(oItem); + } + //scrolls + EquipScroll(oObject); + //healing kits + nCount = GetSkillRank(SKILL_HEAL, oObject)/5+d6()-3; + for (i=1;i<=nCount;i++) + { + oItem = CreateItemOnObject("nw_it_medkit"+FilledIntToString2(Random(4)+1),oObject); + } + //lock picks + +} + +object EquipShield(object oObject) +{ + int nRandom = Random(3); + object oItem; + if(GetCreatureSize(oObject) <=CREATURE_SIZE_SMALL) + oItem = CreateItemOnObject("nw_ashsw001",oObject); + else if(GetCreatureSize(oObject) == CREATURE_SIZE_MEDIUM) + oItem = CreateItemOnObject("nw_ashlw001",oObject); + else if(GetCreatureSize(oObject) >= CREATURE_SIZE_LARGE) + oItem = CreateItemOnObject("nw_ashto001",oObject); + return oItem; +} + +object EquipWeaponProfDruid(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 6; + nHighNumber = 6; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 5; + nHighNumber = 5; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 1; + nHighNumber = 2; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 6; + nHighNumber = 6; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 5; + nHighNumber = 5; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 1; + nHighNumber = 4; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //tiny size + case 0://dagger + sResRef = "nw_wswdg001"; + break; + //small size + case 1://short spear + sResRef = "nw_wplss001"; + break; + case 2://scicle + sResRef = "nw_wspsc001"; + break; + case 3://sling + sResRef = "nw_wbwsl001"; + break; + case 4://dart + sResRef = "nw_wthdt001"; + break; + //medium size + case 5://scimitar + sResRef = "nw_wswsc001"; + break; + //large size + case 6://quarterstaff + sResRef = "nw_wdbqs001"; + break; + //none + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; +} + +object EquipWeaponProfRogue(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 13; + nHighNumber = 13; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 7; + nHighNumber = 12; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 1; + nHighNumber = 5; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 13; + nHighNumber = 13; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 7; + nHighNumber = 12; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 1; + nHighNumber = 7; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //:: Tiny size + case 0:// Dagger + sResRef = "nw_wswdg001"; + break; + + //:: Small size + case 1:// Light Mace + sResRef = "w_wblml001"; + break; + case 2:// Short Sword + sResRef = "nw_wswss001"; + break; + case 3:// Handaxe + sResRef = "nw_waxhn001"; + break; + case 4:// Sap + sResRef = "prc_wspsp001"; + break; + case 5:// Dart + sResRef = "nw_wthdt001"; + break; + case 6:// Sling + sResRef = "nw_wbwsl001"; + break; + case 7:// Light Xbow + sResRef = "nw_wbwxl001"; + break; + + //:: Medium size + case 8://club + sResRef = "nw_wblcl001"; + break; + case 9://morning star + sResRef = "nw_wblms001"; + break; + case 10://rapier + sResRef = "nw_wswrp001"; + break; + case 11://shorbow + sResRef = "nw_wbwsh001"; + break; + case 12://heavy xbow + sResRef = "w_wbwxh001"; + break; + + //:: Large size + case 13://quarterstaff + sResRef = "nw_wdbqs001"; + break; + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; +} + +object EquipWeaponProfWizard(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 4; + nHighNumber = 4; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 2; + nHighNumber = 2; + break; + case CREATURE_SIZE_SMALL: + //no small onehanded wizard, fall through + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 4; + nHighNumber = 4; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 2; + nHighNumber = 3; + break; + case CREATURE_SIZE_SMALL: + nLowNumber = 1; + nHighNumber = 1; + break; + case CREATURE_SIZE_TINY: + nLowNumber = 0; + nHighNumber = 0; + break; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //tiny size + case 0://dagger + sResRef = "nw_wswdg001"; + break; + //small size + case 1://light xbow + sResRef = "nw_wbwxl001"; + break; + //medium size + case 2://club + sResRef = "nw_wblcl001"; + break; + case 3://heavy xbow + sResRef = "w_wbwxh001"; + break; + //large size + case 4://quarterstaff + sResRef = "nw_wdbqs001"; + break; + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; +} + +object EquipWeaponProfElf(object oObject, int nHands = 1) +{ + int nMaxSize; + object oItem; + int nLowNumber; + int nHighNumber; + + if(nHands < 1 || nHands > 2) + nHands = Random(2)+1; + + if(nHands == 1) + { + nMaxSize = GetCreatureSize(oObject); + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + //fallthrough + case CREATURE_SIZE_MEDIUM: + nLowNumber = 0; + nHighNumber = 1; + break; + //should never happen to be small or tiny + case CREATURE_SIZE_SMALL: + case CREATURE_SIZE_TINY: + return OBJECT_INVALID; + } + } + else if(nHands == 2) + { + nMaxSize = GetCreatureSize(oObject)+1; + switch (nMaxSize) + { + case CREATURE_SIZE_HUGE: + //fallthrough + case CREATURE_SIZE_LARGE: + nLowNumber = 3; + nHighNumber = 3; + break; + case CREATURE_SIZE_MEDIUM: + nLowNumber = 0; + nHighNumber = 2; + break; + case CREATURE_SIZE_SMALL: + case CREATURE_SIZE_TINY: + return OBJECT_INVALID; + } + } + + int nRandom = nLowNumber+Random(nHighNumber-nLowNumber+1); + string sResRef; + switch(nRandom) + { + //:: Tiny size + // No tiny + + //:: Small size + // No small + + //:: Medium size + case 0:// Rapier + sResRef = "nw_wswrp001"; + break; + case 1:// Longsword + sResRef = "nw_wswls001"; + break; + case 2:// Short Bow + sResRef = "nw_wbwsh001"; + break; + + //:: Large size + case 3:// Longbow + sResRef = "nw_wbwln001"; + break; + } + oItem = CreateItemOnObject(sResRef,oObject); + return oItem; + +} + +object EquipWeaponOfChoice(object oObject) +{ + string sResRef; + if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_BASTARDSWORD, oObject)) + sResRef = "nw_wswbs001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_BATTLEAXE, oObject)) + sResRef = "nw_waxbt001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_CLUB, oObject)) + sResRef = "nw_wblcl001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_DAGGER, oObject)) + sResRef = "nw_wswdg001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_DIREMACE, oObject)) + sResRef = "nw_wdbma001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_DOUBLEAXE, oObject)) + sResRef = "nw_wdbax001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_DWAXE, oObject)) + sResRef = "x2_wdwraxe001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_GREATAXE, oObject)) + sResRef = "nw_waxgr001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_GREATSWORD, oObject)) + sResRef = "nw_wswgs001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_HALBERD, oObject)) + sResRef = "nw_wplhb001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_HANDAXE, oObject)) + sResRef = "nw_waxhn001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_HEAVYFLAIL, oObject)) + sResRef = "nw_wblfh001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_KAMA, oObject)) + sResRef = "nw_wspka001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_KATANA, oObject)) + sResRef = "nw_wswka001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_KUKRI, oObject)) + sResRef = "nw_wspku001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_LIGHTFLAIL, oObject)) + sResRef = "nw_wblfl001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_LIGHTHAMMER, oObject)) + sResRef = "nw_wblmhl001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_LIGHTMACE, oObject)) + sResRef = "w_wblml001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_LONGSWORD, oObject)) + sResRef = "nw_wswls001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_MORNINGSTAR, oObject)) + sResRef = "nw_wblms001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_QUARTERSTAFF, oObject)) + sResRef = "nw_wdbqs001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_RAPIER, oObject)) + sResRef = "nw_wswrp001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SCIMITAR, oObject)) + sResRef = "nw_wswsc001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SCYTHE, oObject)) + sResRef = "nw_wplsc001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SHORTSPEAR, oObject)) + sResRef = "nw_wplss001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SHORTSWORD, oObject)) + sResRef = "nw_wswss001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SICKLE, oObject)) + sResRef = "nw_wspsc001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_TWOBLADEDSWORD, oObject)) + sResRef = "nw_wdbsw001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_WARHAMMER, oObject)) + sResRef = "nw_wblhw001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_DBL_SCIMITAR , oObject)) + sResRef = "prc_wxdbsc001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW , oObject)) + sResRef = "prc_wswec001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE, oObject)) + sResRef = "prc_wspec001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE, oObject)) + sResRef = "prc_wspel001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE, oObject)) + sResRef = "prc_wspet001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_FALCHION, oObject)) + sResRef = "prc_wswfa001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_GOAD, oObject)) + sResRef = "prc_wspgd001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_HEAVY_MACE, oObject)) + sResRef = "prc_wxblmh001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_HEAVY_PICK, oObject)) + sResRef = "prc_wswhp001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_KATAR, oObject)) + sResRef = "prc_wswdp001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_LIGHT_LANCE, oObject)) + sResRef = "prc_wpllc001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_LIGHT_PICK, oObject)) + sResRef = "prc_wblpl001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_MAUL, oObject)) + sResRef = "prc_wxblma001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_NUNCHAKU, oObject)) + sResRef = "prc_wspnn001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SAI, oObject)) + sResRef = "prc_wswsi001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_SAP, oObject)) + sResRef = "prc_wswsi001"; + else if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_TRIDENT, oObject)) + sResRef = "nw_wpltr001"; + + if(sResRef == "") + return OBJECT_INVALID; + object oItem = CreateItemOnObject(sResRef,oObject); + EquipDebugString(GetName(oObject) + " has WeaponOfChoice "+GetName(oItem)); + return oItem; +} + +object EquipEpicWeaponSpecialization(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_BASTARDSWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_BATTLEAXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_DIREMACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_DOUBLEAXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_GREATAXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_GREATSWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_HANDAXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVYCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVYFLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTFLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTHAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTMACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 22: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LONGSWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 23: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_MORNINGSTAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_QUARTERSTAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 29: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 30: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 31: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 32: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 33: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 34: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_THROWINGAXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_TWOBLADEDSWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_WARHAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + break; + case 37: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + break; + case 38: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + break; + case 39: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + break; + case 40: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + break; + case 41: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + break; + case 42: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + break; + case 43: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + break; + case 44: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + break; + case 45: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + break; + case 46: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + break; + case 47: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + break; + case 48: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + break; + case 49: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_DBL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + break; + case 60: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + break; + case 51: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + break; + case 52: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + break; + case 53: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + break; + case 54: + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has EpicWeaponSpec "+GetName(oItem)); + return oItem; +} + +object EquipWeaponSpecialization(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DIRE_MACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GREAT_AXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HAND_AXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONG_SWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 22: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 23: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_MORNING_STAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_STAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 29: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 30: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 31: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 32: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 33: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 34: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_THROWING_AXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + break; + case 37: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + break; + case 38: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + break; + case 39: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + break; + case 40: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + break; + case 41: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + break; + case 42: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + break; + case 43: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + break; + case 44: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + break; + case 45: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + break; + case 46: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + break; + case 47: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + break; + case 48: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + break; + case 49: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + break; + case 50: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + break; + case 51: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + break; + case 52: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + break; + case 53: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + break; + case 54: + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has WeaponSpec "+GetName(oItem)); + return oItem; +} + +object EquipDevastatingCritical(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_BASTARDSWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_BATTLEAXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_DIREMACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_DOUBLEAXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_GREATAXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_GREATSWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_HANDAXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYFLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTFLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTHAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTMACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 22: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LONGSWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 23: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_MORNINGSTAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_QUARTERSTAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 29: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 30: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 31: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 32: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 33: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 34: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_THROWINGAXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_TWOBLADEDSWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_WARHAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + break; + case 37: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + break; + case 38: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + break; + case 39: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + break; + case 40: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + break; + case 41: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + break; + case 42: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + break; + case 43: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + break; + case 44: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + break; + case 45: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + break; + case 46: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + break; + case 47: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + break; + case 48: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + break; + case 49: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_DBL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + break; + case 50: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + break; + case 51: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + break; + case 52: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + break; + case 53: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + break; + case 54: + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has DevCrit "+GetName(oItem)); + return oItem; +} + +object EquipEpicWeaponFocus(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_BASTARDSWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_BATTLEAXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DIREMACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DOUBLEAXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_GREATAXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_GREATSWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_HANDAXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_HEAVYCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_HEAVYFLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LIGHTCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LIGHTFLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LIGHTHAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LIGHTMACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 22: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LONGSWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 23: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_MORNINGSTAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_QUARTERSTAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 29: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SHORTSPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 30: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SHORTSWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 31: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 32: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 33: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 34: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_THROWINGAXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_TWOBLADEDSWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_WARHAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + break; + case 37: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + break; + case 38: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + break; + case 39: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + break; + case 40: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + break; + case 41: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + break; + case 42: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + break; + case 43: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + break; + case 44: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + break; + case 45: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + break; + case 46: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + break; + case 47: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + break; + case 48: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + break; + case 49: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DBL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + break; + case 50: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + break; + case 51: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + break; + case 52: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + break; + case 53: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + break; + case 54: + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has EpicWeaponFocus "+GetName(oItem)); + return oItem; +} + +object EquipOverwhelmingCritical(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_BASTARDSWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_BATTLEAXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_DIREMACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_DOUBLEAXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_GREATAXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_GREATSWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_HANDAXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYFLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTCROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTFLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTHAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTMACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 22: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LONGSWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 23: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_MORNINGSTAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_QUARTERSTAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 29: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 30: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 31: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 32: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 33: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 34: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_THROWINGAXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_TWOBLADEDSWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_WARHAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + break; + case 37: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + break; + case 38: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + break; + case 39: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + break; + case 40: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + break; + case 41: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + break; + case 42: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + break; + case 43: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + break; + case 44: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + break; + case 45: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + break; + case 46: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + break; + case 47: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + break; + case 48: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + break; + case 49: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_DBL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + break; + case 50: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + break; + case 51: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + break; + case 52: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + break; + case 53: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + break; + case 54: + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has OverCrit "+GetName(oItem)); + return oItem; +} + +object EquipImprovedCritical(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_BASTARD_SWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_BATTLE_AXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_DIRE_MACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_DOUBLE_AXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_AXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_SWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_HAND_AXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_MACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LONG_SWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 22: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 23: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_MORNING_STAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_STAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORT_SWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 29: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 30: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 31: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 32: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 33: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 34: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_THROWING_AXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_WAR_HAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + case 37: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + case 38: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + case 39: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + case 40: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + case 41: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + case 42: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + case 43: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + case 44: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + case 45: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + case 46: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + case 47: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + case 48: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + case 49: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_DBL_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + case 50: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + case 51: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + case 52: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + case 53: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + case 54: + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has ImpCrit "+GetName(oItem)); + return oItem; +} + +object EquipWeaponFocus(object oObject) +{ + int nRandom = Random(55); + int nStartingPoint = nRandom; + int nBaseItemType = -1; + int bFirst = TRUE; + while(nBaseItemType == -1) + { + switch(nRandom) + { + case 0: + if(GetHasFeat(FEAT_WEAPON_FOCUS_BASTARD_SWORD,oObject)) + nBaseItemType = BASE_ITEM_BASTARDSWORD; + break; + case 1: + if(GetHasFeat(FEAT_WEAPON_FOCUS_BATTLE_AXE,oObject)) + nBaseItemType = BASE_ITEM_BATTLEAXE; + break; + case 2: + if(GetHasFeat(FEAT_WEAPON_FOCUS_CLUB,oObject)) + nBaseItemType = BASE_ITEM_CLUB; + break; + case 3: + if(GetHasFeat(FEAT_WEAPON_FOCUS_DAGGER,oObject)) + nBaseItemType = BASE_ITEM_DAGGER; + break; + case 4: + if(GetHasFeat(FEAT_WEAPON_FOCUS_DART,oObject)) + nBaseItemType = BASE_ITEM_DART; + break; + case 5: + if(GetHasFeat(FEAT_WEAPON_FOCUS_DIRE_MACE,oObject)) + nBaseItemType = BASE_ITEM_DIREMACE; + break; + case 6: + if(GetHasFeat(FEAT_WEAPON_FOCUS_DOUBLE_AXE,oObject)) + nBaseItemType = BASE_ITEM_DOUBLEAXE; + break; + case 7: + if(GetHasFeat(FEAT_WEAPON_FOCUS_DWAXE,oObject)) + nBaseItemType = BASE_ITEM_DWARVENWARAXE; + break; + case 8: + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_AXE,oObject)) + nBaseItemType = BASE_ITEM_GREATAXE; + break; + case 9: + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_SWORD,oObject)) + nBaseItemType = BASE_ITEM_GREATSWORD; + break; + case 10: + if(GetHasFeat(FEAT_WEAPON_FOCUS_HALBERD,oObject)) + nBaseItemType = BASE_ITEM_HALBERD; + break; + case 11: + if(GetHasFeat(FEAT_WEAPON_FOCUS_HAND_AXE,oObject)) + nBaseItemType = BASE_ITEM_HANDAXE; + break; + case 12: + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_HEAVYCROSSBOW; + break; + case 13: + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_FLAIL,oObject)) + nBaseItemType = BASE_ITEM_HEAVYFLAIL; + break; + case 14: + if(GetHasFeat(FEAT_WEAPON_FOCUS_KAMA,oObject)) + nBaseItemType = BASE_ITEM_KAMA; + break; + case 15: + if(GetHasFeat(FEAT_WEAPON_FOCUS_KATANA,oObject)) + nBaseItemType = BASE_ITEM_KATANA; + break; + case 16: + if(GetHasFeat(FEAT_WEAPON_FOCUS_KUKRI,oObject)) + nBaseItemType = BASE_ITEM_KUKRI; + break; + case 17: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW,oObject)) + nBaseItemType = BASE_ITEM_LIGHTCROSSBOW; + break; + case 18: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_FLAIL,oObject)) + nBaseItemType = BASE_ITEM_LIGHTFLAIL; + break; + case 19: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_HAMMER,oObject)) + nBaseItemType = BASE_ITEM_LIGHTHAMMER; + break; + case 20: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_MACE,oObject)) + nBaseItemType = BASE_ITEM_LIGHTMACE; + break; + case 21: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONG_SWORD,oObject)) + nBaseItemType = BASE_ITEM_LONGSWORD; + break; + case 22: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONGBOW,oObject)) + nBaseItemType = BASE_ITEM_LONGBOW; + break; + case 23: + if(GetHasFeat(FEAT_WEAPON_FOCUS_MORNING_STAR,oObject)) + nBaseItemType = BASE_ITEM_MORNINGSTAR; + break; + case 24: + if(GetHasFeat(FEAT_WEAPON_FOCUS_STAFF,oObject)) + nBaseItemType = BASE_ITEM_QUARTERSTAFF; + break; + case 25: + if(GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER,oObject)) + nBaseItemType = BASE_ITEM_RAPIER; + break; + case 26: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_SCIMITAR; + break; + case 27: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SCYTHE,oObject)) + nBaseItemType = BASE_ITEM_SCYTHE; + break; + case 28: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORT_SWORD,oObject)) + nBaseItemType = BASE_ITEM_SHORTSWORD; + break; + case 29: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORTBOW,oObject)) + nBaseItemType = BASE_ITEM_SHORTBOW; + break; + case 30: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHURIKEN,oObject)) + nBaseItemType = BASE_ITEM_SHURIKEN; + break; + case 31: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SICKLE,oObject)) + nBaseItemType = BASE_ITEM_SICKLE; + break; + case 32: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SLING,oObject)) + nBaseItemType = BASE_ITEM_SLING; + break; + case 33: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SPEAR,oObject)) + nBaseItemType = BASE_ITEM_SHORTSPEAR; + break; + case 34: + if(GetHasFeat(FEAT_WEAPON_FOCUS_THROWING_AXE,oObject)) + nBaseItemType = BASE_ITEM_THROWINGAXE; + break; + case 35: + if(GetHasFeat(FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD,oObject)) + nBaseItemType = BASE_ITEM_TWOBLADEDSWORD; + break; + case 36: + if(GetHasFeat(FEAT_WEAPON_FOCUS_WAR_HAMMER,oObject)) + nBaseItemType = BASE_ITEM_WARHAMMER; + break; + case 37: + if(GetHasFeat(FEAT_WEAPON_FOCUS_WHIP,oObject)) + nBaseItemType = BASE_ITEM_WHIP; + break; + case 38: + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_PICK,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_PICK; + break; + case 39: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_PICK,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_PICK; + break; + case 40: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SAI,oObject)) + nBaseItemType = BASE_ITEM_SAI; + break; + case 41: + if(GetHasFeat(FEAT_WEAPON_FOCUS_NUNCHAKU,oObject)) + nBaseItemType = BASE_ITEM_NUNCHAKU; + break; + case 42: + if(GetHasFeat(FEAT_WEAPON_FOCUS_FALCHION,oObject)) + nBaseItemType = BASE_ITEM_FALCHION; + break; + case 43: + if(GetHasFeat(FEAT_WEAPON_FOCUS_SAP,oObject)) + nBaseItemType = BASE_ITEM_SAP; + break; + case 44: + if(GetHasFeat(FEAT_WEAPON_FOCUS_KATAR,oObject)) + nBaseItemType = BASE_ITEM_KATAR; + break; + case 45: + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_MACE,oObject)) + nBaseItemType = BASE_ITEM_HEAVY_MACE; + break; + case 46: + if(GetHasFeat(FEAT_WEAPON_FOCUS_MAUL,oObject)) + nBaseItemType = BASE_ITEM_MAUL; + break; + case 47: + if(GetHasFeat(FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR,oObject)) + nBaseItemType = BASE_ITEM_DOUBLE_SCIMITAR; + break; + case 48: + if(GetHasFeat(FEAT_WEAPON_FOCUS_GOAD,oObject)) + nBaseItemType = BASE_ITEM_GOAD; + break; + case 49: + if(GetHasFeat(FEAT_WEAPON_FOCUS_EAGLE_CLAW,oObject)) + nBaseItemType = BASE_ITEM_EAGLE_CLAW; + break; + case 50: + if(GetHasFeat(FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_LIGHTBLADE; + break; + case 51: + if(GetHasFeat(FEAT_WEAPON_FOCUS_ELVEN_THINBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_THINBLADE; + break; + case 52: + if(GetHasFeat(FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE,oObject)) + nBaseItemType = BASE_ITEM_ELVEN_COURTBLADE; + break; + case 53: + if(GetHasFeat(FEAT_WEAPON_FOCUS_TRIDENT,oObject)) + nBaseItemType = BASE_ITEM_TRIDENT; + break; + case 54: + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_LANCE,oObject)) + nBaseItemType = BASE_ITEM_LIGHT_LANCE; + break; + } + if(nBaseItemType == -1) + { + nRandom++; + if(nRandom >=55 ) + nRandom = 0; + if(nRandom == nStartingPoint + && bFirst == FALSE) + nBaseItemType = -2; + } + bFirst = FALSE; + } + if(nBaseItemType == -2) + return OBJECT_INVALID; + object oItem = CreateItemOnObject(GetBaseResRef(nBaseItemType),oObject); + EquipDebugString(GetName(oObject) + " has WeaponFocus "+GetName(oItem)); + return oItem; +} + +int GetCanDualWeild(object oObject) +{ + if(GetHasFeat(FEAT_AMBIDEXTERITY,oObject) == TRUE + && d4()==1) + return TRUE; + if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING,oObject) == TRUE + && d4()==1) + return TRUE; + if(GetHasFeat(FEAT_IMPROVED_TWO_WEAPON_FIGHTING,oObject) == TRUE + && d2()==1) + return TRUE; + if(GetHasFeat(374,oObject) == TRUE + && d2()==1)//ranger dual-wielding + return TRUE; + return FALSE; +} + +int GetIsLeftHandFree(object oObject, object oWeapon) +{ + int nWeaponSize = PrimoGetWeaponSize(oWeapon); + int nCreatureSize = GetCreatureSize(oObject); + if(nWeaponSize <= nCreatureSize + &&GetWeaponRanged(oWeapon)==FALSE) + return TRUE; + return FALSE; +} + +int IsItemWeapon(object oItem) +{ + int bResult; + int bBase = GetBaseItemType(oItem); + if(bBase == BASE_ITEM_ARROW + || bBase == BASE_ITEM_BASTARDSWORD + || bBase == BASE_ITEM_BATTLEAXE + || bBase == BASE_ITEM_BOLT + || bBase == BASE_ITEM_BULLET + || bBase == BASE_ITEM_CBLUDGWEAPON + || bBase == BASE_ITEM_CPIERCWEAPON + || bBase == BASE_ITEM_CLUB + || bBase == BASE_ITEM_CSLASHWEAPON + || bBase == BASE_ITEM_CSLSHPRCWEAP + || bBase == BASE_ITEM_DAGGER + || bBase == BASE_ITEM_DART + || bBase == BASE_ITEM_DIREMACE + || bBase == BASE_ITEM_DOUBLE_SCIMITAR + || bBase == BASE_ITEM_DOUBLEAXE + || bBase == BASE_ITEM_DWARVENWARAXE + || bBase == BASE_ITEM_EAGLE_CLAW + || bBase == BASE_ITEM_ELVEN_COURTBLADE + || bBase == BASE_ITEM_ELVEN_LIGHTBLADE + || bBase == BASE_ITEM_ELVEN_THINBLADE + || bBase == BASE_ITEM_FALCHION + || bBase == BASE_ITEM_GOAD + || bBase == BASE_ITEM_GREATAXE + || bBase == BASE_ITEM_GREATSWORD + || bBase == BASE_ITEM_HALBERD + || bBase == BASE_ITEM_HANDAXE + || bBase == BASE_ITEM_HEAVY_MACE + || bBase == BASE_ITEM_HEAVY_PICK + || bBase == BASE_ITEM_HEAVYCROSSBOW + || bBase == BASE_ITEM_HEAVYFLAIL + || bBase == BASE_ITEM_KATANA + || bBase == BASE_ITEM_KATAR + || bBase == BASE_ITEM_KUKRI + || bBase == BASE_ITEM_LIGHT_PICK + || bBase == BASE_ITEM_LIGHTCROSSBOW + || bBase == BASE_ITEM_LIGHTFLAIL + || bBase == BASE_ITEM_LIGHTHAMMER + || bBase == BASE_ITEM_LIGHTMACE + || bBase == BASE_ITEM_LONGBOW + || bBase == BASE_ITEM_LONGSWORD + || bBase == BASE_ITEM_MAUL + || bBase == BASE_ITEM_MORNINGSTAR + || bBase == BASE_ITEM_NUNCHAKU + || bBase == BASE_ITEM_QUARTERSTAFF + || bBase == BASE_ITEM_RAPIER + || bBase == BASE_ITEM_SAI + || bBase == BASE_ITEM_SAP + || bBase == BASE_ITEM_SCIMITAR + || bBase == BASE_ITEM_SCYTHE + || bBase == BASE_ITEM_SHORTBOW + || bBase == BASE_ITEM_SHORTSPEAR + || bBase == BASE_ITEM_SHORTSWORD + || bBase == BASE_ITEM_SHURIKEN + || bBase == BASE_ITEM_SICKLE + || bBase == BASE_ITEM_SLING + || bBase == BASE_ITEM_THROWINGAXE + || bBase == BASE_ITEM_TRIDENT + || bBase == BASE_ITEM_TWOBLADEDSWORD + || bBase == BASE_ITEM_WARHAMMER + || bBase == BASE_ITEM_WHIP ) + bResult = TRUE; + return bResult; +} + +int PrimoGetWeaponSize(object oItem) +{ + int bResult; + int bBase = GetBaseItemType(oItem); + switch(bBase) + { + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_HALBERD: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_LONGBOW: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_TWOBLADEDSWORD: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_SHORTSPEAR: + case BASE_ITEM_DOUBLE_SCIMITAR: + case BASE_ITEM_MAUL: + case BASE_ITEM_FALCHION: + case BASE_ITEM_ELVEN_COURTBLADE: + case BASE_ITEM_LIGHT_LANCE: + bResult = CREATURE_SIZE_LARGE; + break; + case BASE_ITEM_BATTLEAXE: + case BASE_ITEM_BASTARDSWORD: + case BASE_ITEM_CLUB: + case BASE_ITEM_HEAVYCROSSBOW: + case BASE_ITEM_DWARVENWARAXE: + case BASE_ITEM_LIGHTFLAIL: + case BASE_ITEM_KATANA: + case BASE_ITEM_MAGICSTAFF: + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_TRIDENT: + case BASE_ITEM_MORNINGSTAR: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SCIMITAR: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_WARHAMMER: + case BASE_ITEM_WHIP: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_HEAVY_MACE: + case BASE_ITEM_HEAVY_PICK: + bResult = CREATURE_SIZE_MEDIUM ; + break; + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_DART: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_HANDAXE: + case BASE_ITEM_LIGHTMACE: + case BASE_ITEM_SICKLE: + case BASE_ITEM_SLING: + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_TORCH: + case BASE_ITEM_KAMA: + case BASE_ITEM_EAGLE_CLAW: + case BASE_ITEM_ELVEN_LIGHTBLADE: + case BASE_ITEM_ELVEN_THINBLADE: + case BASE_ITEM_NUNCHAKU: + case BASE_ITEM_LIGHT_PICK: + case BASE_ITEM_GOAD: + bResult = CREATURE_SIZE_SMALL; + break; + case BASE_ITEM_KUKRI: + case BASE_ITEM_DAGGER: + case BASE_ITEM_SHURIKEN: + case BASE_ITEM_SAP: + case BASE_ITEM_SAI: + case BASE_ITEM_KATAR: + bResult = CREATURE_SIZE_TINY; + break; + default: + bResult = 0; //invalid + } + return bResult; +} + +object EquipHeavyArmor(object oObject) +{ + int nRandom = Random(4); + object oItem; + switch(nRandom) + { + case 0: + oItem = CreateItemOnObject("nw_aarcl011",oObject); + break; + case 1: + oItem = CreateItemOnObject("nw_aarcl007",oObject); + break; + case 2: + oItem = CreateItemOnObject("nw_aarcl006",oObject); + break; + case 3: + oItem = CreateItemOnObject("nw_aarcl005",oObject); + break; + } + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); + return oItem; +} + +object EquipMediumArmor(object oObject) +{ + int nRandom = Random(4); + object oItem; + switch(nRandom) + { + case 0: + oItem = CreateItemOnObject("nw_aarcl010",oObject); + break; + case 1: + oItem = CreateItemOnObject("nw_aarcl004",oObject); + break; + case 2://chain shirt + oItem = CreateItemOnObject("nw_aarcl012",oObject); + break; + case 3: + oItem = CreateItemOnObject("nw_aarcl003",oObject); + break; + } + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); + return oItem; +} + +object EquipLightArmor(object oObject) +{ + int nRandom = Random(4); + object oItem; + switch(nRandom) + { + case 0: //hide + oItem = CreateItemOnObject("nw_aarcl008",oObject); + break; + case 1: + oItem = CreateItemOnObject("nw_aarcl001",oObject); + break; + case 2: + oItem = CreateItemOnObject("nw_aarcl009",oObject); + break; + case 3: + oItem = CreateItemOnObject("nw_aarcl002",oObject); + break; + } + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); + return oItem; +} + +object EquipClothes(object oObject) +{ + int nClass = GetClassByPosition(2, oObject); + if(nClass == CLASS_TYPE_INVALID) + nClass = GetClassByPosition(1, oObject); + object oItem; + switch(nClass) + { + case CLASS_TYPE_MONK: + oItem = CreateItemOnObject("nw_cloth016",oObject); + break; + case CLASS_TYPE_SORCERER: + oItem = CreateItemOnObject("nw_cloth008",oObject); + break; + case CLASS_TYPE_WIZARD: + oItem = CreateItemOnObject("nw_cloth005",oObject); + break; + default: + oItem = CreateItemOnObject("nw_cloth001",oObject); + break; + } + AssignCommand(oObject, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); + return oItem; +} + +string GetBaseResRef(int nBaseItemType) +{ + string sResRef; + switch(nBaseItemType) + { + case BASE_ITEM_BASTARDSWORD: + sResRef = "nw_wswbs001"; + break; + case BASE_ITEM_BATTLEAXE: + sResRef = "nw_waxbt001"; + break; + case BASE_ITEM_CLUB: + sResRef = "nw_wblcl001"; + break; + case BASE_ITEM_DAGGER: + sResRef = "nw_wswdg001"; + break; + case BASE_ITEM_DART: + sResRef = "nw_wthdt001"; + break; + case BASE_ITEM_DIREMACE: + sResRef = "nw_wdbma001"; + break; + case BASE_ITEM_DOUBLEAXE: + sResRef = "nw_wdbax001"; + break; + case BASE_ITEM_DOUBLE_SCIMITAR: + sResRef = "prc_wxdbsc001"; + break; + case BASE_ITEM_DWARVENWARAXE: + sResRef = "x2_wdwraxe001"; + break; + case BASE_ITEM_GREATAXE: + sResRef = "nw_waxgr001"; + break; + case BASE_ITEM_EAGLE_CLAW: + sResRef = "prc_wswec001"; + break; + case BASE_ITEM_ELVEN_COURTBLADE: + sResRef = "prc_wspec001"; + break; + case BASE_ITEM_ELVEN_LIGHTBLADE: + sResRef = "prc_wspel001"; + break; + case BASE_ITEM_ELVEN_THINBLADE: + sResRef = "prc_wspet001"; + break; + case BASE_ITEM_FALCHION: + sResRef = "prc_wswfa001"; + break; + case BASE_ITEM_GOAD: + sResRef = "prc_spgd001"; + break; + case BASE_ITEM_GREATSWORD: + sResRef = "nw_wswgs001"; + break; + case BASE_ITEM_HALBERD: + sResRef = "nw_wplhb001"; + break; + case BASE_ITEM_HANDAXE: + sResRef = "nw_waxhn001"; + break; + case BASE_ITEM_HEAVYCROSSBOW: + sResRef = "w_wbwxh001"; + break; + case BASE_ITEM_HEAVYFLAIL: + sResRef = "nw_wblfh001"; + break; + case BASE_ITEM_HEAVY_MACE: + sResRef = "prc_wxblmh001"; + break; + case BASE_ITEM_HEAVY_PICK: + sResRef = "prc_wblph001"; + break; + case BASE_ITEM_KAMA: + sResRef = "nw_wspka001"; + break; + case BASE_ITEM_KATANA: + sResRef = "nw_wswka001"; + break; + case BASE_ITEM_KATAR: + sResRef = "prc_wswdp001"; + break; + case BASE_ITEM_KUKRI: + sResRef = "nw_wspku001"; + break; + case BASE_ITEM_LIGHTCROSSBOW: + sResRef = "nw_wbwxl001"; + break; + case BASE_ITEM_LIGHTFLAIL: + sResRef = "nw_wblfl001"; + break; + case BASE_ITEM_LIGHTHAMMER: + sResRef = "nw_wblmhl001"; + break; + case BASE_ITEM_LIGHT_LANCE: + sResRef = "prc_wpllc001"; + break; + case BASE_ITEM_LIGHTMACE: + sResRef = "w_wblml001"; + break; + case BASE_ITEM_LIGHT_PICK: + sResRef = "prc_wblpl001"; + break; + case BASE_ITEM_LONGBOW: + sResRef = "nw_wbwln001"; + break; + case BASE_ITEM_LONGSWORD: + sResRef = "nw_wswls001"; + break; + case BASE_ITEM_MORNINGSTAR: + sResRef = "nw_wblms001"; + break; + case BASE_ITEM_MAUL: + sResRef = "prc_wxblma001"; + break; + case BASE_ITEM_NUNCHAKU: + sResRef = "prc_wblnn001"; + break; + case BASE_ITEM_QUARTERSTAFF: + sResRef = "nw_wdbqs001"; + break; + case BASE_ITEM_RAPIER: + sResRef = "nw_wswrp001"; + break; + case BASE_ITEM_SAI: + sResRef = "prc_wswsi001"; + break; + case BASE_ITEM_SAP: + sResRef = "prc_wspsp001"; + break; + case BASE_ITEM_SCIMITAR: + sResRef = "nw_wswsc001"; + break; + case BASE_ITEM_SCYTHE: + sResRef = "nw_wplsc001"; + break; + case BASE_ITEM_SHORTBOW: + sResRef = "nw_wbwsh001"; + break; + case BASE_ITEM_SHORTSPEAR: + sResRef = "nw_wplss001"; + break; + case BASE_ITEM_SHORTSWORD: + sResRef = "nw_wswss001"; + break; + case BASE_ITEM_SHURIKEN: + sResRef = "nw_wthsh001"; + break; + case BASE_ITEM_SICKLE: + sResRef = "nw_wspsc001"; + break; + case BASE_ITEM_SLING: + sResRef = "nw_wbwsl001"; + break; + case BASE_ITEM_TRIDENT: + sResRef = "nw_wpltr001"; + break; + case BASE_ITEM_THROWINGAXE: + sResRef = "nw_wthax001"; + break; + case BASE_ITEM_TWOBLADEDSWORD: + sResRef = "nw_wdbsw001"; + break; + case BASE_ITEM_WARHAMMER: + sResRef = "nw_wblhw001"; + break; + } + return sResRef; +} + +void EquipScroll(object oObject) +{ + object oItem; + int nRandom; + int nCount = GetLevelByClass(CLASS_TYPE_CLERIC, oObject) + + GetLevelByClass(CLASS_TYPE_WIZARD, oObject) + + GetLevelByClass(CLASS_TYPE_DRUID, oObject) + + GetLevelByClass(CLASS_TYPE_SORCERER, oObject) + + GetLevelByClass(CLASS_TYPE_BARD, oObject) + + (GetLevelByClass(CLASS_TYPE_ROGUE, oObject)/3)+ d6() - 3; + int i; + for (i=1;i<=nCount;i++) + { + nRandom = Random(287)+1; + switch(nRandom) + { + case 1: + oItem = CreateItemOnObject("x1_it_sparscr002", oObject); + break; + case 2: + oItem = CreateItemOnObject("nw_it_sparscr003", oObject); + break; + case 3: + oItem = CreateItemOnObject("x1_it_sparscr003", oObject); + break; + case 4: + oItem = CreateItemOnObject("x1_it_sparscr001", oObject); + break; + case 5: + oItem = CreateItemOnObject("x1_it_spdvscr001", oObject); + break; + case 6: + oItem = CreateItemOnObject("nw_it_sparscr004", oObject); + break; + case 7: + oItem = CreateItemOnObject("nw_it_sparscr002", oObject); + break; + case 8: + oItem = CreateItemOnObject("nw_it_sparscr001", oObject); + break; + case 9: + oItem = CreateItemOnObject("x2_it_spdvscr001", oObject); + break; + case 10: + oItem = CreateItemOnObject("x2_it_spdvscr002", oObject); + break; + case 11: + oItem = CreateItemOnObject("x1_it_sparscr102", oObject); + break; + case 12: + oItem = CreateItemOnObject("x1_it_spdvscr101", oObject); + break; + case 13: + oItem = CreateItemOnObject("nw_it_sparscr112", oObject); + break; + case 14: + oItem = CreateItemOnObject("x1_it_spdvscr107", oObject); + break; + case 15: + oItem = CreateItemOnObject("nw_it_sparscr107", oObject); + break; + case 16: + oItem = CreateItemOnObject("nw_it_sparscr110", oObject); + break; + case 17: + oItem = CreateItemOnObject("x1_it_spdvscr102", oObject); + break; + case 18: + oItem = CreateItemOnObject("nw_it_sparscr101", oObject); + break; + case 19: + oItem = CreateItemOnObject("x1_it_spdvscr103", oObject); + break; + case 20: + oItem = CreateItemOnObject("x1_it_sparscr101", oObject); + break; + case 21: + oItem = CreateItemOnObject("nw_it_sparscr103", oObject); + break; + case 22: + oItem = CreateItemOnObject("nw_it_sparscr106", oObject); + break; + case 23: + oItem = CreateItemOnObject("x1_it_spdvscr104", oObject); + break; + case 24: + oItem = CreateItemOnObject("nw_it_sparscr104", oObject); + break; + case 25: + oItem = CreateItemOnObject("x1_it_spdvscr106", oObject); + break; + case 26: + oItem = CreateItemOnObject("nw_it_sparscr109", oObject); + break; + case 27: + oItem = CreateItemOnObject("nw_it_sparscr113", oObject); + break; + case 28: + oItem = CreateItemOnObject("nw_it_sparscr102", oObject); + break; + case 29: + oItem = CreateItemOnObject("nw_it_sparscr111", oObject); + break; + case 30: + oItem = CreateItemOnObject("nw_it_sparscr210", oObject); + break; + case 31: + oItem = CreateItemOnObject("x1_it_sparscr103", oObject); + break; + case 32: + oItem = CreateItemOnObject("x1_it_spdvscr105", oObject); + break; + case 33: + oItem = CreateItemOnObject("nw_it_sparscr108", oObject); + break; + case 34: + oItem = CreateItemOnObject("nw_it_sparscr105", oObject); + break; + case 35: + oItem = CreateItemOnObject("x1_it_sparscr104", oObject); + break; + case 36: + oItem = CreateItemOnObject("x2_it_spdvscr103", oObject); + break; + case 37: + oItem = CreateItemOnObject("x2_it_spdvscr102", oObject); + break; + case 38: + oItem = CreateItemOnObject("x2_it_spdvscr104", oObject); + break; + case 39: + oItem = CreateItemOnObject("x2_it_spdvscr101", oObject); + break; + case 40: + oItem = CreateItemOnObject("x2_it_spdvscr105", oObject); + break; + case 41: + oItem = CreateItemOnObject("x2_it_spdvscr106", oObject); + break; + case 42: + oItem = CreateItemOnObject("x2_it_spavscr101", oObject); + break; + case 43: + oItem = CreateItemOnObject("x2_it_spavscr104", oObject); + break; + case 44: + oItem = CreateItemOnObject("x2_it_spavscr102", oObject); + break; + case 45: + oItem = CreateItemOnObject("x2_it_spavscr105", oObject); + break; + case 46: + oItem = CreateItemOnObject("x2_it_sparscral", oObject); + break; + case 47: + oItem = CreateItemOnObject("x2_it_spdvscr107", oObject); + break; + case 48: + oItem = CreateItemOnObject("x2_it_spdvscr108", oObject); + break; + case 49: + oItem = CreateItemOnObject("x2_it_spavscr103", oObject); + break; + case 50: + oItem = CreateItemOnObject("x1_it_spdvscr204", oObject); + break; + case 51: + oItem = CreateItemOnObject("x1_it_sparscr201", oObject); + break; + case 52: + oItem = CreateItemOnObject("nw_it_sparscr211", oObject); + break; + case 53: + oItem = CreateItemOnObject("x1_it_spdvscr202", oObject); + break; + case 54: + oItem = CreateItemOnObject("nw_it_sparscr212", oObject); + break; + case 55: + oItem = CreateItemOnObject("nw_it_sparscr213", oObject); + break; + case 56: + oItem = CreateItemOnObject("nw_it_spdvscr202", oObject); + break; + case 57: + oItem = CreateItemOnObject("x1_it_sparscr301", oObject); + break; + case 58: + oItem = CreateItemOnObject("nw_it_sparscr206", oObject); + break; + case 59: + oItem = CreateItemOnObject("nw_it_sparscr219", oObject); + break; + case 60: + oItem = CreateItemOnObject("nw_it_sparscr215", oObject); + break; + case 61: + oItem = CreateItemOnObject("x1_it_spdvscr205", oObject); + break; + case 62: + oItem = CreateItemOnObject("nw_it_sparscr220", oObject); + break; + case 63: + oItem = CreateItemOnObject("nw_it_sparscr208", oObject); + break; + case 64: + oItem = CreateItemOnObject("nw_it_sparscr209", oObject); + break; + case 65: + oItem = CreateItemOnObject("x1_it_spdvscr201", oObject); + break; + case 66: + oItem = CreateItemOnObject("nw_it_sparscr207", oObject); + break; + case 67: + oItem = CreateItemOnObject("nw_it_sparscr216", oObject); + break; + case 68: + oItem = CreateItemOnObject("nw_it_sparscr218", oObject); + break; + case 69: + oItem = CreateItemOnObject("nw_it_spdvscr201", oObject); + break; + case 70: + oItem = CreateItemOnObject("nw_it_sparscr202", oObject); + break; + case 71: + oItem = CreateItemOnObject("x1_it_spdvscr203", oObject); + break; + case 72: + oItem = CreateItemOnObject("nw_it_sparscr221", oObject); + break; + case 73: + oItem = CreateItemOnObject("nw_it_sparscr201", oObject); + break; + case 74: + oItem = CreateItemOnObject("nw_it_sparscr205", oObject); + break; + case 75: + oItem = CreateItemOnObject("nw_it_spdvscr203", oObject); + break; + case 76: + oItem = CreateItemOnObject("nw_it_spdvscr204", oObject); + break; + case 77: + oItem = CreateItemOnObject("nw_it_sparscr203", oObject); + break; + case 78: + oItem = CreateItemOnObject("x1_it_sparscr202", oObject); + break; + case 79: + oItem = CreateItemOnObject("nw_it_sparscr214", oObject); + break; + case 80: + oItem = CreateItemOnObject("nw_it_sparscr204", oObject); + break; + case 81: + oItem = CreateItemOnObject("x2_it_spdvscr201", oObject); + break; + case 82: + oItem = CreateItemOnObject("x2_it_spdvscr202", oObject); + break; + case 83: + oItem = CreateItemOnObject("x2_it_spavscr207", oObject); + break; + case 84: + oItem = CreateItemOnObject("x2_it_spavscr206", oObject); + break; + case 85: + oItem = CreateItemOnObject("x2_it_spavscr201", oObject); + break; + case 86: + oItem = CreateItemOnObject("x2_it_spdvscr203", oObject); + break; + case 87: + oItem = CreateItemOnObject("x2_it_spavscr201", oObject); + break; + case 88: + oItem = CreateItemOnObject("x2_it_spavscr205", oObject); + break; + case 89: + oItem = CreateItemOnObject("x2_it_spavscr203", oObject); + break; + case 90: + oItem = CreateItemOnObject("x2_it_spdvscr204", oObject); + break; + case 91: + oItem = CreateItemOnObject("x2_it_spdvscr205", oObject); + break; + case 92: + oItem = CreateItemOnObject("x2_it_spavscr204", oObject); + break; + case 93: + oItem = CreateItemOnObject("nw_it_sparscr307", oObject); + break; + case 94: + oItem = CreateItemOnObject("nw_it_sparscr217", oObject); + break; + case 95: + oItem = CreateItemOnObject("nw_it_sparscr301", oObject); + break; + case 96: + oItem = CreateItemOnObject("x1_it_sparscr301", oObject); + break; + case 97: + oItem = CreateItemOnObject("nw_it_sparscr309", oObject); + break; + case 98: + oItem = CreateItemOnObject("nw_it_sparscr304", oObject); + break; + case 99: + oItem = CreateItemOnObject("x1_it_spdvscr303", oObject); + break; + case 100: + oItem = CreateItemOnObject("x1_it_sparscr303", oObject); + break; + case 101: + oItem = CreateItemOnObject("nw_it_sparscr312", oObject); + break; + case 102: + oItem = CreateItemOnObject("nw_it_sparscr308", oObject); + break; + case 103: + oItem = CreateItemOnObject("x1_it_spdvscr302", oObject); + break; + case 104: + oItem = CreateItemOnObject("nw_it_sparscr314", oObject); + break; + case 105: + oItem = CreateItemOnObject("nw_it_sparscr310", oObject); + break; + case 106: + oItem = CreateItemOnObject("nw_it_sparscr302", oObject); + break; + case 107: + oItem = CreateItemOnObject("nw_it_sparscr315", oObject); + break; + case 108: + oItem = CreateItemOnObject("nw_it_sparscr303", oObject); + break; + case 109: + oItem = CreateItemOnObject("x1_it_spdvscr305", oObject); + break; + case 110: + oItem = CreateItemOnObject("nw_it_spdvscr302", oObject); + break; + case 111: + oItem = CreateItemOnObject("nw_it_sparscr313", oObject); + break; + case 112: + oItem = CreateItemOnObject("x1_it_spdvscr304", oObject); + break; + case 113: + oItem = CreateItemOnObject("nw_it_sparscr305", oObject); + break; + case 114: + oItem = CreateItemOnObject("nw_it_sparscr306", oObject); + break; + case 115: + oItem = CreateItemOnObject("nw_it_sparscr311", oObject); + break; + case 116: + oItem = CreateItemOnObject("x1_it_sparscr302", oObject); + break; + case 117: + oItem = CreateItemOnObject("x2_it_spdvscr303", oObject); + break; + case 118: + oItem = CreateItemOnObject("x2_it_spdvscr307", oObject); + break; + case 119: + oItem = CreateItemOnObject("x2_it_spdvscr308", oObject); + break; + case 120: + oItem = CreateItemOnObject("x2_it_spdvscr305", oObject); + break; + case 121: + oItem = CreateItemOnObject("x2_it_spdvscr309", oObject); + break; + case 122: + oItem = CreateItemOnObject("x2_it_spavscr305", oObject); + break; + case 123: + oItem = CreateItemOnObject("x2_it_spdvscr306", oObject); + break; + case 124: + oItem = CreateItemOnObject("x2_it_spavscr304", oObject); + break; + case 125: + oItem = CreateItemOnObject("x2_it_spdvscr302", oObject); + break; + case 126: + oItem = CreateItemOnObject("x2_it_spdvscr301", oObject); + break; + case 127: + oItem = CreateItemOnObject("x2_it_spdvscr310", oObject); + break; + case 128: + oItem = CreateItemOnObject("x2_it_spavscr303", oObject); + break; + case 129: + oItem = CreateItemOnObject("x2_it_sparscrmc", oObject); + break; + case 130: + oItem = CreateItemOnObject("x2_it_spdvscr304", oObject); + break; + case 131: + oItem = CreateItemOnObject("x2_it_spavscr301", oObject); + break; + case 132: + oItem = CreateItemOnObject("x2_it_spdvscr311", oObject); + break; + case 133: + oItem = CreateItemOnObject("x2_it_spdvscr312", oObject); + break; + case 134: + oItem = CreateItemOnObject("x2_it_spavscr302", oObject); + break; + case 135: + oItem = CreateItemOnObject("x2_it_spdvscr313", oObject); + break; + case 136: + oItem = CreateItemOnObject("nw_it_sparscr414", oObject); + break; + case 137: + oItem = CreateItemOnObject("nw_it_sparscr405", oObject); + break; + case 138: + oItem = CreateItemOnObject("nw_it_sparscr406", oObject); + break; + case 139: + oItem = CreateItemOnObject("nw_it_sparscr411", oObject); + break; + case 140: + oItem = CreateItemOnObject("nw_it_sparscr416", oObject); + break; + case 141: + oItem = CreateItemOnObject("nw_it_sparscr412", oObject); + break; + case 142: + oItem = CreateItemOnObject("nw_it_sparscr418", oObject); + break; + case 143: + oItem = CreateItemOnObject("nw_it_sparscr413", oObject); + break; + case 144: + oItem = CreateItemOnObject("nw_it_sparscr408", oObject); + break; + case 145: + oItem = CreateItemOnObject("x1_it_spdvscr401", oObject); + break; + case 146: + oItem = CreateItemOnObject("x1_it_sparscr401", oObject); + break; + case 147: + oItem = CreateItemOnObject("nw_it_sparscr417", oObject); + break; + case 148: + oItem = CreateItemOnObject("x1_it_spdvscr402", oObject); + break; + case 149: + oItem = CreateItemOnObject("nw_it_sparscr401", oObject); + break; + case 150: + oItem = CreateItemOnObject("nw_it_spdvscr402", oObject); + break; + case 151: + oItem = CreateItemOnObject("nw_it_sparscr409", oObject); + break; + case 152: + oItem = CreateItemOnObject("nw_it_sparscr415", oObject); + break; + case 153: + oItem = CreateItemOnObject("nw_it_sparscr402", oObject); + break; + case 154: + oItem = CreateItemOnObject("nw_it_spdvscr401", oObject); + break; + case 155: + oItem = CreateItemOnObject("nw_it_sparscr410", oObject); + break; + case 156: + oItem = CreateItemOnObject("nw_it_sparscr403", oObject); + break; + case 157: + oItem = CreateItemOnObject("nw_it_sparscr404", oObject); + break; + case 158: + oItem = CreateItemOnObject("nw_it_sparscr407", oObject); + break; + case 159: + oItem = CreateItemOnObject("x2_it_spdvscr402", oObject); + break; + case 160: + oItem = CreateItemOnObject("x2_it_spdvscr403", oObject); + break; + case 161: + oItem = CreateItemOnObject("x2_it_spdvscr404", oObject); + break; + case 162: + oItem = CreateItemOnObject("x2_it_spdvscr405", oObject); + break; + case 163: + oItem = CreateItemOnObject("x2_it_spdvscr406", oObject); + break; + case 164: + oItem = CreateItemOnObject("x2_it_spdvscr401", oObject); + break; + case 165: + oItem = CreateItemOnObject("x2_it_spavscr401", oObject); + break; + case 166: + oItem = CreateItemOnObject("x2_it_spdvscr407", oObject); + break; + case 167: + oItem = CreateItemOnObject("nw_it_sparscr509", oObject); + break; + case 168: + oItem = CreateItemOnObject("x1_it_sparscr502", oObject); + break; + case 169: + oItem = CreateItemOnObject("nw_it_sparscr502", oObject); + break; + case 170: + oItem = CreateItemOnObject("nw_it_sparscr507", oObject); + break; + case 171: + oItem = CreateItemOnObject("nw_it_sparscr501", oObject); + break; + case 172: + oItem = CreateItemOnObject("nw_it_sparscr503", oObject); + break; + case 173: + oItem = CreateItemOnObject("nw_it_sparscr504", oObject); + break; + case 174: + oItem = CreateItemOnObject("x1_it_sparscr501", oObject); + break; + case 175: + oItem = CreateItemOnObject("x1_it_spdvscr403", oObject); + break; + case 176: + oItem = CreateItemOnObject("nw_it_sparscr508", oObject); + break; + case 177: + oItem = CreateItemOnObject("nw_it_sparscr505", oObject); + break; + case 178: + oItem = CreateItemOnObject("x1_it_spdvscr501", oObject); + break; + case 179: + oItem = CreateItemOnObject("nw_it_sparscr511", oObject); + break; + case 180: + oItem = CreateItemOnObject("nw_it_sparscr512", oObject); + break; + case 181: + oItem = CreateItemOnObject("nw_it_sparscr513", oObject); + break; + case 182: + oItem = CreateItemOnObject("nw_it_sparscr506", oObject); + break; + case 183: + oItem = CreateItemOnObject("x1_it_spdvscr502", oObject); + break; + case 184: + oItem = CreateItemOnObject("nw_it_spdvscr501", oObject);//raisedead + break; + case 185: + oItem = CreateItemOnObject("nw_it_sparscr510", oObject); + break; + case 186: + oItem = CreateItemOnObject("x2_it_spdvscr508", oObject); + break; + case 187: + oItem = CreateItemOnObject("x2_it_spavscr501", oObject); + break; + case 188: + oItem = CreateItemOnObject("x2_it_spdvscr501", oObject); + break; + case 189: + oItem = CreateItemOnObject("x2_it_spdvscr504", oObject); + break; + case 190: + oItem = CreateItemOnObject("x2_it_spavscr503", oObject); + break; + case 191: + oItem = CreateItemOnObject("x2_it_spdvscr509", oObject); + break; + case 192: + oItem = CreateItemOnObject("x2_it_spdvscr505", oObject); + break; + case 193: + oItem = CreateItemOnObject("x2_it_spavscr502", oObject); + break; + case 194: + oItem = CreateItemOnObject("x2_it_spdvscr502", oObject); + break; + case 195: + oItem = CreateItemOnObject("x2_it_spdvscr506", oObject); + break; + case 196: + oItem = CreateItemOnObject("x2_it_spdvscr507", oObject); + break; + case 197: + oItem = CreateItemOnObject("x2_it_spdvscr503", oObject); + break; + case 198: + oItem = CreateItemOnObject("nw_it_sparscr603", oObject); + break; + case 199: + oItem = CreateItemOnObject("x1_it_sparscr602", oObject); + break; + case 200: + oItem = CreateItemOnObject("nw_it_sparscr607", oObject); + break; + case 201: + oItem = CreateItemOnObject("nw_it_sparscr610", oObject); + break; + case 202: + oItem = CreateItemOnObject("x1_it_sparscr601", oObject); + break; + case 203: + oItem = CreateItemOnObject("x1_it_spdvscr604", oObject); + break; + case 204: + oItem = CreateItemOnObject("nw_it_sparscr608", oObject); + break; + case 205: + oItem = CreateItemOnObject("x1_it_sparscr605", oObject); + break; + case 206: + oItem = CreateItemOnObject("nw_it_sparscr601", oObject); + break; + case 207: + oItem = CreateItemOnObject("nw_it_sparscr602", oObject); + break; + case 208: + oItem = CreateItemOnObject("nw_it_sparscr612", oObject); + break; + case 209: + oItem = CreateItemOnObject("nw_it_sparscr613", oObject); + break; + case 210: + oItem = CreateItemOnObject("x1_it_sparscr603", oObject); + break; + case 211: + oItem = CreateItemOnObject("nw_it_sparscr611", oObject); + break; + case 212: + oItem = CreateItemOnObject("x1_it_spdvscr603", oObject); + break; + case 213: + oItem = CreateItemOnObject("nw_it_sparscr604", oObject); + break; + case 214: + oItem = CreateItemOnObject("nw_it_sparscr609", oObject); + break; + case 215: + oItem = CreateItemOnObject("x1_it_sparscr604", oObject); + break; + case 216: + oItem = CreateItemOnObject("nw_it_sparscr605", oObject); + break; + case 217: + oItem = CreateItemOnObject("nw_it_sparscr614", oObject); + break; + case 218: + oItem = CreateItemOnObject("nw_it_sparscr606", oObject); + break; + case 219: + oItem = CreateItemOnObject("x2_it_spdvscr603", oObject); + break; + case 220: + oItem = CreateItemOnObject("x2_it_spdvscr601", oObject); + break; + case 221: + oItem = CreateItemOnObject("x2_it_spdvscr606", oObject); + break; + case 222: + oItem = CreateItemOnObject("x2_it_spdvscr604", oObject); + break; + case 223: + oItem = CreateItemOnObject("x2_it_spdvscr605", oObject); + break; + case 224: + oItem = CreateItemOnObject("x2_it_spavscr602", oObject); + break; + case 225: + oItem = CreateItemOnObject("x2_it_spdvscr602", oObject); + break; + case 226: + oItem = CreateItemOnObject("x2_it_spavscr601", oObject); + break; + case 227: + oItem = CreateItemOnObject("x1_it_spdvscr701", oObject); + break; + case 228: + oItem = CreateItemOnObject("x1_it_sparscr701", oObject); + break; + case 229: + oItem = CreateItemOnObject("nw_it_sparscr707", oObject); + break; + case 230: + oItem = CreateItemOnObject("x1_it_spdvscr702", oObject); + break; + case 231: + oItem = CreateItemOnObject("nw_it_sparscr704", oObject); + break; + case 232: + oItem = CreateItemOnObject("x1_it_spdvscr703", oObject); + break; + case 233: + oItem = CreateItemOnObject("nw_it_sparscr708", oObject); + break; + case 234: + oItem = CreateItemOnObject("nw_it_spdvscr701", oObject); + break; + case 235: + oItem = CreateItemOnObject("nw_it_sparscr705", oObject); + break; + case 236: + oItem = CreateItemOnObject("nw_it_sparscr702", oObject); + break; + case 237: + oItem = CreateItemOnObject("nw_it_sparscr706", oObject); + break; + case 238: + oItem = CreateItemOnObject("nw_it_sparscr802", oObject); + break; + case 239: + oItem = CreateItemOnObject("nw_it_spdvscr702", oObject);//ressurection + break; + case 240: + oItem = CreateItemOnObject("nw_it_sparscr701", oObject); + break; + case 241: + oItem = CreateItemOnObject("nw_it_sparscr703", oObject); + break; + case 242: + oItem = CreateItemOnObject("x2_it_spavscr701", oObject); + break; + case 243: + oItem = CreateItemOnObject("x2_it_spdvscr702", oObject); + break; + case 244: + oItem = CreateItemOnObject("x2_it_spavscr703", oObject); + break; + case 245: + oItem = CreateItemOnObject("x2_it_spdvscr701", oObject); + break; + case 246: + oItem = CreateItemOnObject("x1_it_sparscr801", oObject); + break; + case 247: + oItem = CreateItemOnObject("x1_it_spdvscr803", oObject); + break; + case 248: + oItem = CreateItemOnObject("x1_it_spdvscr804", oObject); + break; + case 249: + oItem = CreateItemOnObject("x1_it_spdvscr801", oObject); + break; + case 250: + oItem = CreateItemOnObject("x1_it_spdvscr704", oObject); + break; + case 251: + oItem = CreateItemOnObject("nw_it_sparscr803", oObject); + break; + case 252: + oItem = CreateItemOnObject("x1_it_spdvscr602", oObject); + break; + case 253: + oItem = CreateItemOnObject("nw_it_sparscr809", oObject); + break; + case 254: + oItem = CreateItemOnObject("nw_it_sparscr804", oObject); + break; + case 255: + oItem = CreateItemOnObject("nw_it_sparscr807", oObject); + break; + case 256: + oItem = CreateItemOnObject("nw_it_sparscr806", oObject); + break; + case 257: + oItem = CreateItemOnObject("nw_it_sparscr801", oObject); + break; + case 258: + oItem = CreateItemOnObject("nw_it_sparscr808", oObject); + break; + case 259: + oItem = CreateItemOnObject("nw_it_sparscr805", oObject); + break; + case 260: + oItem = CreateItemOnObject("x2_it_spdvscr804", oObject); + break; + case 261: + oItem = CreateItemOnObject("x2_it_spavscr801", oObject); + break; + case 262: + oItem = CreateItemOnObject("x2_it_spdvscr801", oObject); + break; + case 263: + oItem = CreateItemOnObject("x2_it_spdvscr802", oObject); + break; + case 264: + oItem = CreateItemOnObject("x2_it_spdvscr803", oObject); + break; + case 265: + oItem = CreateItemOnObject("x1_it_sparscr901", oObject); + break; + case 266: + oItem = CreateItemOnObject("nw_it_sparscr905", oObject); + break; + case 267: + oItem = CreateItemOnObject("nw_it_sparscr908", oObject); + break; + case 268: + oItem = CreateItemOnObject("nw_it_sparscr902", oObject); + break; + case 269: + oItem = CreateItemOnObject("nw_it_sparscr912", oObject); + break; + case 270: + oItem = CreateItemOnObject("nw_it_sparscr906", oObject); + break; + case 271: + oItem = CreateItemOnObject("nw_it_sparscr901", oObject); + break; + case 272: + oItem = CreateItemOnObject("nw_it_sparscr903", oObject); + break; + case 273: + oItem = CreateItemOnObject("nw_it_sparscr910", oObject); + break; + case 274: + oItem = CreateItemOnObject("nw_it_sparscr904", oObject); + break; + case 275: + oItem = CreateItemOnObject("nw_it_sparscr911", oObject); + break; + case 276: + oItem = CreateItemOnObject("x1_it_spdvscr901", oObject); + break; + case 277: + oItem = CreateItemOnObject("nw_it_sparscr909", oObject); + break; + case 278: + oItem = CreateItemOnObject("nw_it_sparscr907", oObject); + break; + case 279: + oItem = CreateItemOnObject("x2_it_spavscr901", oObject); + break; + case 280: + oItem = CreateItemOnObject("x2_it_spdvscr901", oObject); + break; + case 281: + oItem = CreateItemOnObject("x2_it_spdvscr902", oObject); + break; + case 282: + oItem = CreateItemOnObject("x2_it_spdvscr903", oObject); + break; + case 283: + oItem = CreateItemOnObject("x2_it_spavscr902", oObject); + break; + case 284: + oItem = CreateItemOnObject("nw_it_spdvscr301", oObject); + break; + case 285: + oItem = CreateItemOnObject("x1_it_spdvscr601", oObject); + break; + case 286: + oItem = CreateItemOnObject("x1_it_spdvscr802", oObject); + break; + case 287: + oItem = CreateItemOnObject("x1_it_spdvscr605", oObject); + break; + } + int nLevel = GetHitDice(oObject); + if(GetGoldPieceValue(oItem)>25*nLevel) + DestroyObject(oItem); + if(GetGoldPieceValue(oItem)<5*nLevel) + DestroyObject(oItem); + } +} + +//:: Test Void Main +//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_ravage.nss b/src/include/inc_ravage.nss new file mode 100644 index 0000000..857a975 --- /dev/null +++ b/src/include/inc_ravage.nss @@ -0,0 +1,40 @@ +//:://///////////////////////////////////////////// +//:: Poison System includes for Ravages +//:: inc_ravage +//:://///////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 10.01.2005 +//::////////////////////////////////////////////// + +#include "prc_alterations" + +// Calculates the amount of extra ability damage ravages cause: +// Charisma bonus, if any +// +1 if undead +// +1 if elemental +// +2 if outsider +// +2 if cleric +int GetRavageExtraDamage(object oTarget) +{ + int nRacial = MyPRCGetRacialType(oTarget); + int nExtra = GetAbilityModifier(ABILITY_CHARISMA, oTarget); + nExtra = (nExtra > 0) ? nExtra : 0; + if ( nRacial == RACIAL_TYPE_UNDEAD) nExtra++; + if ( nRacial == RACIAL_TYPE_ELEMENTAL) nExtra++; + if ( nRacial == RACIAL_TYPE_OUTSIDER) nExtra+=2; + if ( GetLevelByClass(CLASS_TYPE_CLERIC,oTarget)) nExtra+=2; + + + return nExtra; +} + +// Creates the VFX common to all ravages. +// This is used when they deal their damage +effect GetRavageVFX() +{ + //effect eReduce = EffectVisualEffect(VFX_IMP_REDUCE_ABILITY_SCORE); + effect eHoly = EffectVisualEffect(VFX_IMP_SUNSTRIKE); + //effect eHoly = EffectVisualEffect(VFX_IMP_HEAD_HOLY); + return eHoly;//EffectLinkEffects(eReduce, eHoly); +} \ No newline at end of file diff --git a/src/include/inc_rend.nss b/src/include/inc_rend.nss new file mode 100644 index 0000000..06e2b2f --- /dev/null +++ b/src/include/inc_rend.nss @@ -0,0 +1,237 @@ +//:://///////////////////////////////////////////// +//:: Rend OnHit include +//:: inc_rend +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 23.01.2005 +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +const string REND_1ST_HIT_DONE = "RendingHit1stHit"; +const string REND_DONE = "RendingHitDone"; + +const string FROST_1ST_HIT_DONE = "FrostRendHit1stHit"; +const string FROST_DONE = "FrostRendHitDone"; + +const string SPINE_1ST_HIT_DONE = "SpineRendHit1stHit"; +const string SPINE_DONE = "SpineRendHitDone"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +void DoRend(object oTarget, object oAttacker, object oWeapon); +int GetDamageFromConstant(int nIPConst); + +void DoFrostRend(object oTarget, object oAttacker, object oWeapon); + +#include "moi_inc_moifunc" + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +void DoRend(object oTarget, object oAttacker, object oWeapon) +{ + if (DEBUG) DoDebug("DoRend running"); + // Only one rend allowed per round for the sake of clearness + if(GetLocalInt(oAttacker, REND_DONE)) + return; + + if(GetLocalObject(oAttacker, REND_1ST_HIT_DONE) == oTarget) + { + if (DEBUG) DoDebug("DoRend second hit"); + // First, find the weapon base damage + int nIPConst; + itemproperty ipCheck = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ipCheck)) + { + if(GetItemPropertyType(ipCheck) == ITEM_PROPERTY_MONSTER_DAMAGE) + { + nIPConst = GetItemPropertyCostTableValue(ipCheck); + break; + } + ipCheck = GetNextItemProperty(oWeapon); + } + + int nDamage = GetDamageFromConstant(nIPConst); + int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); + nStrBon = nStrBon < 0 ? 0 : nStrBon; + nDamage += nStrBon; + if (GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oAttacker) >= 6) nDamage *= 2; + if (GetIsMeldBound(oAttacker, MELD_GIRALLON_ARMS) == CHAKRA_ARMS) nDamage *= 2; + if (GetHasSpellEffect(VESTIGE_IPOS, oAttacker) >= 6) nDamage *= 2; + + int nDamageType; + switch(GetBaseItemType(oWeapon)) + { + case BASE_ITEM_CBLUDGWEAPON: + nDamageType = DAMAGE_TYPE_BLUDGEONING; + break; + case BASE_ITEM_CPIERCWEAPON: + nDamageType = DAMAGE_TYPE_PIERCING; + break; + // Both slashing and slashing & piercing weapons do slashing damage from rend + // because it's not possible to make the damage be of both types in any + // elegant way + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + nDamageType = DAMAGE_TYPE_SLASHING; + break; + + default: + WriteTimestampedLogEntry("Unexpected weapon type in DoRend()!"); + return; + } + + // Apply damage and VFX + effect eDamage = EffectDamage(nDamage, nDamageType); + effect eLink = EffectLinkEffects(eDamage, EffectVisualEffect(VFX_COM_BLOOD_CRT_RED)); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget); + + // Tell people what happened + // * AttackerName rends TargetName * + FloatingTextStringOnCreature("* " + GetName(oAttacker) + " " + GetStringByStrRef(0x01000000 + 51197) + " " + GetName(oTarget) + " *", oAttacker, TRUE); + + // Note the rend having happened in the locals + SetLocalInt(oAttacker, REND_DONE, TRUE); + DelayCommand(4.5, DeleteLocalInt(oAttacker, REND_DONE)); + }// end if - the target had a local signifying that rend is possible + else + { + SetLocalObject(oAttacker, REND_1ST_HIT_DONE, oTarget); + if (DEBUG) DoDebug("DoRend first hit"); + } +} + +void DoFrostRend(object oTarget, object oAttacker, object oWeapon) +{ + // Only one rend allowed per round for the sake of clearness + if(GetLocalInt(oAttacker, FROST_DONE)) + return; + + float fDelay1 = 6.0 - (6.0 / GetMainHandAttacks(oAttacker)); + float fDelay2 = 6.0 - 2 * (6.0 - fDelay1); + + if(GetLocalObject(oAttacker, FROST_1ST_HIT_DONE) == oTarget) + { + // First, find the weapon base damage + int nIPConst; + itemproperty ipCheck = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ipCheck)) + { + if(GetItemPropertyType(ipCheck) == ITEM_PROPERTY_MONSTER_DAMAGE) + { + nIPConst = GetItemPropertyCostTableValue(ipCheck); + break; + } + ipCheck = GetNextItemProperty(oWeapon); + } + + + int nDamage = GetDamageFromConstant(nIPConst); + int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); + nStrBon = nStrBon < 0 ? 0 : nStrBon; + // nDamage += nStrBon; + // nDamage += FloatToInt(nStrBon/2); //1.5x Strength damage + nDamage += FloatToInt(nStrBon * 1.5); + + int nDamageType = DAMAGE_TYPE_BLUDGEONING; // It's an unarmed strike, so always bludgeoning for this + + // Apply damage and VFX + effect eDamage = EffectDamage(nDamage, nDamageType); + effect eLink = EffectLinkEffects(eDamage, EffectVisualEffect(VFX_IMP_FROST_L)); + eLink = EffectLinkEffects(eLink, EffectDamage(d6(), DAMAGE_TYPE_COLD)); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget); + + // Tell people what happened + // * AttackerName rends TargetName * + FloatingTextStringOnCreature("* " + GetName(oAttacker) + " " + GetStringByStrRef(0x01000000 + 51197) + " " + GetName(oTarget) + " *", + oAttacker, + TRUE); + + // Note the rend having happened in the locals + SetLocalInt(oAttacker, FROST_DONE, TRUE); + DelayCommand(fDelay2, DeleteLocalInt(oAttacker, FROST_DONE)); + }// end if - the target had a local signifying that rend is possible + else + { + SetLocalObject(oAttacker, FROST_1ST_HIT_DONE, oTarget); + DelayCommand(fDelay1, DeleteLocalObject(oAttacker, FROST_1ST_HIT_DONE)); + } +} + +void DoSpineRend(object oTarget, object oAttacker, object oWeapon) +{ + // Only one rend allowed per round for the sake of clearness + if(GetLocalInt(oAttacker, SPINE_DONE)) + return; + + float fDelay1 = 6.0 - (6.0 / GetMainHandAttacks(oAttacker)); + float fDelay2 = 6.0 - 2 * (6.0 - fDelay1); + + if(GetLocalObject(oAttacker, SPINE_1ST_HIT_DONE) == oTarget) + { + int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); + nStrBon = nStrBon < 0 ? 0 : nStrBon; + int nDamage = FloatToInt(nStrBon * 1.5); + + // Apply damage and VFX + effect eDamage = EffectDamage(nDamage + d6(2), DAMAGE_TYPE_PIERCING); + effect eLink = EffectLinkEffects(eDamage, EffectVisualEffect(VFX_COM_BLOOD_SPARK_MEDIUM)); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget); + + // Tell people what happened + // * AttackerName rends TargetName * + FloatingTextStringOnCreature("* " + GetName(oAttacker) + " " + GetStringByStrRef(0x01000000 + 51197) + " " + GetName(oTarget) + " *", oAttacker, TRUE); + + // Note the rend having happened in the locals + SetLocalInt(oAttacker, SPINE_DONE, TRUE); + DelayCommand(fDelay2, DeleteLocalInt(oAttacker, SPINE_DONE)); + }// end if - the target had a local signifying that rend is possible + else + { + SetLocalObject(oAttacker, SPINE_1ST_HIT_DONE, oTarget); + DelayCommand(fDelay1, DeleteLocalObject(oAttacker, SPINE_1ST_HIT_DONE)); + } +} + +int GetDamageFromConstant(int nIPConst) +{ + // First, handle the values outside the main series + switch(nIPConst) + { + case IP_CONST_MONSTERDAMAGE_1d2: return d2(1); + case IP_CONST_MONSTERDAMAGE_1d3: return d3(1); + case IP_CONST_MONSTERDAMAGE_1d4: return d4(1); + case IP_CONST_MONSTERDAMAGE_2d4: return d4(2); + case IP_CONST_MONSTERDAMAGE_3d4: return d4(3); + case IP_CONST_MONSTERDAMAGE_4d4: return d4(4); + case IP_CONST_MONSTERDAMAGE_5d4: return d4(5); + case IP_CONST_MONSTERDAMAGE_7d4: return d4(7); + } + + + int nDieNum = ((nIPConst - 8) % 10) + 1; + + switch((nIPConst - 8) / 10) + { + case 0: return d6(nDieNum); + case 1: return d8(nDieNum); + case 2: return d10(nDieNum); + case 3: return d12(nDieNum); + case 4: return d20(nDieNum); + } + + WriteTimestampedLogEntry("Unknown IP_CONST_MONSTERDAMAGE_* constant passed to GetDamageFromConstant()!"); + return 0; +} + +//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_sbr_readme.nss b/src/include/inc_sbr_readme.nss new file mode 100644 index 0000000..87e7294 --- /dev/null +++ b/src/include/inc_sbr_readme.nss @@ -0,0 +1,227 @@ +/* +Supply Based Rest System: Version 1.2 +Created by: Demetrious and OldManWhistler + +For questions or comments, or to get the latest version please visit: +http://nwvault.ign.com/Files/scripts/data/1055903555000.shtml + +**************************************************************************** +CONTENTS +**************************************************************************** + +Placeables>>Misc Interior - restful bed, restful bed (invisible object), restful cot, restful bedroll, restful campsite. + +Items>>Misc>>kits - Supply kit, Woodland kit, DM Rest Widget. + +Supply Kit weighs 30 lbs, costs 100gp and can be used by anyone to create a restful object. +Supply Kit weighs 15 lbs, costs 75gp and can only be used by rangers/druids. + +Scripts - sbr_onrest, sbr_restful_obj, sbr_onactivate, sbr_onacquire, sbr_include, logmessage + + + +**************************************************************************** +DESCRIPTION +**************************************************************************** + +This is a supply based rest system. There are three methods of resting in this system: + +1) Restful beds, cots, bedrolls - I personally place these in towns or cities and maybe rarely in dungeons where it is assumed that there are sufficient supplies to rest (ie food, water, bed). These objects will ALWAYS allow a player to rest. + +2) Supply kit resting (or woodland kit resting) - Using this item will create a campfire that players may use to rest. For roleplaying, the kit contains all supplies needed to rest and these are used while the players rest. It is removed after a time delay of several minutes. + +3) DM Controlled rest widget - DM can turn resting on/off for the entire module, a specific area, or a specific player. + +This is an attempt create a rest system closer to the resting that I remember from PnP. This system encourages players to rest as a group and forces them to ration supplies. Finally it forces the players to guard the campfire because how bad would it be if a bear destroyed the campfire before they could all rest. + +The kits are purposefully heavy. This is to encourage appropriate rationing of supplies. + +If player rest is restricted by a BW trigger or by your widget, you will receive a message stating the reason rest was restricted and the message will include the player name and area and level that restricted rest. Both the player and the DM will receive a message that indicates the reason for rest being canceled. + + + +**************************************************************************** +INSTALLATION +**************************************************************************** + +// SupplyBasedRest.erf contains the base scripts, the placeables and the items. +// sbr_acttag.erf is only needed if you use an activate item system +// where you execute the item's tag as a script +// (ie: you use X0_ONITEMACTV as your OnActivateItem script). + +// #1: Modify OnAcquiredItem module event script (create if it does not exist). +// Add the following line to somewhere in the void main function. If you do +// not have an OnAcquiredItem script then you can use 'sbr_onacquire' as your +// OnAcquiredItem script. +ExecuteScript("sbr_onacquire", OBJECT_SELF); + +// #2: Set your module OnPlayerRest script to "sbr_onrest". + +// #3: Modify OnActivateItem module event script. You can skip this step if you use an activate item script +// that executes a script with the same name as the item tag and you have imported sbr_acttag.erf. +ExecuteScript("sbr_onactivate", OBJECT_SELF); + +// #4: Add the kits to some stores and place restful objects in your inns. + +// #5: (optional) Create a journal entry that explains the rest system and give +// it to players when they enter the module. A sample journal entry is provided +// in this documentation. + +// #6: (optional) You can extend the rest system by handling the SBR_EVENT_REST +// in the module OnUserDefined event. You could easily add a wandering monster system, +// a "dream plane" or have cutscenes play when resting in specific areas. +// Read more about the OnUserDefined event at: +// http://www.reapers.org/nwn/reference/compiled/event.OnUserDefined.html +// SBR_EVENT_REST is defined in the sbr_include script. + + + +**************************************************************************** +CONFIGURATION +**************************************************************************** + +Configuration settings can be found in 'sbr_include'. + +Always recompile your module after modifying sbr_include because it is an include file. +#1: Select "Build" from the toolset menu and then choose "Build Module". +#2: Click the "Advanced Controls" box to bring up the advanced options. +#3: Make sure that the only boxes that are selected are "Compile" and "Scripts". +#4: Click the "Build" button. +#5: Remember to always make sure you are using the options you want to use when running "Build Module"! + +// The variable below sets up how the DM Rest widget is configured. +// Change this to true to only toggle rest module wide rather +// than using the 3 different level options. +// If this is TRUE clicking the ground, yourself or a player will +// toggle the Module level rest restriction. +// If FALSE, then you have 3 options. +// Target yourself = Module level toggle. +// Target ground (ie the area) = Area level toggle. +// Target player = Party level toggle. +// +// In either mode, targeting an NPC or other placeable will report +// all pertinent rest system information to you. +const int SBR_MAKE_IT_SIMPLE = FALSE; + +// This is the maximum distance the player can be from a "restful object" to +// automatically use it when they hit rest. *****BUILDERS remember that this does NOT +// account for walls so be careful at inns with bed placement****** +const float SBR_DISTANCE = 5.0; + +// This is the event number that will be signalled to your module OnUserDefined Event +// when a player rests. This user defined event is how you should extend the system +// so that specific things happen on rest. IE: create some wandering monsters, +// play a cutscene, teleport them to a "dream" area, etc. +const int SBR_EVENT_REST = 2000; + + + +**************************************************************************** +PLAYER DOCUMENTATION +**************************************************************************** + +This module uses Supply Based Rest. + +In order to rest you must be near a "Restful Object". These objects have "Restful" in their name: a Restful Bed or a Restful Campsite, for example. You can use a restful object by clicking on it or by pressing the rest button while you are near it. + +If you are not near a restful object then you must use a supply kit to build one. Using a supply kit will create a temporary Restful Campsite that will last several minutes. There are two ways to use a supply kit. You can either right click on the kit and activate the item, or you can hit the rest button (or press 'r') twice in rapid succession. + +There are two kinds of supply kits: regular kits that anyone can use and woodland kits that are lighter/cheaper but can only be carried by rangers or druids. You cannot carry a woodland kit unless you are a ranger or druid. + +Supply kits are quite heavy and quite expensive. This is to encourage players to rest as a group and to encourage players to ration the how often they rest. + +Sometimes you will encounter areas that are not secure for rest. This means you must find a safe area (an enclosed room for example) before you can rest. + + + +**************************************************************************** +BUILDER NOTES +**************************************************************************** +This version will allow you to use the new BioWare rest restriction triggers "on top of" these restrictions. For example, the rules above will still apply, but you can go the extra step and make players close the door to the room (like the BioWare trigger) just by laying down the standard trigger - all code is included and integrated into the system. For discussion on this new trigger - see bottom of document. + +LogMessage is used for displaying text to players and DMs. It is scripting package designed by OldManWhistler. You can configure who receives feedback to almost anything imaginable. See the actual script for details and see the sbr_include file to see the multitude of options available to you. + + + +**************************************************************************** +DM FUNCTIONS +**************************************************************************** + +1) DM Rest Widget. Allows you to have total control over rest in your module. There are 2 modes: standard and "MAKEITSIMPLE". In standard mode, if you target your avatar it will toggle rest enable/disable on a module level. Targeting the ground will toggle rest enable/disable on an area level and targeting a player will toggle rest enable/disable on a party level. In "MAKEITSIMPLE" mode, targeting the ground, yourself or a player will toggle the module level rest restriction. Finally, targeting an NPC or placeable will report the rest settings just like clicking a restful bed (see below). + +Toggling the module level rest will NOT remove area, or party rest restrictions you set with the widget. The same goes for area and party restrictions. They are all INDEPENDENT and ALL must be OFF to allow the player to rest. You will receive a message why they can't but you could have 3 separate settings to clear if in standard mode. I included the feature with extra flexibility due to the huge varieties of server set-ups using NWN. For single party, classic DM adventure - "MAKEITSIMPLE" is the easiest. + +To change modes: Open the sbr_include file and change the "MAKEITSIMPLE" variable to either TRUE or FALSE. Default is FALSE. BUILD YOUR MODULE IF YOU CHANGE ANY INCLUDE FILE - at least compile all scripts using the build function. + +2) Rest settings report. The report has a lot of pertinent information to you to help you keep track of exactly what is the status of the rest system. You will receive the following information if you use the DM Rest Widget on an NPC/placeable OR if you click on any restful object, : + +- Module Rest Setting +- Area Rest Setting +- BW trigger information: is there a Bioware rest trigger in the current area. + +The following information is reported based on the nearest player. NOTE: It uses GetNearestCreature function and therefore will return "No valid player found" if the "nearest" player is in a different area transition. This is a good feature for PW or other situations with multiple parties. + +- Player Rest Setting +- Number of party kits (both woodland and supply kit information). + + + +**************************************************************************** +QUESTIONS AND COMMENTS +**************************************************************************** + +Q: What is the difference between the supply kit and the woodland kit? + +A: The woodland kit can be used by rangers and druids. It attempts to simulate the fact that these classes can find many resources from the land. Therefore, woodland kits are significantly lighter as these classes will supplement the kit with things from the woods. The woodlands kits are medium size item and the supply kits are a large item. Woodland kits are a little bit cheaper too. + +NOTE: If you are NOT a druid or a ranger, the OnAcquire code will drop any woodland kit you attempt to hold or purchase. This is to avoid other players acting as pack mules for the druid or ranger so that they can use the cheaper kits. Not exactly "realistic" but forces them to play fair. + +If you don't like this added feature of the woodland kit - don't add any to stores and then you do NOT need the sbr_onacquire script. + + +Q: What do the kits cost? + +A: 100 for supply kit and 75 for woodland kit. This is a pretty hefty price tag for a brand new level 1 adventures and therefore you may need to adjust the starting money value (or just give out a few supply kits for "free" when you begin the adventure). Another option would be to make the players rest in a town or inn until they gain enough money to begin traveling and camping out in the wilderness. + + +Q: How does this affect game play balance? + +A: The number of kits the players carry ARE the only limit to resting so be careful with not only how much money the players have, but use caution with store inventories and with magical bag or bags of holding. The kits are large so that only 3 supply kits or 6 woodland kits can fit into a bag of holding but as a designer, it may be important to further ration the kits (The are "rationed" in a sense of the weight and price tag). I initially had the weight at 50lb for the supply kit but decided that this was too heavy for non-fighter based parties and so I backed the weight down. You can adjust the weight or price by editing the price in the toolset and then restocking any stores that you have created. + + +**BW Rest Trigger Commentary and Answers** +Q: What is the deal with the new BW rest triggers? + +First off, much of this is opinion, much is fact. They are not simple and they are not easy to integrate into a module. They are poorly documented and the documentation contains errors. In the official campaign there are 2 checks that are performed to rest. One is area specific and one looks for this trigger. There are subroutines for both of these checks. If you do NOT have rest script - the trigger does nothing despite what the comment says. You should also NOT have the area level "No rest" box checked like it says. All this being said you can use the idea but it takes custom work on your end as a builder. +How do they work in this system? Closer to what I expected :) . In this system, if you lay down a BW rest trigger that ENTIRE AREA becomes a region where the players MUST find a secure region. It will NOT affect other areas (this is a coding change I made to the trigger subroutine - by default one BW trigger negated resting throughout the entire module without adding more code other places). That is it. The trigger must contain one door. You could still use the "no rest" box in the area properties to disallow rest everywhere in the area at all times. +I hated to have to include this long explanation but thought it would save me time and effort in the long run of answering a lot of questions related to this system. + + + +**************************************************************************** +CONCLUSION +**************************************************************************** + +As a builder, I find the system easy to install and very flexible. I think it is straightforward for players and functions to limit rest in a way that allows the players to decide when, where, and how they want it to happen while the final decision to "how many times" is left to the builder when they place the objects and supplies. + +Special thanks to Mogney, Dick Nervous, and JohhnyB for play testing, feedback, and fine tuning of the concept. + +Demetrious + + + +**************************************************************************** +CHANGELOG +**************************************************************************** + +v1.1 to v1.2 +- Cosmetic module changes: made the signs, door and merchants plot so they couldn't be destroyed and screw up the tutorial. +- Revamped the documentation and installation instructions. +- Changed "IsDM" checks to check for DMPossessed creatures. The system will treat DM possessed creatures the same as DMs. +- Added a module OnUserDefined event for players resting so that you can easily extend the rest system without touching the code. +- Changed interface so that players have to hit 'r' or the rest button twice to enable the "automatically use supply" feature. In live play some players would accidently hit R because they forgot to press enter before they started typing. This solves that issue. +- Hitting the rest button when you are near a "restful" object will automatically use that restful object instead of a kit. In live play some players who were not used to the system would try a normal rest and accidently use another kit. This solves that issue. +- Changed it so that woodland kits are not destroyed when picked up by non-ranger/druids. Now they will be dropped. In live play some players would accidently destroy woodland kits by picking them up. This solves that issue. +- Modified the store conversations to reflect the changes. +- Version 1.1 was tagged as requiring SoU. Version 1.2 can be used by anyone -- regardless of whether they have SoU. +*/ diff --git a/src/include/inc_set.nss b/src/include/inc_set.nss new file mode 100644 index 0000000..28664fd --- /dev/null +++ b/src/include/inc_set.nss @@ -0,0 +1,667 @@ +//:://///////////////////////////////////////////// +//:: Set-like storage data type include +//:: inc_set +//::////////////////////////////////////////////// +/** @file + This file defines a "data type" that behaves + like a set. It is implemented as an extension + of the arrays defined in prc_inc_array. + + Operations: + - Get number of entities in the set. O(1) + - Store an entity in the set. O(1) + - Remove an entity from the set. O(n) + - Determine if the set contains a given entity. O(1) + - Get operations on the underlying array + + The memory complexity is O(n), specifically 2n(m + c), where + n is the number of entities stored in the set, m is the memory + taken up the local variable store of a member entity and c is + a constant overhead from disambiguation strings. + + Note that a set can contain only contain one instance + of any specific entity. Any attempts to add an entity to + a set that is already a member of will return with success, + but do nothing. + For example, if a set contains {"Foo", "Bar, 123, OBJECT_INVALID} + calling set_add_string(container, setname, "Foo") will return + SDL_SUCCESS, but not modify the contents of the set. + + + @author Ornedan + @date Created - 2006.09.16 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Creates a new set on the given storage object. If a set with + * the same name already exists, the function fails. + * + * @param store The object to use as holder for the set + * @param name The name of the set + * @return SDL_SUCCESS if the set was successfully created, + * one of SDL_ERROR_* on error. + */ +int set_create(object store, string name); + +/** + * Deletes a set, deleting all local variables it consists of. + * + * @param store The object which holds the set to delete + * @param name The name of the set + * @return SDL_SUCCESS if the set was successfully deleted, + * one of SDL_ERROR_* on error + */ +int set_delete(object store, string name); + +/** + * Adds a string to the set. + * + * @param store The object holding the set + * @param name The name of the set + * @param entry The string to add + * @return SDL_SUCCESS if the addition was successfull, SDL_ERROR_* on error. + */ +int set_add_string(object store, string name, string entry); + +/** + * Adds a integer to the set. + * + * @param store The object holding the set + * @param name The name of the set + * @param entry The integer to add + * @return SDL_SUCCESS if the addition was successfull, SDL_ERROR_* on error. + */ +int set_add_int(object store, string name, int entry); + +/** + * Adds a float to the set. + * + * @param store The object holding the set + * @param name The name of the set + * @param entry The float to add + * @return SDL_SUCCESS if the addition was successfull, SDL_ERROR_* on error. + */ +int set_add_float(object store, string name, float entry); + +/** + * Adds a object to the set. + * + * @param store The object holding the set + * @param name The name of the set + * @param entry The object to add + * @return SDL_SUCCESS if the addition was successfull, SDL_ERROR_* on error. + */ +int set_add_object(object store, string name, object entry); + +/** + * Determines whether the set contains the given string. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The string to test + * @return TRUE if the set contains entry, FALSE otherwise + */ +int set_contains_string(object store, string name, string entity); + +/** + * Determines whether the set contains the given integer. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The integer to test + * @return TRUE if the set contains entry, FALSE otherwise + */ +int set_contains_int(object store, string name, int entity); + +/** + * Determines whether the set contains the given float. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The float to test + * @return TRUE if the set contains entry, FALSE otherwise + */ +int set_contains_float(object store, string name, float entity); + +/** + * Determines whether the set contains the given object. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The object to test + * @return TRUE if the set contains entry, FALSE otherwise + */ +int set_contains_object(object store, string name, object entity); + +/** + * Removes the given string from the set, if it is a member. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The string to remove + */ +void set_remove_string(object store, string name, string entity); + +/** + * Removes the given integer from the set, if it is a member. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The integer to remove + */ +void set_remove_int(object store, string name, int entity); + +/** + * Removes the given float from the set, if it is a member. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The float to remove + */ +void set_remove_float(object store, string name, float entity); + +/** + * Removes the given object from the set, if it is a member. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The object to remove + */ +void set_remove_object(object store, string name, object entity); + +/** + * Gets the type of the i:th member of the set. + * + * @param store The object holding the set + * @param name The name of the set + * @param i The index of the member the type of which to retrieve + * @return One of the ENTITY_TYPE_* defined in inc_heap, or 0 in case of error + */ +int set_get_member_type(object store, string name, int i); + + +/** + * Gets the i:th member of the set as a string. + * + * NOTE: If the member is actually not a string, the return + * value in undefined. As such, always check the real + * type of the member first using set_get_member_type(). + * + * @param store The object holding the set + * @param name The name of the set + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * "" on error + */ +string set_get_string(object store, string name, int i); + +/** + * Gets the i:th member of the set as an integer. + * + * NOTE: If the member is actually not an integer, the return + * value in undefined. As such, always check the real + * type of the member first using set_get_member_type(). + * + * @param store The object holding the set + * @param name The name of the set + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * 0 on error + */ +int set_get_int(object store, string name, int i); + +/** + * Gets the i:th member of the set as an float. + * + * NOTE: If the member is actually not an float, the return + * value in undefined. As such, always check the real + * type of the member first using set_get_member_type(). + * + * @param store The object holding the set + * @param name The name of the set + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * 0.0 on error + */ +float set_get_float(object store, string name, int i); + +/** + * Gets the i:th member of the set as an object. + * + * NOTE: If the member is actually not an object, the return + * value in undefined. As such, always check the real + * type of the member first using set_get_member_type(). + * + * @param store The object holding the set + * @param name The name of the set + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * OBJECT_INVALID on error + */ +object set_get_object(object store, string name, int i); + +/** + * Gets the number of members in the set + * + * @param store The object holding the set + * @param name The name of the set + * @return The size of the set, or -1 if the specified + * set does not exist. + */ +int set_get_size(object store, string name); + +/** + * Checks whether the given set exists. + * + * @param store The object holding the set + * @param name The name of the set + * @return TRUE if the set exists, FALSE otherwise. + */ +int set_exists(object store, string name); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +// prc_inc_array access is via inc_heap +//#include "prc_inc_array" +#include "inc_debug" +#include "inc_heap" + + +////////////////////////////////////////////////// +/* Internal Constants */ +////////////////////////////////////////////////// + +const string _PRC_SET_PREFIX = "@@@set"; + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Performs the real addition operation. + * + * @param store The object holding the set + * @param name The name of the set + * @param entry String form of the entity to be stored in the set + * @param isobject Whether the entity being stored is an object. Objects need special handling + * @param obj The object being stored, if any + */ +int _inc_set_set_add_aux(object store, string name, string entry, int isobject = FALSE, object obj = OBJECT_INVALID) +{ + // Sanity checks + if(!set_exists(store, name)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Set the presence marker to be the index where the entity will be stored + 1 to avoid storing a 0 + int index = array_get_size(store, name); + SetLocalInt(store, name + entry, index + 1); + + // Store the member's value in the array + if(isobject) + { + if(DEBUG) + { + Assert(array_set_object(store, name, index, obj) == SDL_SUCCESS, + "array_set_object(store, name, index, obj) == SDL_SUCCESS", + "", "inc_set", "_inc_set_set_add_aux" + ); + return SDL_SUCCESS; + } + else + { + return array_set_object(store, name, index, obj); + } + } + else + { + if(DEBUG) + { + Assert(array_set_string(store, name, index, entry) == SDL_SUCCESS, + "array_set_string(store, name, index, entry) == SDL_SUCCESS", + "", "inc_set", "_inc_set_set_add_aux" + ); + return SDL_SUCCESS; + } + else + { + return array_set_string(store, name, index, entry); + } + } +} + +/** Internal function. + * Determines whether the set contains the given entity. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The presence string for the entity to test + * @return TRUE if the set contains the entity, FALSE otherwise + */ +int _inc_set_set_contains_aux(object store, string name, string entity) +{ + // Sanity check + if(!set_exists(store, name)) + return FALSE; + + // Get the value of the presence marker and normalise to TRUE / FALSE + return GetLocalInt(store, _PRC_SET_PREFIX + name + entity) != 0; +} + +/** Internal function. + * Removes the given entity from the set. + * + * @param store The object holding the set + * @param name The name of the set + * @param entity The presence string for the entity to remove + */ +void _inc_set_set_remove_aux(object store, string name, string entity) +{ + // Set does not exist or exists, but does not contain the given entity. Nothing to do + if(!_inc_set_set_contains_aux(store, name, entity)) + return; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Get the index where the entity is stored and the size of the underlying array + int index = GetLocalInt(store, name + entity) - 1; + int size = array_get_size(store, name); + string raw; + object obj; + + // Move each element in the array after the one being removed back by one + for(index = index + 1; index < size; index++) + { + // Determine what's stored here + raw = array_get_string(store, name, index); + + // Different handling for objects vs everything else + if(raw == "OBJECT") + { + obj = array_get_object(store, name, index); + // Move back + array_set_object(store, name, index - 1, obj); + + // Update the marker value + SetLocalInt(store, name + "O" + ObjectToString(obj), index/* - 1 + 1 */); + } + else + { + // Move back + array_set_string(store, name, index - 1, raw); + + // Update the marker value + SetLocalInt(store, name + raw, index/* - 1 + 1 */); + } + } + + // Delete the marker local + DeleteLocalInt(store, name + entity); + + // Shrink the array + array_shrink(store, name, size - 1); +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int set_create(object store, string name) +{ + // Sanity checks + if(!GetIsObjectValid(store)) + return SDL_ERROR_NOT_VALID_OBJECT; + if(name == "") + return SDL_ERROR_INVALID_PARAMETER; + if(set_exists(store, name)) + return SDL_ERROR_ALREADY_EXISTS; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // All OK, create the underlying array + return array_create(store, name); +} + +int set_delete(object store, string name) +{ + // Sanity check + if(!set_exists(store, name)) + return SDL_ERROR_DOES_NOT_EXIST; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Loop over all members, getting deleting the presence indicator local + int size = array_get_size(store, name); + int i; + string raw, member; + for(i = 0; i < size; i++) + { + // Get the raw data stored in the array. + // NOTE: This relies on internal details of the array implementation. Specifically, objects having a string containing "OBJECT" + // stored in their position + raw = array_get_string(store, name, i); + + // Construct the presence marker name. Special handling for objects + if(raw == "OBJECT") + member = name + "O" + ObjectToString(array_get_object(store, name, i)); + else + member = name + raw; + + // Delete the marker + DeleteLocalInt(store, member); + } + + // Clean up the underlying array + if(DEBUG) + { + Assert(array_delete(store, name) == SDL_SUCCESS, "array_delete(store, name) == SDL_SUCCESS", "", "inc_set", "set_delete"); + return SDL_SUCCESS; + } + else + { + return array_delete(store, name); + } +} + +int set_add_string(object store, string name, string entry) +{ + // Convert the entry to the storable string form + entry = "S" + entry; + + // If the set already contains the entry being added, nothing happens + if(_inc_set_set_contains_aux(store, name, entry)) + return SDL_SUCCESS; + + return _inc_set_set_add_aux(store, name, entry); +} + +int set_add_int(object store, string name, int entry) +{ + // Convert the entry to the storable string form + string strentry = "I" + IntToString(entry); + + // If the set already contains the entry being added, nothing happens + if(_inc_set_set_contains_aux(store, name, strentry)) + return SDL_SUCCESS; + + return _inc_set_set_add_aux(store, name, strentry); +} + +int set_add_float(object store, string name, float entry) +{ + // Convert the entry to the storable string form + string strentry = "F" + FloatToString(entry); + + // If the set already contains the entry being added, nothing happens + if(_inc_set_set_contains_aux(store, name, strentry)) + return SDL_SUCCESS; + + return _inc_set_set_add_aux(store, name, strentry); +} + +int set_add_object(object store, string name, object entry) +{ + // Convert the entry to the storable string form + string strentry = "O" + ObjectToString(entry); + + // If the set already contains the entry being added, nothing happens + if(_inc_set_set_contains_aux(store, name, strentry)) + return SDL_SUCCESS; + + return _inc_set_set_add_aux(store, name, strentry, TRUE, entry); +} + +int set_get_member_type(object store, string name, int i) +{ + // Sanity check + if(!set_exists(store, name) || i >= set_get_size(store, name)) + return 0; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Grab the first character of the raw string, it determines type + string type = GetSubString(array_get_string(store, name, i), 0, 1); + + // First character in the string determines type + if (type == "F") + return ENTITY_TYPE_FLOAT; + else if(type == "I") + return ENTITY_TYPE_INTEGER; + else if(type == "O") + return ENTITY_TYPE_OBJECT; + else if(type == "S") + return ENTITY_TYPE_STRING; + else + return 0; +} + +int set_contains_string(object store, string name, string entity) +{ + return _inc_set_set_contains_aux(store, name, "S" + entity); +} + +int set_contains_int(object store, string name, int entity) +{ + return _inc_set_set_contains_aux(store, name, "I" + IntToString(entity)); +} + +int set_contains_float(object store, string name, float entity) +{ + return _inc_set_set_contains_aux(store, name, "F" + FloatToString(entity)); +} + +int set_contains_object(object store, string name, object entity) +{ + return _inc_set_set_contains_aux(store, name, "O" + ObjectToString(entity)); +} + +void set_remove_string(object store, string name, string entity) +{ + _inc_set_set_remove_aux(store, name, "S" + entity); +} + +void set_remove_int(object store, string name, int entity) +{ + _inc_set_set_remove_aux(store, name, "I" + IntToString(entity)); +} + +void set_remove_float(object store, string name, float entity) +{ + _inc_set_set_remove_aux(store, name, "F" + FloatToString(entity)); +} + +void set_remove_object(object store, string name, object entity) +{ + _inc_set_set_remove_aux(store, name, "O" + ObjectToString(entity)); +} + +string set_get_string(object store, string name, int i) +{ + // Sanity check + if(!set_exists(store, name) || i >= set_get_size(store, name)) + return ""; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Chop off the first character from the raw string and return the rest + string raw = array_get_string(store, name, i); + return GetSubString(raw, 1, GetStringLength(raw)); +} + +int set_get_int(object store, string name, int i) +{ + // Sanity check + if(!set_exists(store, name) || i >= set_get_size(store, name)) + return 0; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Chop off the first character from the raw string and return the rest + string raw = array_get_string(store, name, i); + return StringToInt(GetSubString(raw, 1, GetStringLength(raw))); +} + +float set_get_float(object store, string name, int i) +{ + // Sanity check + if(!set_exists(store, name) || i >= set_get_size(store, name)) + return 0.0f; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + // Chop off the first character from the raw string and return the rest + string raw = array_get_string(store, name, i); + return StringToFloat(GetSubString(raw, 1, GetStringLength(raw))); +} + +object set_get_object(object store, string name, int i) +{ + // Sanity check + if(!set_exists(store, name) || i >= set_get_size(store, name)) + return OBJECT_INVALID; + + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + return array_get_object(store, name, i); +} + +int set_get_size(object store, string name) +{ + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + return array_get_size(store, name); +} + +int set_exists(object store, string name) +{ + // Generate real name for accessing array functions + name = _PRC_SET_PREFIX + name; + + return array_exists(store, name); +} + + +// Test main +//void main() {} diff --git a/src/include/inc_sp_gain_mem.nss b/src/include/inc_sp_gain_mem.nss new file mode 100644 index 0000000..d994896 --- /dev/null +++ b/src/include/inc_sp_gain_mem.nss @@ -0,0 +1,366 @@ +//::////////////////////////////////////////////// +//:: Name: new spellbook spellgain / spell memorization include +//:: File: inc_sp_gain_mem.nss +//::////////////////////////////////////////////// +/** +contains helper functions for the two dynamic conversation scripts +- prc_s_spellb.nss +- prc_s_spellgain.nss +that handle learning / gaining new spells at level up (prc_s_spellgain) +or preparing what spells to learn at next rest (prc_s_spellb) + +Author: motu99 +Created: May 1, 2008 +*/ + +//#include "prc_inc_core" //granted access via parent (inc_newspellbook) + +//:: Updated for .35 by Jaysyn 2023/03/11 + +//:: Test Void +//void main (){} + +//::////////////////////////////////////////////// +//:: Constants +//::////////////////////////////////////////////// + +// max. number of classes a PC (or creature) can take (8 for NWN, 4 for NWN2) +const int MAX_CLASSES = 8; + +////////////////////////////////////////////////// +/* Aid functions */ +////////////////////////////////////////////////// + +string GetNSBDefinitionFileName(int nClass); +int GetCasterLevelByClass(int nClass, object oPC); + +int GetSpellsKnown_MaxCount(int nCasterLevel, int nClass, int nSpellLevel, object oPC); +int GetSpellsInClassSpellbook_Count(int nClass, int nSpellLevel); +string GetClassString(int nClass); +int GetMaxSpellLevelForCasterLevel(int nClass, int nCasterLevel); +int GetMinSpellLevelForCasterLevel(int nClass, int nCasterLevel); + +void WipeSpellFromHide(int nIPFeatID, object oPC); + +string GetSpellsKnown_Array(int nClass, int nSpellLevel = -1); +object GetSpellsOfClass_Token(int nClass, int nSpellLevel); +string GetSpellsOfClass_Array(); +string GetSpellsMemorized_Array(int nClass); +string GetSpellsToBeMemorized_Array(int nClass, int nSpellSlotLevel); + +void array_set_size(object oPC, string sArrayName, int nSize); +int array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0); +int array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0); +int persistant_array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0); +int persistant_array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0); +int array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0); +int array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0); +int persistant_array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0); +int persistant_array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0); + +string GetMetaMagicString_Short(int nMetaMagic); +string GetMetaMagicString(int nMetaMagic); +int GetMetaMagicFromFeat(int nFeat); +int GetMetaMagicOfCaster(object oPC = OBJECT_SELF); + +// name of the new spellbook file (cls_spell_*) +string GetNSBDefinitionFileName(int nClass) +{ + return GetFileForClass(nClass); +} + +// gets the caster level (without special modifications due to feats), by which the max spell slot level is determined +int GetCasterLevelByClass(int nClass, object oPC) +{ + return GetSpellslotLevel(nClass, oPC); + // return GetPrCAdjustedCasterLevel(nClass, oPC, TRUE); +} + +// gets the maximum nr of spells that oPC can know with a given nCasterLevel, nClass and nSpellLevel +int GetSpellsKnown_MaxCount(int nCasterLevel, int nClass, int nSpellLevel, object oPC) +{ + return GetSpellKnownMaxCount(nCasterLevel, nSpellLevel, nClass, oPC); +} + + +// gets the total nr of spells available at nSpellLevel for nClass +int GetSpellsInClassSpellbook_Count(int nClass, int nSpellLevel) +{ + return persistant_array_get_size(GetSpellsOfClass_Token(nClass, nSpellLevel), GetSpellsOfClass_Array()); +} + +string GetClassString(int nClass) +{ + // get the name of the feats table 2da + string sClass = Get2DACache("classes", "FeatsTable", nClass); + // truncate the first 8 characters (the "cls_feat" part), leaving the "_" part + sClass = GetStringRight(sClass, GetStringLength(sClass) - 8); + return sClass; +} + +// gets the maximum spell level that nClass can cast at nCasterLevel +int GetMaxSpellLevelForCasterLevel(int nClass, int nCasterLevel) +{ + string sFile; + // Bioware casters use their classes.2da-specified tables + //if(GetIsBioSpellCastClass(nClass)) + //{ + sFile = Get2DACache("classes", "SpellGainTable", nClass); + //} + //else + //{ + // sFile = "cls_spbk" + GetClassString(nClass); + //} + + // row nr in the files is nCasterLevel minus 1 + nCasterLevel--; + int nSpellLevel; + + if (Get2DACache(sFile, "NumSpellLevels", 9) != "") + { + string sTemp = Get2DACache(sFile, "NumSpellLevels", nCasterLevel); + if (sTemp != "") + { + nSpellLevel = StringToInt(sTemp)-1; + if (nSpellLevel <= 0) nSpellLevel = 0; + } + } + else + { + for (nSpellLevel=9; nSpellLevel >= 0; nSpellLevel--) + { + string sTemp = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nCasterLevel); + if (sTemp != "") + { + break; + } + } + } + return nSpellLevel; +} + +// gets the minimum spell level that nClass can cast at nCasterLevel +int GetMinSpellLevelForCasterLevel(int nClass, int nCasterLevel) +{ + string sFile; + // Bioware casters use their classes.2da-specified tables + //if(GetIsBioSpellCastClass(nClass)) + //{ + sFile = Get2DACache("classes", "SpellGainTable", nClass); + //} + //else + //{ + // sFile = "cls_spbk" + GetClassString(nClass); + //} + + // row nr in the files is nCasterLevel minus 1 + nCasterLevel--; + + int bFound = 0; + + int nSpellLevel; + for (nSpellLevel=0; nSpellLevel <= 9; nSpellLevel++) + { + string sTemp = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nCasterLevel); + if (sTemp != "") + { + bFound = TRUE; + break; + } + } + + if (!bFound) nSpellLevel = -1; + return nSpellLevel; +} + +// wipes the IPbonusfeat from the hide +void WipeSpellFromHide(int nIPFeatID, object oPC) +{ + // go through all item properties on the hide + object oHide = GetPCSkin(oPC); + itemproperty ipTest = GetFirstItemProperty(oHide); + while(GetIsItemPropertyValid(ipTest)) + { + // is it a bonus feat? + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT) + { + // get the row nr of the bonus feat in iprp_feat.2da + // is it the ipfeat to delete? + if (GetItemPropertySubType(ipTest) == nIPFeatID) + { + RemoveItemProperty(oHide, ipTest); + if(DEBUG) DoDebug("WipeSpellFromHide: Removing item property " + DebugIProp2Str(ipTest)); + } + } + ipTest = GetNextItemProperty(oHide); + } +} + +// one array for each class (array holds all spell levels, but only non-metamagicked masterspells) +string GetSpellsKnown_Array(int nClass, int nSpellLevel = -1) +{ + int nSpellbookType = GetSpellbookTypeForClass(nClass); + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + return "Spellbook_Known_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); + return "Spellbook" + IntToString(nClass); +} + +// class spellbook (one storage token for each spell level and class) +object GetSpellsOfClass_Token(int nClass, int nSpellLevel) +{ + return GetObjectByTag("SpellLvl_" + IntToString(nClass) + "_Level_" + IntToString(nSpellLevel)); +} + +string GetSpellsOfClass_Array() +{ + return "Lkup"; +} + +string GetSpellsMemorized_Array(int nClass) +{ + return "NewSpellbookMem_" + IntToString(nClass); +} + +string GetSpellsToBeMemorized_Array(int nClass, int nSpellSlotLevel) +{ + return "Spellbook" + IntToString(nSpellSlotLevel) + "_" + IntToString(nClass); +} + + +void array_set_size(object oPC, string sArrayName, int nSize) +{ + SetPersistantLocalInt(oPC, sArrayName, nSize+1); +} + +int array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0) +{ + // get array size, if size not already supplied + if (nSize == 0) nSize = GetPersistantLocalInt(oPC, sArrayName) -1; + int i; + + for (i = nFirst; i < nSize; i++) + { + if (sValue == GetPersistantLocalString(oPC, sArrayName + "_" + IntToString(i))) + return i; + } + return -1; +} + +int array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0) +{ + // array values are stored as strings, so convert nValue to a string + return array_has_string(oPC, sArrayName, IntToString(nValue), nFirst, nSize); +} + +int persistant_array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0) +{ + return array_has_string(oPC, sArrayName, sValue, nFirst, nSize); +} + +int persistant_array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0) +{ + return array_has_string(oPC, sArrayName, IntToString(nValue), nFirst, nSize); +} + +int array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0) +{ + // get array size + int nSize = GetPersistantLocalInt(oPC, sArrayName)-1; + if (nSize <= nFirst) return -1; + + // position of the first found; -1 if not found + int nPos = array_has_string(oPC, sArrayName, sValue, nFirst, nSize); + if (nPos < 0) return -1; + + // Is is not the last element? + if (nPos < nSize-1) + { + // then swap nPos (or rather nPos-1) with the last element (nSize-1) + string sTemp = GetPersistantLocalString(oPC, sArrayName + "_" + IntToString(nSize-1)); + SetPersistantLocalString(oPC, sArrayName + "_" + IntToString(nPos), sTemp); + } + + // now decrement the array size (note that we already subtracted one in the beginning) + SetPersistantLocalInt(oPC, sArrayName, nSize); + + return nPos; +} + +// extracts the integer value nValue from a persistant sArray on oPC +// extracts the first instance of nValue that it finds +// extracts it by swapping the last array element to the position of the extracted element and reducing the array size by one +// returns the position where the extracted element was found +int array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0) +{ + // array values are stored as strings, so convert nValue to a string + return array_extract_string(oPC, sArrayName, IntToString(nValue), nFirst); +} + +int persistant_array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0) +{ + return array_extract_string(oPC, sArrayName, sValue, nFirst); +} + +int persistant_array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0) +{ + // array values are stored as strings, so convert nValue to a string + return array_extract_string(oPC, sArrayName, IntToString(nValue), nFirst); +} + +string GetMetaMagicString_Short(int nMetaMagic) +{ + string s; + if (nMetaMagic == 0) return s; + + if (nMetaMagic & METAMAGIC_EXTEND) s += "ext "; + if (nMetaMagic & METAMAGIC_SILENT) s += "sil "; + if (nMetaMagic & METAMAGIC_STILL) s += "sti "; + if (nMetaMagic & METAMAGIC_EMPOWER) s += "emp "; + if (nMetaMagic & METAMAGIC_MAXIMIZE) s += "max "; + if (nMetaMagic & METAMAGIC_QUICKEN) s += "qui "; + + return GetStringLeft(s, GetStringLength(s)-1); +} + +string GetMetaMagicString(int nMetaMagic) +{ + string s; + if (nMetaMagic == 0) return s; + + if (nMetaMagic & METAMAGIC_EXTEND) s += "extend "; + if (nMetaMagic & METAMAGIC_SILENT) s += "silent "; + if (nMetaMagic & METAMAGIC_STILL) s += "still "; + if (nMetaMagic & METAMAGIC_EMPOWER) s += "empower "; + if (nMetaMagic & METAMAGIC_MAXIMIZE) s += "maximize "; + if (nMetaMagic & METAMAGIC_QUICKEN) s += "quicken "; + + return GetStringLeft(s, GetStringLength(s)-1); +} + +int GetMetaMagicFromFeat(int nFeat) +{ + switch(nFeat) + { + case FEAT_EMPOWER_SPELL: return METAMAGIC_EMPOWER; + case FEAT_EXTEND_SPELL: return METAMAGIC_EXTEND; + case FEAT_MAXIMIZE_SPELL: return METAMAGIC_MAXIMIZE; + case FEAT_QUICKEN_SPELL: return METAMAGIC_QUICKEN; + case FEAT_SILENCE_SPELL: return METAMAGIC_SILENT; + case FEAT_STILL_SPELL: return METAMAGIC_STILL; + } + return METAMAGIC_NONE; +} + +int GetMetaMagicOfCaster(object oPC = OBJECT_SELF) +{ + int nMetaMagic; + + if (GetHasFeat(FEAT_EMPOWER_SPELL, oPC)) nMetaMagic |= METAMAGIC_EMPOWER; + if (GetHasFeat(FEAT_EXTEND_SPELL, oPC)) nMetaMagic |= METAMAGIC_EXTEND; + if (GetHasFeat(FEAT_MAXIMIZE_SPELL, oPC)) nMetaMagic |= METAMAGIC_MAXIMIZE; + if (GetHasFeat(FEAT_QUICKEN_SPELL, oPC)) nMetaMagic |= METAMAGIC_QUICKEN; + if (GetHasFeat(FEAT_SILENCE_SPELL, oPC)) nMetaMagic |= METAMAGIC_SILENT; + if (GetHasFeat(FEAT_STILL_SPELL, oPC)) nMetaMagic |= METAMAGIC_STILL; + + return nMetaMagic; +} \ No newline at end of file diff --git a/src/include/inc_spirit_weapn.nss b/src/include/inc_spirit_weapn.nss new file mode 100644 index 0000000..a0f726a --- /dev/null +++ b/src/include/inc_spirit_weapn.nss @@ -0,0 +1,1108 @@ +//:://///////////////////////////////////////////// +//:: [Spiritual Weapon] +//:: [inc_spirit_weapn.nss] +//:: [Jaysyn 2024-08-23 07:58:14] +//:: +//:: Include script for Spiritual Weapon +//:: +//:://///////////////////////////////////////////// +/**@ Spiritual Weapon +(Player's Handbook v.3.5, p. 283) + +Evocation [Force] +Level: Cleric 2, Knight of the Chalice 2, War 2, Mysticism 2, +Components: V, S, DF, +Casting Time: 1 standard action +Range: Medium (100 ft. + 10 ft./level) +Effect: Magic weapon of force +Duration: 1 round/level (D) +Saving Throw: None +Spell Resistance: Yes + +A weapon made of pure force springs into existence and attacks +opponents at a distance, as you direct it, dealing 1d8 force +damage per hit, +1 point per three caster levels (maximum +5 +at 15th level). The weapon takes the shape of a weapon +favored by your deity or a weapon with some spiritual +significance or symbolism to you (see below) and has the +same threat range and critical multipliers as a real weapon +of its form. It strikes the opponent you designate, starting +with one attack in the round the spell is cast and continuing +each round thereafter on your turn. It uses your base attack +bonus (possibly allowing it multiple attacks per round in +subsequent rounds) plus your Wisdom modifier as its attack +bonus. It strikes as a spell, not as a weapon, so, for +example, it can damage creatures that have damage +reduction. As a force effect, it can strike incorporeal +creatures without the normal miss chance associated with +incorporeality. The weapon always strikes from your +direction. It does not get a flanking bonus or help a +combatant get one. Your feats (such as Weapon Focus) +or combat actions (such as charge) do not affect the +weapon. If the weapon goes beyond the spell range, if +it goes out of your sight, or if you are not directing +it, the weapon returns to you and hovers. + +Each round after the first, you can use a move action to + redirect the weapon to a new target. If you do not, + the weapon continues to attack the previous round's + target. On any round that the weapon switches targets, + it gets one attack. Subsequent rounds of attacking that + target allow the weapon to make multiple attacks if your + base attack bonus would allow it to. Even if the spiritual + weapon is a ranged weapon, use the spell's range, not the + weapon's normal range increment, and switching targets + still is a move action. + +A spiritual weapon cannot be attacked or harmed by +physical attacks, but dispel magic, disintegrate, a +sphere of annihilation, or a rod of cancellation affects it. +A spiritual weapon's AC against touch attacks is 12 (10 + +size bonus for Tiny object). + +If an attacked creature has spell resistance, you make a +caster level check (1d20 + caster level) against that +spell resistance the first time the spiritual weapon +strikes it. If the weapon is successfully resisted, the +spell is dispelled. If not, the weapon has its normal +full effect on that creature for the duration of the spell. +*/////////////////////////////////////////////////////////// +#include "inc_rand_equip" +#include "prc_inc_spells" + + +void NullifyAppearance(object oSummon, int nThrowHands = FALSE) +{ + // Ensure the object is valid and is not a player or DM + if (!GetIsObjectValid(oSummon) || GetIsPC(oSummon) || GetIsDM(oSummon)) + { + return; + } + + // Nullify all body parts + SetCreatureBodyPart(CREATURE_PART_HEAD, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_TORSO, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_BELT, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_PELVIS, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, 0, oSummon); + SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, 0, oSummon); + + if(nThrowHands) + { + // Keep hands and forearms visible + SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, 1, oSummon); + SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, 1, oSummon); + SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, 1, oSummon); + SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, 1, oSummon); + } +} + +void RegisterSummonEvents(object oCreature) +{ + // Explicitly re-register the event for the next spell cast at the summon. + AddEventScript(oCreature, EVENT_NPC_ONSPELLCASTAT, "sp_spiritweapon", TRUE); + AddEventScript(oCreature, EVENT_NPC_ONSPELLCASTAT, "sp_spiritweapon", TRUE); + SendMessageToPC(GetFirstPC(), "inc_spirit_weapn: Event re-registered for next spell cast."); +} + +// Returns the alignment component with the most points towards it. +// Possible returns: ALIGNMENT_LAWFUL, ALIGNMENT_CHAOTIC, ALIGNMENT_GOOD, ALIGNMENT_EVIL, or ALIGNMENT_NEUTRAL. +int GetDominantAlignment(object oCreature) +{ + int nLawChaos = GetLawChaosValue(oCreature); + int nGoodEvil = GetGoodEvilValue(oCreature); + + int nDominant = ALIGNMENT_NEUTRAL; + + // Check Law vs Chaos + if (nLawChaos > 50) + { + nDominant = ALIGNMENT_LAWFUL; + } + else if (nLawChaos < 50) + { + nDominant = ALIGNMENT_CHAOTIC; + } + + // Check Good vs Evil + if (nGoodEvil > 50) + { + if (nLawChaos == 50 || nGoodEvil > nLawChaos) // Tie or Good is stronger + { + nDominant = ALIGNMENT_GOOD; + } + } + else if (nGoodEvil < 50) + { + if (nLawChaos == 50 || nGoodEvil < nLawChaos) // Tie or Evil is stronger + { + nDominant = ALIGNMENT_EVIL; + } + } + + return nDominant; +} + +void SetDeityByClass(object oCreature) +{ + if(GetLevelByClass(CLASS_TYPE_RAVAGER, oCreature) > 0) SetDeity(oCreature, "Erythnul"); + + if(GetLevelByClass(CLASS_TYPE_TEMPUS, oCreature) > 0) SetDeity(oCreature, "Tempus"); + + if(GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oCreature) > 0) SetDeity(oCreature, "Malar"); + + if(GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oCreature) > 0) SetDeity(oCreature, "Sharess"); + + if(GetLevelByClass(CLASS_TYPE_COC, oCreature) > 0) SetDeity(oCreature, "Corellon Larethian"); + + if(GetLevelByClass(CLASS_TYPE_VASSAL, oCreature) > 0) SetDeity(oCreature, "Bahamut"); + + if(GetLevelByClass(CLASS_TYPE_ORCUS, oCreature) > 0 ) SetDeity(oCreature, "Orcus"); + + if(GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCreature) > 0 ) SetDeity(oCreature, "Talona"); + + if(GetLevelByClass(CLASS_TYPE_STORMLORD, oCreature) > 0 ) SetDeity(oCreature, "Talos"); + + if(GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCreature) > 0 ) SetDeity(oCreature, "Tiamat"); + + if(GetLevelByClass(CLASS_TYPE_SOLDIER_OF_LIGHT, oCreature) > 0 ) SetDeity(oCreature, "Elishar"); + + if(GetLevelByClass(CLASS_TYPE_SLAYER_OF_DOMIEL, oCreature) > 0 ) SetDeity(oCreature, "Domiel"); + + if(GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCreature) > 0 ) SetDeity(oCreature, "Heironeous"); + + if(GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCreature) > 0 ) SetDeity(oCreature, "Wee Jas"); + + if(GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCreature) > 0 ) SetDeity(oCreature, "Lathander"); + + if(GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCreature) > 0 ) SetDeity(oCreature, "Kord"); + + if(GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCreature) > 0 ) SetDeity(oCreature, "Sune"); + + if(GetLevelByClass(CLASS_TYPE_HEXTOR, oCreature) > 0 ) SetDeity(oCreature, "Hextor"); + + if(GetLevelByClass(CLASS_TYPE_PRC_EYE_OF_GRUUMSH, oCreature) > 0 ) SetDeity(oCreature, "Gruumsh"); + + //if(GetLevelByClass(CLASS_TYPE_JUDICATOR, oCreature) > 0 ) SetDeity(oCreature, "Selvetarm"); + + if(GetLevelByClass(CLASS_TYPE_JUDICATOR, oCreature) > 0 ) SetDeity(oCreature, "Lolth"); + + if(GetLevelByClass(CLASS_TYPE_OCULAR, oCreature) > 0 ) SetDeity(oCreature, "Great Mother"); + + if(GetLevelByClass(CLASS_TYPE_KNIGHT_WEAVE, oCreature) > 0 ) SetDeity(oCreature, "Mystra"); +} + +string GetSpiritualWeaponTypeByDeity(object oCreature) +{ + SetDeityByClass(oCreature); + + string sDeity = GetStringLowerCase(GetDeity(oCreature)); + string sResRef; + + int nDomAlignment = GetDominantAlignment(oCreature); + + if(GetStringRight(sDeity, 12) == GetStringLowerCase("glittergold") || GetStringRight(sDeity, 5) == GetStringLowerCase("auril") + || GetStringRight(sDeity, 7) == GetStringLowerCase("balinor") || GetStringRight(sDeity, 11) == GetStringLowerCase("smoothhands") + || GetStringRight(sDeity, 11) == GetStringLowerCase("silverbeard") || GetStringRight(sDeity, 6) == GetStringLowerCase("duerra") + || GetStringRight(sDeity, 7) == GetStringLowerCase("gulthyn") || GetStringRight(sDeity, 8) == GetStringLowerCase("iallanis") + || GetStringRight(sDeity, 5) == GetStringLowerCase("llerg") || GetStringRight(sDeity, 10) == GetStringLowerCase("maglubiyet") + || GetStringRight(sDeity, 6) == GetStringLowerCase("tempus") || GetStringRight(sDeity, 6) == GetStringLowerCase("uthgar") + || GetStringRight(sDeity, 6) == GetStringLowerCase("valkar") || GetStringRight(sDeity, 5) == GetStringLowerCase("vatun")) + sResRef = "nw_waxbt001"; //:: Battleaxe + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("boccob") || GetStringRight(sDeity, 10) == GetStringLowerCase("fharlanghn") + || GetStringRight(sDeity, 3) == GetStringLowerCase("hai") || GetStringRight(sDeity, 6) == GetStringLowerCase("faenya") + || GetStringRight(sDeity, 6) == GetStringLowerCase("aureon") || GetStringRight(sDeity, 5) == GetStringLowerCase("azuth") + || GetStringRight(sDeity, 5) == GetStringLowerCase("bralm") || GetStringRight(sDeity, 11) == GetStringLowerCase("cyrrollalee") + || GetStringRight(sDeity, 8) == GetStringLowerCase("liothiel") || GetStringRight(sDeity, 9) == GetStringLowerCase("incabulos") + || GetStringRight(sDeity, 3) == GetStringLowerCase("geb") || GetStringRight(sDeity, 7) == GetStringLowerCase("enoreth") + || GetStringRight(sDeity, 6) == GetStringLowerCase("joramy") || GetStringRight(sDeity, 9) == GetStringLowerCase("panzuriel") + || GetStringRight(sDeity, 9) == GetStringLowerCase("rallathil") || GetStringRight(sDeity, 7) == GetStringLowerCase("pholtus") + || GetStringRight(sDeity, 3) == GetStringLowerCase("hai") || GetStringRight(sDeity, 10) == GetStringLowerCase("fharlanghn") + || GetStringRight(sDeity, 7) == GetStringLowerCase("moonbow") || GetStringRight(sDeity, 8) == GetStringLowerCase("shiallia") + || GetStringRight(sDeity, 7) == GetStringLowerCase("solanil") || GetStringRight(sDeity, 7) == GetStringLowerCase("tarmuid") + || GetStringRight(sDeity, 6) == GetStringLowerCase("shadow") || GetStringRight(sDeity, 5) == GetStringLowerCase("thoth") + || GetStringRight(sDeity, 10) == GetStringLowerCase("velsharoon") || GetStringRight(sDeity, 7) == GetStringLowerCase("ventila")) + sResRef = "nw_wdbqs001"; //:: Quarterstaff + + else if(GetStringRight(sDeity, 9) == GetStringLowerCase("larethian") || GetStringRight(sDeity, 7) == GetStringLowerCase("ehlonna") + || GetStringRight(sDeity, 10) == GetStringLowerCase("heironeous") || GetStringRight(sDeity, 5) == GetStringLowerCase("altua") + || GetStringRight(sDeity, 9) == GetStringLowerCase("barachiel") || GetStringRight(sDeity, 10) == GetStringLowerCase("heironeous") + || GetStringRight(sDeity, 5) == GetStringLowerCase("cyric") || GetStringRight(sDeity, 4) == GetStringLowerCase("dorn") + || GetStringRight(sDeity, 7) == GetStringLowerCase("garagos") || GetStringRight(sDeity, 7) == GetStringLowerCase("glautru") + || GetStringRight(sDeity, 7) == GetStringLowerCase("ilneval") || GetStringRight(sDeity, 6) == GetStringLowerCase("lendys") + || GetStringRight(sDeity, 4) == GetStringLowerCase("mask") || GetStringRight(sDeity, 10) == GetStringLowerCase("merrshaulk") + || GetStringRight(sDeity, 5) == GetStringLowerCase("oghma") || GetStringRight(sDeity, 8) == GetStringLowerCase("pyremius") + || GetStringRight(sDeity, 10) == GetStringLowerCase("red knight") || GetStringRight(sDeity, 3) == GetStringLowerCase("tyr") + || GetStringRight(sDeity, 9) == GetStringLowerCase("vergadain") || GetStringRight(sDeity, 7) == GetStringLowerCase("elishar")) + sResRef = "nw_wswls001"; //:: Longsword + + else if(GetStringRight(sDeity, 8) == GetStringLowerCase("erythnul") || GetStringRight(sDeity, 6) == GetStringLowerCase("arawai") + || GetStringRight(sDeity, 4) == GetStringLowerCase("bane") || GetStringRight(sDeity, 7) == GetStringLowerCase("hruggek") + || GetStringRight(sDeity, 6) == GetStringLowerCase("ngatha") || GetStringRight(sDeity, 6) == GetStringLowerCase("memnor") + || GetStringRight(sDeity, 7) == GetStringLowerCase("wathaku")) + sResRef = "nw_wblms001"; //:: Morningstar + + else if(GetStringRight(sDeity, 8) == GetStringLowerCase("gruumsh") || GetStringRight(sDeity, 10) == GetStringLowerCase("angharradh") + || GetStringRight(sDeity, 5) == GetStringLowerCase("annam") || GetStringRight(sDeity, 10) == GetStringLowerCase("aventernus") + || GetStringRight(sDeity, 13) == GetStringLowerCase("wildwanderer") || GetStringRight(sDeity, 7) == GetStringLowerCase("boldrei") + || GetStringRight(sDeity, 9) == GetStringLowerCase("celestian") || GetStringRight(sDeity, 5) == GetStringLowerCase("eadro") + || GetStringRight(sDeity, 7) == GetStringLowerCase("geshtai") || GetStringRight(sDeity, 6) == GetStringLowerCase("hiatea") + || GetStringRight(sDeity, 10) == GetStringLowerCase("kaelthiere") || GetStringRight(sDeity, 6) == GetStringLowerCase("kithin") + || GetStringRight(sDeity, 9) == GetStringLowerCase("kurtulmak") || GetStringRight(sDeity, 5) == GetStringLowerCase("lurue") + || GetStringRight(sDeity, 6) == GetStringLowerCase("procan") || GetStringRight(sDeity, 5) == GetStringLowerCase("sebek") + || GetStringRight(sDeity, 3) == GetStringLowerCase("set") || GetStringRight(sDeity, 7) == GetStringLowerCase("skerrit") + || GetStringRight(sDeity, 5) == GetStringLowerCase("talos") || GetStringRight(sDeity, 7) == GetStringLowerCase("telchur") + || GetStringRight(sDeity, 10) == GetStringLowerCase("trithereon") || GetStringRight(sDeity, 6) == GetStringLowerCase("ulutiu")) + sResRef = "nw_wplss001"; //:: Spear + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("hextor") || GetStringRight(sDeity, 11) == GetStringLowerCase("patient one")) + sResRef = "nw_wblfl001"; //:: Light Flail + + else if(GetStringRight(sDeity, 5) == GetStringLowerCase("akadi") || GetStringRight(sDeity, 8) == GetStringLowerCase("lliendil") + || GetStringRight(sDeity, 6) == GetStringLowerCase("osiris") || GetStringRight(sDeity, 8) == GetStringLowerCase("urogalan")) + sResRef = "nw_wblfh001"; //:: Heavy Flail + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("domiel") || GetStringRight(sDeity, 9) == GetStringLowerCase("windstrom") + || GetStringRight(sDeity, 9) == GetStringLowerCase("brightaxe") || GetStringRight(sDeity, 3) == GetStringLowerCase("iuz") + || GetStringRight(sDeity, 4) == GetStringLowerCase("kord") || GetStringRight(sDeity, 10) == GetStringLowerCase("shaundakul") + || GetStringRight(sDeity, 5) == GetStringLowerCase("surtr") || GetStringRight(sDeity, 14) == GetStringLowerCase("lord of blades") + || GetStringRight(sDeity, 4) == GetStringLowerCase("torm") || GetStringRight(sDeity, 6) == GetStringLowerCase("typhos") + || GetStringRight(sDeity, 5) == GetStringLowerCase("zarus")) + sResRef = "nw_wswgs001"; //:: Greatsword + + else if(GetStringRight(sDeity, 7) == GetStringLowerCase("aulasha") || GetStringRight(sDeity, 9) == GetStringLowerCase("steelskin") + || GetStringRight(sDeity, 8) == GetStringLowerCase("ironhand") || GetStringRight(sDeity, 7) == GetStringLowerCase("grumbar") + || GetStringRight(sDeity, 4) == GetStringLowerCase("gond") || GetStringRight(sDeity, 8) == GetStringLowerCase("istishia") + || GetStringRight(sDeity, 8) == GetStringLowerCase("laduguer") || GetStringRight(sDeity, 5) == GetStringLowerCase("lyris") + || GetStringRight(sDeity, 7) == GetStringLowerCase("moradin") || GetStringRight(sDeity, 6) == GetStringLowerCase("onatar") + || GetStringRight(sDeity, 10) == GetStringLowerCase("stonebones") || GetStringRight(sDeity, 9) == GetStringLowerCase("stronmaus")) + sResRef = "nw_wblhw001"; //:: Warhammer + + else if(GetStringRight(sDeity, 8) == GetStringLowerCase("chauntea") || GetStringRight(sDeity, 11) == GetStringLowerCase("chronepsis") + || GetStringRight(sDeity, 7) == GetStringLowerCase("duthila") || GetStringRight(sDeity, 8) == GetStringLowerCase("iborighu") + || GetStringRight(sDeity, 6) == GetStringLowerCase("jergal") || GetStringRight(sDeity, 6) == GetStringLowerCase("nerull") + || GetStringRight(sDeity, 6) == GetStringLowerCase("keeper") || GetStringRight(sDeity, 5) == GetStringLowerCase("zuggtmoy")) + sResRef = "nw_wplsc001"; //:: Scythe + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("halmyr") || GetStringRight(sDeity, 6) == GetStringLowerCase("halmyr") + || GetStringRight(sDeity, 8) == GetStringLowerCase("levistus") || GetStringRight(sDeity, 4) == GetStringLowerCase("lirr") + || GetStringRight(sDeity, 5) == GetStringLowerCase("milil") || GetStringRight(sDeity, 10) == GetStringLowerCase("olidammara") + || GetStringRight(sDeity, 8) == GetStringLowerCase("the fury")) + sResRef = "nw_wswrp001"; //:: Rapier + + else if(GetStringRight(sDeity, 8) == GetStringLowerCase("cuthbert") || GetStringRight(sDeity, 5) == GetStringLowerCase("pelor") + || GetStringRight(sDeity, 10) == GetStringLowerCase("truesilver") || GetStringRight(sDeity, 8) == GetStringLowerCase("kikanuti") + || GetStringRight(sDeity, 6) == GetStringLowerCase("korran") || GetStringRight(sDeity, 9) == GetStringLowerCase("lathander") + || GetStringRight(sDeity, 4) == GetStringLowerCase("duin") || GetStringRight(sDeity, 5) == GetStringLowerCase("orcus") + || GetStringRight(sDeity, 11) == GetStringLowerCase("earthcaller") || GetStringRight(sDeity, 6) == GetStringLowerCase("selune") + || GetStringRight(sDeity, 9) == GetStringLowerCase("selvetarm") || GetStringRight(sDeity, 6) == GetStringLowerCase("syeret") + || GetStringRight(sDeity, 6) == GetStringLowerCase("syreth") || GetStringRight(sDeity, 7) == GetStringLowerCase("urbanus")) + sResRef = "prc_wxblmh001"; //:: Heavy Mace + + else if(GetStringRight(sDeity, 3) == GetStringLowerCase("rao") || GetStringRight(sDeity, 9) == GetStringLowerCase("siamorphe")) + sResRef = "nw_wblml001"; //:: Light Mace + + else if(GetStringRight(sDeity, 3) == GetStringLowerCase("jas") || GetStringRight(sDeity, 5) == GetStringLowerCase("vecna") + || GetStringRight(sDeity, 8) == GetStringLowerCase("lorfiril") || GetStringRight(sDeity, 11) == GetStringLowerCase("cloakshadow") + || GetStringRight(sDeity, 8) == GetStringLowerCase("abbathor") || GetStringRight(sDeity, 11) == GetStringLowerCase("brandobaris") + || GetStringRight(sDeity, 5) == GetStringLowerCase("thaun") || GetStringRight(sDeity, 6) == GetStringLowerCase("deneir") + || GetStringRight(sDeity, 8) == GetStringLowerCase("diirinka") || GetStringRight(sDeity, 5) == GetStringLowerCase("glory") + || GetStringRight(sDeity, 9) == GetStringLowerCase("mestarine") || GetStringRight(sDeity, 8) == GetStringLowerCase("gargauth") + || GetStringRight(sDeity, 7) == GetStringLowerCase("celanil") || GetStringRight(sDeity, 5) == GetStringLowerCase("istus") + || GetStringRight(sDeity, 11) == GetStringLowerCase("kiaransalee") || GetStringRight(sDeity, 6) == GetStringLowerCase("savras") + || GetStringRight(sDeity, 11) == GetStringLowerCase("shekinester") || GetStringRight(sDeity, 9) == GetStringLowerCase("tharizdun") + || GetStringRight(sDeity, 6) == GetStringLowerCase("of vol") || GetStringRight(sDeity, 5) == GetStringLowerCase("zagyg")) + sResRef = "nw_wswdg001"; //:: Dagger + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("afflux") || GetStringRight(sDeity, 8) == GetStringLowerCase("arvoreen") + || GetStringRight(sDeity, 12) == GetStringLowerCase("brightmantle") || GetStringRight(sDeity, 7) == GetStringLowerCase("ilesere") + || GetStringRight(sDeity, 6) == GetStringLowerCase("hathor") || GetStringRight(sDeity, 4) == GetStringLowerCase("hlal") + || GetStringRight(sDeity, 8) == GetStringLowerCase("nadirech") || GetStringRight(sDeity, 8) == GetStringLowerCase("shargaas") + || GetStringRight(sDeity, 5) == GetStringLowerCase("sixin") || GetStringRight(sDeity, 8) == GetStringLowerCase("vhaeraun") + || GetStringRight(sDeity, 8) == GetStringLowerCase("yondalla")) + sResRef = "nw_wswss001"; //:: Shortsword + + else if(GetStringRight(sDeity, 8) == GetStringLowerCase("kelemvor") || GetStringRight(sDeity, 4) == GetStringLowerCase("helm") + || GetStringRight(sDeity, 10) == GetStringLowerCase("wyvernspur") || GetStringRight(sDeity, 10) == GetStringLowerCase("eilistraee") + || GetStringRight(sDeity, 8) == GetStringLowerCase("aengrist")) + sResRef = "nw_wswbs001"; //:: Bastard Sword + + else if(GetStringRight(sDeity, 11) == GetStringLowerCase("aasterinian") || GetStringRight(sDeity, 9) == GetStringLowerCase("astilabor") + || GetStringRight(sDeity, 8) == GetStringLowerCase("doresain") || GetStringRight(sDeity, 8) == GetStringLowerCase("falazure") + || GetStringRight(sDeity, 4) == GetStringLowerCase("haku") || GetStringRight(sDeity, 8) == GetStringLowerCase("mielikki") + || GetStringRight(sDeity, 8) == GetStringLowerCase("nilthina") || GetStringRight(sDeity, 8) == GetStringLowerCase("soorinek") + || GetStringRight(sDeity, 6) == GetStringLowerCase("tamara") || GetStringRight(sDeity, 8) == GetStringLowerCase("traveler") + || GetStringRight(sDeity, 13) == GetStringLowerCase("undying court")) + sResRef = "nw_wswsc001"; //:: Scimitar + + else if(GetStringRight(sDeity, 19) == GetStringLowerCase("spirits of the past")) + sResRef = "prc_wxdbsc001"; //:: Double Scimitar + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("mouqol")) + sResRef = "nw_wbwxl001"; //:: Light Crossbow + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("cyndor")) + sResRef = "nw_wbwsl001"; //:: Sling + + else if(GetStringRight(sDeity, 4) == GetStringLowerCase("isis")) + sResRef = "prc_wswdp001"; //:: Katar (punching dagger) + + else if(GetStringRight(sDeity, 5) == GetStringLowerCase("arrah")) + sResRef = "nw_wplhb001"; //:: Halberd + + else if(GetStringRight(sDeity, 5) == GetStringLowerCase("delleb")) + sResRef = "nw_wthdt001"; //:: Dart + + else if(GetStringRight(sDeity, 12) == GetStringLowerCase("great mother") || GetStringRight(sDeity, 8) == GetStringLowerCase("sulerian") + || GetStringRight(sDeity, 5) == GetStringLowerCase("thrym")) + sResRef = "nw_waxgr001"; //:: Greataxe + + else if(GetStringRight(sDeity, 9) == GetStringLowerCase("tem-et-nu") || GetStringRight(sDeity, 7) == GetStringLowerCase("mockery")) + sResRef = "nw_wspka001"; //:: Kama + + else if(GetStringRight(sDeity, 9) == GetStringLowerCase("dumathoin") || GetStringRight(sDeity, 8) == GetStringLowerCase("silvanus")) + sResRef = "prc_wxblma001"; //:: Maul + + else if(GetStringRight(sDeity, 9) == GetStringLowerCase("shevarash") || GetStringRight(sDeity, 10) == GetStringLowerCase("thelandira") + || GetStringRight(sDeity, 12) == GetStringLowerCase("silver flame") || GetStringRight(sDeity, 10) == GetStringLowerCase("gilmadrith")) + sResRef = "nw_wbwln001"; //:: Long Bow + + else if(GetStringRight(sDeity, 11) == GetStringLowerCase("cyrrollalee") || GetStringRight(sDeity, 9) == GetStringLowerCase("grolantor") + || GetStringRight(sDeity, 8) == GetStringLowerCase("konkresh") || GetStringRight(sDeity, 5) == GetStringLowerCase("kyuss") + || GetStringRight(sDeity, 8) == GetStringLowerCase("semuanya") || GetStringRight(sDeity, 6) == GetStringLowerCase("vaprak") + || GetStringRight(sDeity, 12) == GetStringLowerCase("whale mother")) + sResRef = "nw_wblcl001"; //:: Club + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("ishtus") || GetStringRight(sDeity, 4) == GetStringLowerCase("azul") + || GetStringRight(sDeity, 5) == GetStringLowerCase("lolth") || GetStringRight(sDeity, 8) == GetStringLowerCase("nephthys") + || GetStringRight(sDeity, 10) == GetStringLowerCase("sharindlar") || GetStringRight(sDeity, 6) == GetStringLowerCase("sune")) + sResRef = "x2_it_wpwhip"; //:: Whip + + else if(GetStringRight(sDeity, 7) == GetStringLowerCase("bahamut") || GetStringRight(sDeity, 8) == GetStringLowerCase("nobanion") + || GetStringRight(sDeity, 12) == GetStringLowerCase("dragon below") || GetStringRight(sDeity, 6) == GetStringLowerCase("tiamat") + || GetStringRight(sDeity, 5) == GetStringLowerCase("ubtao")) + sResRef = "prc_wblph001"; //:: Heavy Pick + + else if(GetStringRight(sDeity, 9) == GetStringLowerCase("waukeen") || GetStringRight(sDeity, 6) == GetStringLowerCase("zouken")) + sResRef = "prc_wblnn001"; //:: Nunchaku + + else if(GetStringRight(sDeity, 5) == GetStringLowerCase("garyx") || GetStringRight(sDeity, 7) == GetStringLowerCase("olladra") + || GetStringRight(sDeity, 8) == GetStringLowerCase("peryroyl")) + sResRef = "nw_wspsc001"; //:: Sickle + + else if(GetStringRight(sDeity, 6) == GetStringLowerCase("lliira") || GetStringRight(sDeity, 6) == GetStringLowerCase("mystra") + || GetStringRight(sDeity, 6) == GetStringLowerCase("tymora")) + sResRef = "nw_wthsh001"; //:: Throwing Stars + + else if(GetStringRight(sDeity, 8) == GetStringLowerCase("sashelas") || GetStringRight(sDeity, 5) == GetStringLowerCase("hleid") + || GetStringRight(sDeity, 6) == GetStringLowerCase("osprem") || GetStringRight(sDeity, 8) == GetStringLowerCase("sekolah") + || GetStringRight(sDeity, 8) == GetStringLowerCase("devourer") || GetStringRight(sDeity, 8) == GetStringLowerCase("umberlee") + || GetStringRight(sDeity, 7) == GetStringLowerCase("yeathan")) + sResRef = "nw_wpltr001"; //:: Trident + + else if(GetStringRight(sDeity, 7) == GetStringLowerCase("ilmater") || GetStringRight(sDeity, 9) == GetStringLowerCase("ilsensine") + || GetStringRight(sDeity, 6) == GetStringLowerCase("sophia") || GetStringRight(sDeity, 6) == GetStringLowerCase("talona") + || GetStringRight(sDeity, 13) == GetStringLowerCase("path of light") || GetStringRight(sDeity, 7) == GetStringLowerCase("yurtrus") + || GetStringRight(sDeity, 7) == GetStringLowerCase("sharess") || GetStringRight(sDeity, 7) == GetStringLowerCase("malar")) + { //sResRef = "nw_wpltr001"; + sResRef = "prc_sprtwpn_slam"; //:: Unarmed Strike + } + + else if(sDeity == "" && nDomAlignment == ALIGNMENT_EVIL) + sResRef = "nw_wblfl001"; //:: Light Flail + + else if(sDeity == "" && nDomAlignment == ALIGNMENT_CHAOTIC) + sResRef = "nw_waxbt001"; //:: Battleaxe + + else if(sDeity == "" && nDomAlignment == ALIGNMENT_GOOD) + sResRef = "nw_wblhw001"; //:: Warhammer + + else if(sDeity == "" && nDomAlignment == ALIGNMENT_LAWFUL) + sResRef = "nw_wswgs001"; //:: Greatsword + + else if(sDeity == "" && nDomAlignment == ALIGNMENT_NEUTRAL) + sResRef = "nw_wswsc001"; //:: Scimitar + + return sResRef; +} + +object CreateDeityWeapon(object oCreature) +{ + string sWeapon = GetSpiritualWeaponTypeByDeity(oCreature); + + if(sWeapon == "") + return OBJECT_INVALID; + + object oWeapon = CreateItemOnObject(sWeapon, oCreature); + + EquipDebugString(GetName(oCreature) + " has Deity weapon "+GetName(oWeapon)); + return oWeapon; +} + +//:: Creates the weapon that the creature will be using. +void CreateSpiritualWeapon(object oCaster, float fDuration, int nClass) +{ +//:: Declare major variables + int iCasterLvL = PRCGetCasterLevel(oCaster); + int nBAB = GetBaseAttackBonus(oCaster); + int nAttNumber = 1+(nBAB / 4); + int nDamBonus = PRCMin(5, iCasterLvL / 3); + int nPenetr = iCasterLvL + SPGetPenetr(); + int nStat = nClass == CLASS_TYPE_INVALID ? + GetAbilityModifier(ABILITY_CHARISMA, oCaster) ://:: if cast from items use charisma by default + GetDCAbilityModForClass(nClass, oCaster); + + object oWeapon; + object oArmor; + object oAmmo1; + object oAmmo2; + + string sWeapon = GetSpiritualWeaponTypeByDeity(oCaster); + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster); + SendMessageToPC(oCaster, "inc_spirit_weapn: Hooking Event."); + + RegisterSummonEvents(oSummon); + + int i = 1; + while(GetIsObjectValid(oSummon) && !GetIsPC(oSummon) && ! GetIsDM(oSummon)) + { + NullifyAppearance(oSummon); + + if(GetResRef(oSummon) == "prc_spirit_weapn") //:: Unarmed / "Claw Bracer" + { + SetBaseAttackBonus(nAttNumber, oSummon); + SendMessageToPC(oCaster, "inc_spirit_weapn:" +GetName(oCaster)+"'s BAB is: "+IntToString(nBAB)); + //EffectModifyAttacks(nAttNumber); + SendMessageToPC(oCaster, "inc_spirit_weapn: Adding "+IntToString(nAttNumber)+" attacks / round."); + SetLocalInt(oSummon, "X2_L_NUMBER_OF_ATTACKS", nAttNumber); + SendMessageToPC(oCaster, "inc_spirit_weapn: Storing caster as: " + GetName(oCaster)); + DelayCommand(0.0f, SetLocalObject(oSummon, "MY_CASTER", oCaster)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_GHOST_SMOKE_2), oSummon); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_GHOST_TRANSPARENT), oSummon); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectAttackIncrease(nBAB+nStat), oSummon); + + if(!GetIsObjectValid(GetItemPossessedBy(oSummon, sWeapon))) + { + //Create item on the creature, equip it and add properties. + oWeapon = CreateItemOnObject(sWeapon, oSummon); + + //Add event scripts + AddEventScript(oWeapon, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + AddEventScript(oWeapon, EVENT_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + AddEventScript(oWeapon, EVENT_ITEM_ONUNAQUIREITEM, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onUnacquire event script on weapon"); + AddEventScript(oWeapon, EVENT_ITEM_ONPLAYERUNEQUIPITEM, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onUnequip event script on weapon"); + + // GZ: Fix for weapon being dropped when killed + SetDroppableFlag(oWeapon, FALSE); + SetItemCursedFlag(oWeapon, TRUE); + + if(sWeapon == "prc_sprtwpn_slam") + { + NullifyAppearance(oSummon, TRUE); + oArmor = CreateItemOnObject("prc_sprtwp_armor", oSummon); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_CWEAPON_R); + ForceEquip(oSummon, oArmor, INVENTORY_SLOT_CHEST); + SetItemCursedFlag(oArmor, TRUE); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_GLOW_WHITE), oSummon); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + } + else if(sWeapon == "nw_wbwln001") //:: Longbow + { + NullifyAppearance(oSummon); + oAmmo1 = CreateItemOnObject("nw_wambo001", oSummon); + SetItemStackSize(oAmmo1, 99); + SetDroppableFlag(oAmmo1, FALSE); + SetItemCursedFlag(oAmmo1, TRUE); + + AddEventScript(oAmmo1, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on ammo"); + + oAmmo2 = CreateItemOnObject("nw_wambo001", oSummon); + SetItemStackSize(oAmmo2, 99); + SetDroppableFlag(oAmmo2, FALSE); + SetItemCursedFlag(oAmmo2, TRUE); + + AddEventScript(oAmmo2, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on ammo"); + + ForceEquip(oSummon, oAmmo1, INVENTORY_SLOT_ARROWS); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage1 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo1, ipDamage1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz1 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipViz1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit1 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipHit1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam1 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipNoDam1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipDamage2 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo2, ipDamage2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit2 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipHit2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz2 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipViz2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam2 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipNoDam2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit3 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz3 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam3 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else if(sWeapon == "nw_wbwxl001") //:: Light crossbow + { + NullifyAppearance(oSummon); + oAmmo1 = CreateItemOnObject("nw_wambo001", oSummon); + SetItemStackSize(oAmmo1, 99); + SetDroppableFlag(oAmmo1, FALSE); + SetItemCursedFlag(oAmmo1, TRUE); + + AddEventScript(oAmmo1, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on ammo"); + + oAmmo2 = CreateItemOnObject("nw_wambo001", oSummon); + SetItemStackSize(oAmmo2, 99); + SetDroppableFlag(oAmmo2, FALSE); + SetItemCursedFlag(oAmmo2, TRUE); + + AddEventScript(oAmmo2, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on ammo"); + + ForceEquip(oSummon, oAmmo1, INVENTORY_SLOT_BOLTS); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage1 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo1, ipDamage1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz1 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipViz1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit1 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipHit1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam1 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipNoDam1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipDamage2 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo2, ipDamage2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit2 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipHit2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz2 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipViz2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam2 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipNoDam2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit3 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz3 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam3 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else if(sWeapon == "nw_wthsh001") //:: Throwing Stars + { + NullifyAppearance(oSummon); + SetItemStackSize(oWeapon, 50); + SetDroppableFlag(oWeapon, FALSE); + SetItemCursedFlag(oWeapon, TRUE); + + AddEventScript(oWeapon, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + + object oWeapon2 = CreateItemOnObject("nw_wthsh001", oSummon); + SetItemStackSize(oWeapon2, 50); + SetDroppableFlag(oWeapon2, FALSE); + SetItemCursedFlag(oWeapon2, TRUE); + + AddEventScript(oWeapon2, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + + object oWeapon3 = CreateItemOnObject("nw_wthsh001", oSummon); + SetItemStackSize(oWeapon3, 50); + SetDroppableFlag(oWeapon3, FALSE); + SetItemCursedFlag(oWeapon3, TRUE); + + AddEventScript(oWeapon3, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon2, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else if(sWeapon == "nw_wthdt001") //:: Darts + { + NullifyAppearance(oSummon); + SetItemStackSize(oWeapon, 50); + SetDroppableFlag(oWeapon, FALSE); + SetItemCursedFlag(oWeapon, TRUE); + + AddEventScript(oWeapon, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + + object oWeapon2 = CreateItemOnObject("nw_wthdt001", oSummon); + SetItemStackSize(oWeapon2, 50); + SetDroppableFlag(oWeapon2, FALSE); + SetItemCursedFlag(oWeapon2, TRUE); + + AddEventScript(oWeapon2, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + + object oWeapon3 = CreateItemOnObject("nw_wthdt001", oSummon); + SetItemStackSize(oWeapon3, 50); + SetDroppableFlag(oWeapon3, FALSE); + SetItemCursedFlag(oWeapon3, TRUE); + + AddEventScript(oWeapon3, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon2, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon3, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else //:: Covers all other weapons + { + NullifyAppearance(oSummon); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + + } + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster, i); + } +} + +void HandleSpiritualWeaponUnequipEvent() +{ + // Get the creature who unequipped the item + object oSummon = GetPCItemLastUnequippedBy(); + object oCaster = GetLocalObject(oSummon, "MY_CASTER"); + + // Get the item that was unequipped + object oWeapon = GetPCItemLastUnequipped(); + + if(GetIsPC(oSummon) == TRUE) + { + return; + } + + int nCasterLevel = PRCGetCasterLevel(oCaster); + int nDuration = nCasterLevel; + int nPenetr = nCasterLevel + SPGetPenetr(); + int nDamBonus = PRCMin(5, nCasterLevel / 3); + float fDuration = IntToFloat(nDuration); + + // Log the event for debugging + SendMessageToPC(oCaster, "prc_spirweap_tbs: Item OnUnEquip Event running."); + + // Check if the item is valid + if (GetIsObjectValid(oWeapon)) + { + // Log the item destruction for debugging + SendMessageToPC(oCaster, "prc_spirweap_tbs: Unequipped item detected. Destroying item."); + + // Destroy the unequipped item + DestroyObject(oWeapon); + + string sWeapon = GetSpiritualWeaponTypeByDeity(oCaster); + object oArmor; + + if(!GetIsObjectValid(GetItemPossessedBy(oSummon, sWeapon))) + { + //Create item on the creature, equip it and add properties. + oWeapon = CreateItemOnObject(sWeapon, oSummon); + + //Add event scripts + AddEventScript(oWeapon, EVENT_ITEM_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + AddEventScript(oWeapon, EVENT_ONHIT, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onHit event script on weapon"); + AddEventScript(oWeapon, EVENT_ITEM_ONUNAQUIREITEM, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onUnacquire event script on weapon"); + AddEventScript(oWeapon, EVENT_ITEM_ONPLAYERUNEQUIPITEM, "prc_evnt_spirwep", TRUE, FALSE); + SendMessageToPC(oCaster, "inc_spirit_weapn: Setting onUnequip event script on weapon"); + + // GZ: Fix for weapon being dropped when killed + SetDroppableFlag(oWeapon, FALSE); + SetItemCursedFlag(oWeapon, TRUE); + + if(sWeapon == "prc_sprtwpn_slam") + { + NullifyAppearance(oSummon, TRUE); + oArmor = CreateItemOnObject("prc_sprtwp_armor", oSummon); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_CWEAPON_R); + ForceEquip(oSummon, oArmor, INVENTORY_SLOT_CHEST); + SetItemCursedFlag(oArmor, TRUE); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_GLOW_WHITE), oSummon); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + } + else if(sWeapon == "nw_wbwln001") //:: Longbow + { + NullifyAppearance(oSummon); + object oAmmo1 = CreateItemOnObject("nw_wamar001", oSummon); + SetItemStackSize(oAmmo1, 99); + SetDroppableFlag(oAmmo1, FALSE); + SetItemCursedFlag(oAmmo1, TRUE); + + object oAmmo2 = CreateItemOnObject("nw_wamar001", oSummon); + SetItemStackSize(oAmmo2, 99); + SetDroppableFlag(oAmmo2, FALSE); + SetItemCursedFlag(oAmmo2, TRUE); + + ForceEquip(oSummon, oAmmo1, INVENTORY_SLOT_ARROWS); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage1 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo1, ipDamage1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz1 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipViz1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit1 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipHit1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam1 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipNoDam1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipDamage2 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo2, ipDamage2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit2 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipHit2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz2 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipViz2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam2 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipNoDam2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit3 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz3 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam3 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else if(sWeapon == "nw_wbwxl001") //:: Light crossbow + { + NullifyAppearance(oSummon); + object oAmmo1 = CreateItemOnObject("nw_wambo001", oSummon); + SetItemStackSize(oAmmo1, 99); + SetDroppableFlag(oAmmo1, FALSE); + SetItemCursedFlag(oAmmo1, TRUE); + + object oAmmo2 = CreateItemOnObject("nw_wambo001", oSummon); + SetItemStackSize(oAmmo2, 99); + SetDroppableFlag(oAmmo2, FALSE); + SetItemCursedFlag(oAmmo2, TRUE); + + ForceEquip(oSummon, oAmmo1, INVENTORY_SLOT_BOLTS); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage1 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo1, ipDamage1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz1 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipViz1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit1 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipHit1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam1 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo1, ipNoDam1, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipDamage2 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oAmmo2, ipDamage2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit2 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipHit2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz2 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipViz2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam2 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oAmmo2, ipNoDam2, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit3 = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz3 = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam3 = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam3, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else if(sWeapon == "nw_wthsh001") //:: Throwing Stars + { + NullifyAppearance(oSummon); + SetItemStackSize(oWeapon, 50); + SetDroppableFlag(oWeapon, FALSE); + SetItemCursedFlag(oWeapon, TRUE); + + object oWeapon2 = CreateItemOnObject("nw_wthsh001", oSummon); + SetItemStackSize(oWeapon2, 50); + SetDroppableFlag(oWeapon2, FALSE); + SetItemCursedFlag(oWeapon2, TRUE); + + object oWeapon3 = CreateItemOnObject("nw_wthsh001", oSummon); + SetItemStackSize(oWeapon3, 50); + SetDroppableFlag(oWeapon3, FALSE); + SetItemCursedFlag(oWeapon3, TRUE); + + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon2, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon3, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else if(sWeapon == "nw_wthdt001") //:: Darts + { + NullifyAppearance(oSummon); + SetItemStackSize(oWeapon, 50); + SetDroppableFlag(oWeapon, FALSE); + SetItemCursedFlag(oWeapon, TRUE); + + object oWeapon2 = CreateItemOnObject("nw_wthdt001", oSummon); + SetItemStackSize(oWeapon2, 50); + SetDroppableFlag(oWeapon2, FALSE); + SetItemCursedFlag(oWeapon2, TRUE); + + object oWeapon3 = CreateItemOnObject("nw_wthdt001", oSummon); + SetItemStackSize(oWeapon3, 50); + SetDroppableFlag(oWeapon3, FALSE); + SetItemCursedFlag(oWeapon3, TRUE); + + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon2, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon2, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon3, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon3, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + else //:: Covers all other weapons + { + NullifyAppearance(oSummon); + ForceEquip(oSummon, oWeapon, INVENTORY_SLOT_RIGHTHAND); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectDamageIncrease(nDamBonus, DAMAGE_TYPE_MAGICAL), oSummon); + + itemproperty ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_MAGICAL, IP_CONST_DAMAGEBONUS_1d8); + DelayCommand(0.0f, IPSafeAddItemProperty(oWeapon, ipDamage, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipViz = ItemPropertyVisualEffect(ITEM_VISUAL_SONIC); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipViz, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, nPenetr); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipHit, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + + itemproperty ipNoDam = ItemPropertyNoDamage(); + DelayCommand(0.0f,IPSafeAddItemProperty(oWeapon, ipNoDam, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING)); + } + } + else + { + SendMessageToPC(oCaster, "prc_spirweap_tbs: No valid item to destroy."); + } + + return; + } +} + +//:: void main(){} \ No newline at end of file diff --git a/src/include/inc_sql.nss b/src/include/inc_sql.nss new file mode 100644 index 0000000..35cd8c8 --- /dev/null +++ b/src/include/inc_sql.nss @@ -0,0 +1,316 @@ +/** + * @file + * All the functions dealing with the NWNx database. Based on the APS include + * with optimisations, renamed to avoid naming clashes with the APS system. + * @author Primogenitor, motu99, fluffyamoeba + * @date created on 2009-01-25 + */ + + #include "prc_inc_switch" + +const int PRC_SQL_ERROR = 0; +const int PRC_SQL_SUCCESS = 1; +const string XCHST_DB = "xchst_db"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +// creates and stores a 1024 Byte string for database-fetches on the module +void PRC_SQLInit(); + +// executes an SQL command direclty +void PRC_SQLExecDirect(string sSQL); + +// fetches data from a previous SQL fetch request; returns TRUE is fetch was successful +int PRC_SQLFetch(); + +// gets the actual data from a fetch; if a table with more than 1 column was requested, return the table element at column iCol +string PRC_SQLGetData(int iCol); + +// SQLite and MYSQL use different syntaxes +string PRC_SQLGetTick(); + +// Problems can arise with SQL commands if variables or values have single quotes +// in their names. These functions are a replace these quote with the tilde character +string ReplaceSingleChars(string sString, string sTarget, string sReplace); + +// only needed for SQLite; commits (actually stores) the changed to the database +void PRC_SQLiteCommit(); +// starts the pseudo heartbeat for committing SQLite DB +void StartSQLiteCommitHB(); +// pseudo heartbeat for committing SQLite DB; interval given in switch PRC_DB_SQLITE_INTERVAL +// default is 600 seconds (= 10 min) +void SQLiteCommitHB(); + +// Set oObject's persistent object with sVarName to sValue +// Optional parameters: +// iExpiration: Number of days the persistent variable should be kept in database (default: 0=forever) +// sTable: Name of the table where variable should be stored (default: pwobjdata) +void PRC_SetPersistentObject(object oObject, string sVarName, object oObject2, int iExpiration = 0, string sTable = "pwobjdata"); + +// Get oObject's persistent object sVarName +// Optional parameters: +// sTable: Name of the table where object is stored (default: pwobjdata) +// * Return value on error: 0 +object PRC_GetPersistentObject(object oObject, string sVarName, object oOwner = OBJECT_INVALID, string sTable = "pwobjdata"); + +// Portable presistent chest system (X-Chest) nwnx database support +// +void CreateXChestDB_SQLite(); +void CreateXChestDB_MySQL(); +void DeleteXChestDB(); + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void PRC_SQLInit() +{ + int i; + + // Placeholder for ODBC persistence + string sMemory; + + for (i = 0; i < 8; i++) // reserve 8*128 bytes + sMemory += + "................................................................................................................................"; + + SetLocalString(GetModule(), "NWNX!ODBC!SPACER", sMemory); +} + +void PRC_SQLExecDirect(string sSQL) +{ + SetLocalString(GetModule(), "NWNX!ODBC!EXEC", sSQL); +} + +int PRC_SQLFetch() +{ + string sRow; + object oModule = GetModule(); + + // initialize the fetch string to a large 1024 Byte string; this will also trigger the fetch operation in NWNX + SetLocalString(oModule, "NWNX!ODBC!FETCH", GetLocalString(oModule, "NWNX!ODBC!SPACER")); + + // get the result from the fetch + sRow = GetLocalString(oModule, "NWNX!ODBC!FETCH"); + if (GetStringLength(sRow) > 0) + { + // store the result on the module and signal success + SetLocalString(oModule, "NWNX_ODBC_CurrentRow", sRow); + return PRC_SQL_SUCCESS; + } + else + { + // set the result string on the module to empty and signal failure + SetLocalString(oModule, "NWNX_ODBC_CurrentRow", ""); + return PRC_SQL_ERROR; + } +} + +/** @todo check mySQL manual, not sure if this is needed - fluffyamoeba */ +string PRC_SQLGetTick() +{ + if(GetPRCSwitch(PRC_DB_SQLITE)) + { + return ""; + } + else + { + return "`"; + } +} + +string PRC_SQLGetData(int iCol) +{ + string sResultSet = GetLocalString(GetModule(), "NWNX_ODBC_CurrentRow"); + int iPos = FindSubString(sResultSet, "¬"); + if (iCol == 1) + { + // only one column returned ? Then we are finished + if (iPos == -1) return sResultSet; + // more than one column returned ? Then return first column + else return GetStringLeft(sResultSet, iPos); + } + // more than one column requested, but only one returned ? Something went wrong; return empty string + else if (iPos == -1) return ""; + + // find column in current row + int iCount = 0; + int nLength = GetStringLength(sResultSet); + + // loop through columns until found + while (iCount++ != iCol) + { + if (iCount == iCol) + return GetStringLeft(sResultSet, iPos); + + // pull off the previous column and find new column marker + nLength -= (iPos + 1); + sResultSet = GetStringRight(sResultSet, nLength); + iPos = FindSubString(sResultSet, "¬"); + + // special case: last column in row + if (iPos == -1) return sResultSet; + } + + return sResultSet; +} + +string ReplaceSingleChars(string sString, string sTarget, string sReplace) +{ + if (FindSubString(sString, sTarget) == -1) // not found + return sString; + + int i; + string sReturn = ""; + string sChar; + + // Loop over every character and replace special characters + for (i = 0; i < GetStringLength(sString); i++) + { + sChar = GetSubString(sString, i, 1); + if (sChar == sTarget) + sReturn += sReplace; + else + sReturn += sChar; + } + return sReturn; +} + +// only needed for SQLite; commits (actually stores) the changed to the database +void PRC_SQLiteCommit() +{ + PRC_SQLExecDirect("COMMIT"); + PRC_SQLExecDirect("BEGIN IMMEDIATE"); +} + +// starts the pseudo heartbeat for committing SQLite DB +void StartSQLiteCommitHB() +{ + if (GetPRCSwitch(PRC_DB_SQLITE)) + { + int nInterval = GetPRCSwitch(PRC_DB_SQLITE_INTERVAL); + DelayCommand(nInterval ? IntToFloat(nInterval) : 600.0f, SQLiteCommitHB()); + } +} + +// pseudo heartbeat for committing SQLite DB; interval given in switch PRC_DB_SQLITE_INTERVAL +// default is 600 seconds (= 10 min) +void SQLiteCommitHB() +{ + // check if we are still using SQLite + if (GetPRCSwitch(PRC_DB_SQLITE)) + { + // do the commit + PRC_SQLExecDirect("COMMIT"); + PRC_SQLExecDirect("BEGIN IMMEDIATE"); + + // continue pseudo heartbeat + int nInterval = GetPRCSwitch(PRC_DB_SQLITE_INTERVAL); + DelayCommand(nInterval ? IntToFloat(nInterval) : 600.0f, SQLiteCommitHB()); + } +} + +void PRC_SetPersistentObject(object oOwner, string sVarName, object oObject, int iExpiration = 0, string sTable = "pwobjdata") +{ + string sPlayer; + string sTag; + + if (GetIsPC(oOwner)) + { + sPlayer = ReplaceSingleChars(GetPCPlayerName(oOwner), "'", "~"); + sTag = ReplaceSingleChars(GetName(oOwner), "'", "~"); + } + else + { + sPlayer = "~"; + sTag = GetTag(oOwner); + } + sVarName = ReplaceSingleChars(sVarName, "'", "~"); + + string sSQL = "SELECT player FROM " + sTable + " WHERE player='" + sPlayer + + "' AND tag='" + sTag + "' AND name='" + sVarName + "'"; + PRC_SQLExecDirect(sSQL); + + if (PRC_SQLFetch() == PRC_SQL_SUCCESS) + { + // row exists + sSQL = "UPDATE " + sTable + " SET val=%s,expire=" + IntToString(iExpiration) + + " WHERE player='" + sPlayer + "' AND tag='" + sTag + "' AND name='" + sVarName + "'"; + SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); + StoreCampaignObject ("NWNX", "-", oObject); + } + else + { + // row doesn't exist + sSQL = "INSERT INTO " + sTable + " (player,tag,name,val,expire) VALUES" + + "('" + sPlayer + "','" + sTag + "','" + sVarName + "',%s," + IntToString(iExpiration) + ")"; + SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); + StoreCampaignObject ("NWNX", "-", oObject); + } +} + +object PRC_GetPersistentObject(object oObject, string sVarName, object oOwner = OBJECT_INVALID, string sTable = "pwobjdata") +{ + string sPlayer; + string sTag; + object oModule; + + if (GetIsPC(oObject)) + { + sPlayer = ReplaceSingleChars(GetPCPlayerName(oObject), "'", "~"); + sTag = ReplaceSingleChars(GetName(oObject), "'", "~"); + } + else + { + sPlayer = "~"; + sTag = GetTag(oObject); + } + sVarName = ReplaceSingleChars(sVarName, "'", "~"); + + string sSQL = "SELECT val FROM " + sTable + " WHERE player='" + sPlayer + + "' AND tag='" + sTag + "' AND name='" + sVarName + "'"; + SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); + + if (!GetIsObjectValid(oOwner)) + oOwner = oObject; + return RetrieveCampaignObject ("NWNX", "-", GetLocation(oOwner), oOwner); +} + +////////////////////////////////////////////////// +/* Functions for portable persistent chest */ +////////////////////////////////////////////////// + +void DeleteXChestDB() +{ + PRC_SQLExecDirect("DROP TABLE "+XCHST_DB); +} + +void CreateXChestDB_SQLite() +{ + PRC_SQLExecDirect("CREATE TABLE "+XCHST_DB+" (" + + "player varchar(64) NOT NULL default '~'," + + "tag varchar(64) NOT NULL default '~'," + + "name varchar(64) NOT NULL default '~'," + + "val blob," + + "expire int(11) default NULL," + + "last timestamp NOT NULL default current_timestamp," + + "PRIMARY KEY (player,tag,name)" + + ")"); +} + +void CreateXChestDB_MySQL() +{ + PRC_SQLExecDirect("CREATE TABLE "+XCHST_DB+" (" + + "player varchar(64) NOT NULL default '~'," + + "tag varchar(64) NOT NULL default '~'," + + "name varchar(64) NOT NULL default '~'," + + "val blob," + + "expire int(11) default NULL," + + "last timestamp NOT NULL default CURRENT_TIMESTAMP," + + "PRIMARY KEY (player,tag,name)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1;"); +} + +//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_switch_setup.nss b/src/include/inc_switch_setup.nss new file mode 100644 index 0000000..afffaae --- /dev/null +++ b/src/include/inc_switch_setup.nss @@ -0,0 +1,1123 @@ +/** @file inc_switch_setup + Functions for switch setups. Most only used on + mod load. Get/Set individual switches is done + via prc_inc_switch. + DO NOT include this file if you just want to + use Set/GetPRCSwitch as it's not needed. +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Controls which itemproperties can be added to + * the samurai weapon. + * @see PRC_SAMURAI_BAN_ in prc_inc_switch and the + * function difinition for more info. + */ +void DoSamuraiBanDefaults(); + +/** + * Sets the epic spell switches to their default values. + * + * If PRC_EPIC_INGORE_DEFAULTS is set, this does nothing. + */ +void DoEpicSpellDefaults(); + +/** + * Sets the file end markers to their default values. + * + * If FILE_END_MANUAL is set, this does nothing. + */ +void SetDefaultFileEnds(); + +/** + * This creates an array of all switch names on a waypoint + * It is used for the switch setting convo to loop over switches easily + */ +void CreateSwitchNameArray(); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "prc_inc_array" // Needs direct include instead of inc_utility +#include "prc_inc_switch" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void DoSamuraiBanDefaults() +{ + if(GetPRCSwitch(PRC_SAMURAI_DISABLE_DEFAULT_BAN)) + return; + //remove all penalty iprops + SetPRCSwitch(PRC_SAMURAI_BAN_+"10_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"21_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"24_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"27_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"28_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"29_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"47_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"49_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"50_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"60_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"62_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"63_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"64_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"65_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"66_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"81_*_*_*", TRUE); + //PRCs restrictions + SetPRCSwitch(PRC_SAMURAI_BAN_+"86_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"87_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"88_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"89_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"90_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"91_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"120_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"121_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"122_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"123_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"124_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"125_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"126_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"127_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"134_*_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"150_*_*_*", TRUE); + //only allow elemental damages 6,7,9,10,13 + //damage + SetPRCSwitch(PRC_SAMURAI_BAN_+"16_5_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"16_8_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"16_11_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"16_12_*_*", TRUE); + //damage vs race + SetPRCSwitch(PRC_SAMURAI_BAN_+"17_*_5_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"17_*_8_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"17_*_11_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"17_*_12_*", TRUE); + //damage vs alignment + SetPRCSwitch(PRC_SAMURAI_BAN_+"18_*_5_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"18_*_8_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"18_*_11_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"18_*_12_*", TRUE); + //damage vs specific alignment + SetPRCSwitch(PRC_SAMURAI_BAN_+"19_*_5_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"19_*_8_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"19_*_11_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"19_*_12_*", TRUE); + //damage immunity + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_5_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_8_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_11_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_12_*_*", TRUE); + //damage resist + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_5_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_8_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_11_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"20_12_*_*", TRUE); + //slays + SetPRCSwitch(PRC_SAMURAI_BAN_+"48_21_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"48_22_*_*", TRUE); + SetPRCSwitch(PRC_SAMURAI_BAN_+"48_23_*_*", TRUE); + //vorpal + SetPRCSwitch(PRC_SAMURAI_BAN_+"48_24_*_*", TRUE); +} + +void DoEpicSpellDefaults() +{ + if(GetPRCSwitch(PRC_EPIC_INGORE_DEFAULTS)) + return; + SetPRCSwitch(PRC_EPIC_XP_COSTS, TRUE); + SetPRCSwitch(PRC_EPIC_BACKLASH_DAMAGE, TRUE); + SetPRCSwitch(PRC_EPIC_FOCI_ADJUST_DC, TRUE); + SetPRCSwitch(PRC_EPIC_GOLD_MULTIPLIER, 9000); + SetPRCSwitch(PRC_EPIC_XP_FRACTION, 25); + SetPRCSwitch(PRC_EPIC_FAILURE_FRACTION_GOLD, 2); + SetPRCSwitch(PRC_EPIC_BOOK_DESTRUCTION, 50); +} + +void SetDefaultFileEnds() +{ + // Overridden values: (TMI prevention) + // + // feat.2da - 390 + // spells.2da - 539 + + //START AUTO-GENERATED FILEENDS + SetPRCSwitch("PRC_FILE_END_actions", 43); + SetPRCSwitch("PRC_FILE_END_ambientmusic", 95); + SetPRCSwitch("PRC_FILE_END_ambientsound", 113); + SetPRCSwitch("PRC_FILE_END_ammunitiontypes", 35); + SetPRCSwitch("PRC_FILE_END_appearance", 870); + SetPRCSwitch("PRC_FILE_END_appearancesndset", 31); + SetPRCSwitch("PRC_FILE_END_areaeffects", 2); + SetPRCSwitch("PRC_FILE_END_armor", 8); + SetPRCSwitch("PRC_FILE_END_armorparts", 0); + SetPRCSwitch("PRC_FILE_END_armourtypes", 42); + SetPRCSwitch("PRC_FILE_END_baseitems", 204); + SetPRCSwitch("PRC_FILE_END_bodybag", 6); + SetPRCSwitch("PRC_FILE_END_caarmorclass", 7); + SetPRCSwitch("PRC_FILE_END_capart", 18); + SetPRCSwitch("PRC_FILE_END_categories", 22); + SetPRCSwitch("PRC_FILE_END_catype", 4); + SetPRCSwitch("PRC_FILE_END_chargenclothes", 15); + SetPRCSwitch("PRC_FILE_END_classes", 254); + SetPRCSwitch("PRC_FILE_END_cloakmodel", 16); + SetPRCSwitch("PRC_FILE_END_cls_atk_1", 59); + SetPRCSwitch("PRC_FILE_END_cls_atk_2", 59); + SetPRCSwitch("PRC_FILE_END_cls_atk_3", 59); + SetPRCSwitch("PRC_FILE_END_cls_atk_4", 59); + SetPRCSwitch("PRC_FILE_END_cls_atk_adst", 59); + SetPRCSwitch("PRC_FILE_END_cls_invkn_dfa", 59); + SetPRCSwitch("PRC_FILE_END_cls_invkn_warlok", 59); + SetPRCSwitch("PRC_FILE_END_cls_inv_dfa", 43); + SetPRCSwitch("PRC_FILE_END_cls_inv_warlok", 112); + SetPRCSwitch("PRC_FILE_END_cls_ivcr_dfa", 22); + SetPRCSwitch("PRC_FILE_END_cls_ivcr_warlok", 93); + SetPRCSwitch("PRC_FILE_END_cls_move_crusdr", 211); + SetPRCSwitch("PRC_FILE_END_cls_move_swdsge", 211); + SetPRCSwitch("PRC_FILE_END_cls_move_warbld", 211); + SetPRCSwitch("PRC_FILE_END_cls_mvcr_crusdr", 207); + SetPRCSwitch("PRC_FILE_END_cls_mvcr_swdsge", 207); + SetPRCSwitch("PRC_FILE_END_cls_mvcr_warbld", 207); + SetPRCSwitch("PRC_FILE_END_cls_mvkn_crusdr", 59); + SetPRCSwitch("PRC_FILE_END_cls_mvkn_swdsge", 59); + SetPRCSwitch("PRC_FILE_END_cls_mvkn_warbld", 59); + SetPRCSwitch("PRC_FILE_END_cls_psbk_foz", 59); + SetPRCSwitch("PRC_FILE_END_cls_psbk_psion", 59); + SetPRCSwitch("PRC_FILE_END_cls_psbk_psywar", 59); + SetPRCSwitch("PRC_FILE_END_cls_psbk_warmnd", 59); + SetPRCSwitch("PRC_FILE_END_cls_psbk_wilder", 59); + SetPRCSwitch("PRC_FILE_END_cls_psicr_foz", 154); + SetPRCSwitch("PRC_FILE_END_cls_psicr_psion", 196); + SetPRCSwitch("PRC_FILE_END_cls_psicr_psywar", 169); + SetPRCSwitch("PRC_FILE_END_cls_psicr_warmnd", 154); + SetPRCSwitch("PRC_FILE_END_cls_psicr_wilder", 196); + SetPRCSwitch("PRC_FILE_END_cls_psipw_foz", 226); + SetPRCSwitch("PRC_FILE_END_cls_psipw_psion", 285); + SetPRCSwitch("PRC_FILE_END_cls_psipw_psywar", 246); + SetPRCSwitch("PRC_FILE_END_cls_psipw_warmnd", 226); + SetPRCSwitch("PRC_FILE_END_cls_psipw_wilder", 285); + SetPRCSwitch("PRC_FILE_END_cls_savthr_barb", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_bard", 60); + SetPRCSwitch("PRC_FILE_END_cls_savthr_cler", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_cons", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_dru", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_fight", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_lich", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_monk", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_pal", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_rang", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_rog", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_sorc", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_wild", 59); + SetPRCSwitch("PRC_FILE_END_cls_savthr_wiz", 59); + SetPRCSwitch("PRC_FILE_END_cls_spcr_antipl", 42); + SetPRCSwitch("PRC_FILE_END_cls_spcr_archv", 502); + SetPRCSwitch("PRC_FILE_END_cls_spcr_asasin", 39); + SetPRCSwitch("PRC_FILE_END_cls_spcr_bard", 144); + SetPRCSwitch("PRC_FILE_END_cls_spcr_beguil", 142); + SetPRCSwitch("PRC_FILE_END_cls_spcr_blkgrd", 47); + SetPRCSwitch("PRC_FILE_END_cls_spcr_dnecro", 137); + SetPRCSwitch("PRC_FILE_END_cls_spcr_duskbl", 69); + SetPRCSwitch("PRC_FILE_END_cls_spcr_favsol", 290); + SetPRCSwitch("PRC_FILE_END_cls_spcr_harper", 35); + SetPRCSwitch("PRC_FILE_END_cls_spcr_healer", 77); + SetPRCSwitch("PRC_FILE_END_cls_spcr_hexbl", 73); + SetPRCSwitch("PRC_FILE_END_cls_spcr_justww", 34); + SetPRCSwitch("PRC_FILE_END_cls_spcr_kchal", 48); + SetPRCSwitch("PRC_FILE_END_cls_spcr_kotmc", 26); + SetPRCSwitch("PRC_FILE_END_cls_spcr_myst", 290); + SetPRCSwitch("PRC_FILE_END_cls_spcr_ocu", 209); + SetPRCSwitch("PRC_FILE_END_cls_spcr_schord", 244); + SetPRCSwitch("PRC_FILE_END_cls_spcr_shaman", 205); + SetPRCSwitch("PRC_FILE_END_cls_spcr_sod", 33); + SetPRCSwitch("PRC_FILE_END_cls_spcr_sohei", 57); + SetPRCSwitch("PRC_FILE_END_cls_spcr_sol", 38); + SetPRCSwitch("PRC_FILE_END_cls_spcr_sorc", 430); + SetPRCSwitch("PRC_FILE_END_cls_spcr_suel", 117); + SetPRCSwitch("PRC_FILE_END_cls_spcr_templ", 112); + SetPRCSwitch("PRC_FILE_END_cls_spcr_tfshad", 27); + SetPRCSwitch("PRC_FILE_END_cls_spcr_vassal", 31); + SetPRCSwitch("PRC_FILE_END_cls_spcr_vigil", 41); + SetPRCSwitch("PRC_FILE_END_cls_spcr_witch", 252); + SetPRCSwitch("PRC_FILE_END_cls_spcr_wrmage", 135); + SetPRCSwitch("PRC_FILE_END_cls_spell_antipl", 115); + SetPRCSwitch("PRC_FILE_END_cls_spell_archv", 2720); + SetPRCSwitch("PRC_FILE_END_cls_spell_asasin", 52); + SetPRCSwitch("PRC_FILE_END_cls_spell_bard", 169); + SetPRCSwitch("PRC_FILE_END_cls_spell_beguil", 119); + SetPRCSwitch("PRC_FILE_END_cls_spell_blkgrd", 163); + SetPRCSwitch("PRC_FILE_END_cls_spell_dnecro", 134); + SetPRCSwitch("PRC_FILE_END_cls_spell_duskbl", 84); + SetPRCSwitch("PRC_FILE_END_cls_spell_favsol", 363); + SetPRCSwitch("PRC_FILE_END_cls_spell_harper", 21); + SetPRCSwitch("PRC_FILE_END_cls_spell_healer", 271); + SetPRCSwitch("PRC_FILE_END_cls_spell_hexbl", 79); + SetPRCSwitch("PRC_FILE_END_cls_spell_justww", 26); + SetPRCSwitch("PRC_FILE_END_cls_spell_kchal", 137); + SetPRCSwitch("PRC_FILE_END_cls_spell_kotmc", 70); + SetPRCSwitch("PRC_FILE_END_cls_spell_myst", 363); + SetPRCSwitch("PRC_FILE_END_cls_spell_ocu", 905); + SetPRCSwitch("PRC_FILE_END_cls_spell_schord", 308); + SetPRCSwitch("PRC_FILE_END_cls_spell_shaman", 667); + SetPRCSwitch("PRC_FILE_END_cls_spell_sod", 110); + SetPRCSwitch("PRC_FILE_END_cls_spell_sohei", 131); + SetPRCSwitch("PRC_FILE_END_cls_spell_sol", 114); + SetPRCSwitch("PRC_FILE_END_cls_spell_sorc", 541); + SetPRCSwitch("PRC_FILE_END_cls_spell_suel", 160); + SetPRCSwitch("PRC_FILE_END_cls_spell_templ", 95); + SetPRCSwitch("PRC_FILE_END_cls_spell_tfshad", 70); + SetPRCSwitch("PRC_FILE_END_cls_spell_vassal", 104); + SetPRCSwitch("PRC_FILE_END_cls_spell_vigil", 75); + SetPRCSwitch("PRC_FILE_END_cls_spell_witch", 193); + SetPRCSwitch("PRC_FILE_END_cls_spell_wrmage", 147); + SetPRCSwitch("PRC_FILE_END_cls_spgn_antipl", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_archv", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_asasin", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_bard", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_beguil", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_blkgrd", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_cler", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_dnecro", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_dru", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_duskbl", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_favsol", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_harper", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_healer", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_hexbl", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_justww", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_kchal", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_kotmc", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_myst", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_ocu", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_pal", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_rang", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_schord", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_shaman", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_sod", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_sohei", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_sol", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_sorc", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_suel", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_templ", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_tfshad", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_vassal", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_vigil", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_witch", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_wiz", 59); + SetPRCSwitch("PRC_FILE_END_cls_spgn_wrmage", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_asasin", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_bard", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_beguil", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_dnecro", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_duskbl", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_favsol", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_harper", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_hexbl", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_justww", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_myst", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_schord", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_sorc", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_suel", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_templ", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_witch", 59); + SetPRCSwitch("PRC_FILE_END_cls_spkn_wrmage", 59); + SetPRCSwitch("PRC_FILE_END_cls_true_known", 39); + SetPRCSwitch("PRC_FILE_END_cls_true_maxlvl", 39); + SetPRCSwitch("PRC_FILE_END_cls_true_utter", 166); + SetPRCSwitch("PRC_FILE_END_colours", 175); + SetPRCSwitch("PRC_FILE_END_combatmodes", 3); + SetPRCSwitch("PRC_FILE_END_craft_armour", 63); + SetPRCSwitch("PRC_FILE_END_craft_golem", 40); + SetPRCSwitch("PRC_FILE_END_craft_ring", 41); + SetPRCSwitch("PRC_FILE_END_craft_weapon", 46); + SetPRCSwitch("PRC_FILE_END_craft_wondrous", 115); + SetPRCSwitch("PRC_FILE_END_creaturesize", 5); + SetPRCSwitch("PRC_FILE_END_creaturespeed", 8); + SetPRCSwitch("PRC_FILE_END_crtemplates", 10); + SetPRCSwitch("PRC_FILE_END_cursors", 10); + SetPRCSwitch("PRC_FILE_END_damagehitvisual", 11); + SetPRCSwitch("PRC_FILE_END_damagelevels", 5); + SetPRCSwitch("PRC_FILE_END_defaultacsounds", 8); + SetPRCSwitch("PRC_FILE_END_des_blumburg", 17); + SetPRCSwitch("PRC_FILE_END_des_conf_treas", 1); + SetPRCSwitch("PRC_FILE_END_des_crft_amat", 1); + SetPRCSwitch("PRC_FILE_END_des_crft_aparts", 17); + SetPRCSwitch("PRC_FILE_END_des_crft_appear", 53); + SetPRCSwitch("PRC_FILE_END_des_crft_armor", 41); + SetPRCSwitch("PRC_FILE_END_des_crft_bmat", 13); + SetPRCSwitch("PRC_FILE_END_des_crft_drop", 476); + SetPRCSwitch("PRC_FILE_END_des_crft_mat", 2); + SetPRCSwitch("PRC_FILE_END_des_crft_poison", 100); + SetPRCSwitch("PRC_FILE_END_des_crft_props", 27); + SetPRCSwitch("PRC_FILE_END_des_crft_scroll", 3999); + SetPRCSwitch("PRC_FILE_END_des_crft_spells", 19348); + SetPRCSwitch("PRC_FILE_END_des_crft_weapon", 29); + SetPRCSwitch("PRC_FILE_END_des_cutconvdur", 26); + SetPRCSwitch("PRC_FILE_END_des_feat2item", 1000); + SetPRCSwitch("PRC_FILE_END_des_matcomp", 510); + SetPRCSwitch("PRC_FILE_END_des_mechupgrades", 6); + SetPRCSwitch("PRC_FILE_END_des_pcstart_arm", 1); + SetPRCSwitch("PRC_FILE_END_des_pcstart_weap", 1); + SetPRCSwitch("PRC_FILE_END_des_prayer", 9); + SetPRCSwitch("PRC_FILE_END_des_restsystem", 21); + SetPRCSwitch("PRC_FILE_END_des_treas_ammo", 28); + SetPRCSwitch("PRC_FILE_END_des_treas_disp", 417); + SetPRCSwitch("PRC_FILE_END_des_treas_enh", 60); + SetPRCSwitch("PRC_FILE_END_des_treas_gold", 8); + SetPRCSwitch("PRC_FILE_END_des_treas_items", 15); + SetPRCSwitch("PRC_FILE_END_des_xp_rewards", 220); + SetPRCSwitch("PRC_FILE_END_diffsettings", 6); + SetPRCSwitch("PRC_FILE_END_disease", 62); + SetPRCSwitch("PRC_FILE_END_dmgxp", 59); + SetPRCSwitch("PRC_FILE_END_domains", 59); + SetPRCSwitch("PRC_FILE_END_doortype", 2); + SetPRCSwitch("PRC_FILE_END_doortypes", 238); + SetPRCSwitch("PRC_FILE_END_ECL", 254); + SetPRCSwitch("PRC_FILE_END_effectanim", 0); + SetPRCSwitch("PRC_FILE_END_effecticons", 129); + SetPRCSwitch("PRC_FILE_END_encdifficulty", 4); + SetPRCSwitch("PRC_FILE_END_encumbrance", 255); + SetPRCSwitch("PRC_FILE_END_environment", 25); + SetPRCSwitch("PRC_FILE_END_epicattacks", 235); + SetPRCSwitch("PRC_FILE_END_epicsaves", 59); + SetPRCSwitch("PRC_FILE_END_epicspells", 70); + SetPRCSwitch("PRC_FILE_END_epicspellseeds", 27); + SetPRCSwitch("PRC_FILE_END_excitedduration", 2); + SetPRCSwitch("PRC_FILE_END_exptable", 40); + SetPRCSwitch("PRC_FILE_END_feat", 24439); + SetPRCSwitch("PRC_FILE_END_fileends", 20); + SetPRCSwitch("PRC_FILE_END_footstepsounds", 17); + SetPRCSwitch("PRC_FILE_END_fractionalcr", 4); + SetPRCSwitch("PRC_FILE_END_gamespyrooms", 12); + SetPRCSwitch("PRC_FILE_END_gender", 4); + SetPRCSwitch("PRC_FILE_END_genericdoors", 25); + SetPRCSwitch("PRC_FILE_END_hen_companion", 18); + SetPRCSwitch("PRC_FILE_END_hen_familiar", 12); + SetPRCSwitch("PRC_FILE_END_inventorysnds", 32); + SetPRCSwitch("PRC_FILE_END_iprp_abilities", 5); + SetPRCSwitch("PRC_FILE_END_iprp_acmodtype", 4); + SetPRCSwitch("PRC_FILE_END_iprp_addcost", 1); + SetPRCSwitch("PRC_FILE_END_iprp_additional", 1); + SetPRCSwitch("PRC_FILE_END_iprp_aligngrp", 5); + SetPRCSwitch("PRC_FILE_END_iprp_alignment", 8); + SetPRCSwitch("PRC_FILE_END_iprp_ammocost", 15); + SetPRCSwitch("PRC_FILE_END_iprp_ammotype", 2); + SetPRCSwitch("PRC_FILE_END_iprp_amount", 4); + SetPRCSwitch("PRC_FILE_END_iprp_aoe", 7); + SetPRCSwitch("PRC_FILE_END_iprp_arcspell", 19); + SetPRCSwitch("PRC_FILE_END_iprp_base1", -1); + SetPRCSwitch("PRC_FILE_END_iprp_bladecost", 5); + SetPRCSwitch("PRC_FILE_END_iprp_bonuscost", 12); + SetPRCSwitch("PRC_FILE_END_iprp_casterlvl", 60); + SetPRCSwitch("PRC_FILE_END_iprp_chargecost", 13); + SetPRCSwitch("PRC_FILE_END_iprp_color", 6); + SetPRCSwitch("PRC_FILE_END_iprp_combatdam", 2); + SetPRCSwitch("PRC_FILE_END_iprp_costtable", 39); + SetPRCSwitch("PRC_FILE_END_iprp_damagecost", 70); + SetPRCSwitch("PRC_FILE_END_iprp_damagetype", 13); + SetPRCSwitch("PRC_FILE_END_iprp_damvulcost", 7); + SetPRCSwitch("PRC_FILE_END_iprp_decvalue1", 9); + SetPRCSwitch("PRC_FILE_END_iprp_decvalue2", 9); + SetPRCSwitch("PRC_FILE_END_iprp_feats", 24819); + SetPRCSwitch("PRC_FILE_END_iprp_immuncost", 7); + SetPRCSwitch("PRC_FILE_END_iprp_immunity", 9); + SetPRCSwitch("PRC_FILE_END_iprp_incvalue1", 9); + SetPRCSwitch("PRC_FILE_END_iprp_incvalue2", 9); + SetPRCSwitch("PRC_FILE_END_iprp_kitcost", 50); + SetPRCSwitch("PRC_FILE_END_iprp_lightcost", 4); + SetPRCSwitch("PRC_FILE_END_iprp_matcost", 77); + SetPRCSwitch("PRC_FILE_END_iprp_material", 77); + SetPRCSwitch("PRC_FILE_END_iprp_maxpp", 8); + SetPRCSwitch("PRC_FILE_END_iprp_meleecost", 20); + SetPRCSwitch("PRC_FILE_END_iprp_metamagic", 6); + SetPRCSwitch("PRC_FILE_END_iprp_monstcost", 58); + SetPRCSwitch("PRC_FILE_END_iprp_monsterdam", 14); + SetPRCSwitch("PRC_FILE_END_iprp_monsterhit", 9); + SetPRCSwitch("PRC_FILE_END_iprp_neg10cost", 50); + SetPRCSwitch("PRC_FILE_END_iprp_neg5cost", 10); + SetPRCSwitch("PRC_FILE_END_iprp_onhit", 25); + SetPRCSwitch("PRC_FILE_END_iprp_onhitcost", 70); + SetPRCSwitch("PRC_FILE_END_iprp_onhitdur", 27); + SetPRCSwitch("PRC_FILE_END_iprp_onhitspell", 209); + SetPRCSwitch("PRC_FILE_END_iprp_paramtable", 11); + SetPRCSwitch("PRC_FILE_END_iprp_poison", 5); + SetPRCSwitch("PRC_FILE_END_iprp_protection", 19); + SetPRCSwitch("PRC_FILE_END_iprp_qualcost", 15); + SetPRCSwitch("PRC_FILE_END_iprp_quality", 15); + SetPRCSwitch("PRC_FILE_END_iprp_redcost", 5); + SetPRCSwitch("PRC_FILE_END_iprp_resistcost", 28); + SetPRCSwitch("PRC_FILE_END_iprp_saveelement", 21); + SetPRCSwitch("PRC_FILE_END_iprp_savingthrow", 3); + SetPRCSwitch("PRC_FILE_END_iprp_skillcost", 50); + SetPRCSwitch("PRC_FILE_END_iprp_slotscost", -1); + SetPRCSwitch("PRC_FILE_END_iprp_soakcost", 50); + SetPRCSwitch("PRC_FILE_END_iprp_speed_dec", 9); + SetPRCSwitch("PRC_FILE_END_iprp_speed_enh", 9); + SetPRCSwitch("PRC_FILE_END_iprp_spellcost", 243); + SetPRCSwitch("PRC_FILE_END_iprp_spellcstr", 42); + SetPRCSwitch("PRC_FILE_END_iprp_spelllvcost", 9); + SetPRCSwitch("PRC_FILE_END_iprp_spelllvlimm", 9); + SetPRCSwitch("PRC_FILE_END_iprp_spells", 1456); + SetPRCSwitch("PRC_FILE_END_iprp_spellshl", 7); + SetPRCSwitch("PRC_FILE_END_iprp_srcost", 99); + SetPRCSwitch("PRC_FILE_END_iprp_staminacost", -1); + SetPRCSwitch("PRC_FILE_END_iprp_storedpp", 16); + SetPRCSwitch("PRC_FILE_END_iprp_terraintype", -1); + SetPRCSwitch("PRC_FILE_END_iprp_trapcost", 11); + SetPRCSwitch("PRC_FILE_END_iprp_traps", 4); + SetPRCSwitch("PRC_FILE_END_iprp_trapsize", 3); + SetPRCSwitch("PRC_FILE_END_iprp_visualfx", 6); + SetPRCSwitch("PRC_FILE_END_iprp_walk", 1); + SetPRCSwitch("PRC_FILE_END_iprp_weightcost", 6); + SetPRCSwitch("PRC_FILE_END_iprp_weightinc", 5); + SetPRCSwitch("PRC_FILE_END_itempropdef", 199); + SetPRCSwitch("PRC_FILE_END_itemprops", 199); + SetPRCSwitch("PRC_FILE_END_itemvalue", 59); + SetPRCSwitch("PRC_FILE_END_itmwizammo", 54); + SetPRCSwitch("PRC_FILE_END_itmwizarmor", 72); + SetPRCSwitch("PRC_FILE_END_itmwizhelmet", 71); + SetPRCSwitch("PRC_FILE_END_itmwizmelee", 47); + SetPRCSwitch("PRC_FILE_END_itmwizpotion", 38); + SetPRCSwitch("PRC_FILE_END_itmwizranged", 46); + SetPRCSwitch("PRC_FILE_END_itmwizrods", 41); + SetPRCSwitch("PRC_FILE_END_itmwizscroll", 38); + SetPRCSwitch("PRC_FILE_END_itmwizstaves", 60); + SetPRCSwitch("PRC_FILE_END_itmwizthrow", 48); + SetPRCSwitch("PRC_FILE_END_itmwiztrap", 43); + SetPRCSwitch("PRC_FILE_END_itmwizwands", 38); + SetPRCSwitch("PRC_FILE_END_keymap", 70); + SetPRCSwitch("PRC_FILE_END_lightcolor", 32); + SetPRCSwitch("PRC_FILE_END_loadhints", 88); + SetPRCSwitch("PRC_FILE_END_loadscreens", 259); + SetPRCSwitch("PRC_FILE_END_masterfeats", 113); + SetPRCSwitch("PRC_FILE_END_materialcomp", 200); + SetPRCSwitch("PRC_FILE_END_metamagic", 6); + SetPRCSwitch("PRC_FILE_END_namefilter", 3); + SetPRCSwitch("PRC_FILE_END_nwconfig", 6); + SetPRCSwitch("PRC_FILE_END_nwconfig2", 6); + SetPRCSwitch("PRC_FILE_END_packeqbarb1", 5); + SetPRCSwitch("PRC_FILE_END_packeqbarb3", 7); + SetPRCSwitch("PRC_FILE_END_packeqbarb4", 4); + SetPRCSwitch("PRC_FILE_END_packeqbarb5", 5); + SetPRCSwitch("PRC_FILE_END_packeqbard1", 11); + SetPRCSwitch("PRC_FILE_END_packeqcler1", 6); + SetPRCSwitch("PRC_FILE_END_packeqcler2", 6); + SetPRCSwitch("PRC_FILE_END_packeqcler3", 6); + SetPRCSwitch("PRC_FILE_END_packeqcler4", 6); + SetPRCSwitch("PRC_FILE_END_packeqcler5", 6); + SetPRCSwitch("PRC_FILE_END_packeqdruid1", 5); + SetPRCSwitch("PRC_FILE_END_packeqfight1", 5); + SetPRCSwitch("PRC_FILE_END_packeqfight2", 7); + SetPRCSwitch("PRC_FILE_END_packeqfight6", 5); + SetPRCSwitch("PRC_FILE_END_packeqfightc", 5); + SetPRCSwitch("PRC_FILE_END_packeqmonk1", 5); + SetPRCSwitch("PRC_FILE_END_packeqmonk2", 6); + SetPRCSwitch("PRC_FILE_END_packeqmonk4", 5); + SetPRCSwitch("PRC_FILE_END_packeqmonk5", 6); + SetPRCSwitch("PRC_FILE_END_packeqpala1", 6); + SetPRCSwitch("PRC_FILE_END_packeqpala2", 7); + SetPRCSwitch("PRC_FILE_END_packeqpala3", 8); + SetPRCSwitch("PRC_FILE_END_packeqrang1", 8); + SetPRCSwitch("PRC_FILE_END_packeqrang2", 8); + SetPRCSwitch("PRC_FILE_END_packeqrang3", 8); + SetPRCSwitch("PRC_FILE_END_packeqrang4", 8); + SetPRCSwitch("PRC_FILE_END_packeqrang5", 8); + SetPRCSwitch("PRC_FILE_END_packeqrog1", 8); + SetPRCSwitch("PRC_FILE_END_packeqrog2", 8); + SetPRCSwitch("PRC_FILE_END_packeqrog3", 8); + SetPRCSwitch("PRC_FILE_END_packeqrog5", 8); + SetPRCSwitch("PRC_FILE_END_packeqrogd", 8); + SetPRCSwitch("PRC_FILE_END_packeqsor1", 11); + SetPRCSwitch("PRC_FILE_END_packeqwiz1", 11); + SetPRCSwitch("PRC_FILE_END_packeqwizb", 11); + SetPRCSwitch("PRC_FILE_END_packftarch", 168); + SetPRCSwitch("PRC_FILE_END_packftassa", 220); + SetPRCSwitch("PRC_FILE_END_packftbarb1", 222); + SetPRCSwitch("PRC_FILE_END_packftbarb2", 219); + SetPRCSwitch("PRC_FILE_END_packftbarb3", 220); + SetPRCSwitch("PRC_FILE_END_packftbarb4", 222); + SetPRCSwitch("PRC_FILE_END_packftbarb5", 221); + SetPRCSwitch("PRC_FILE_END_packftbarbf", 37); + SetPRCSwitch("PRC_FILE_END_packftbard1", 177); + SetPRCSwitch("PRC_FILE_END_packftbard2", 173); + SetPRCSwitch("PRC_FILE_END_packftbard3", 176); + SetPRCSwitch("PRC_FILE_END_packftbard4", 169); + SetPRCSwitch("PRC_FILE_END_packftbard5", 168); + SetPRCSwitch("PRC_FILE_END_packftbard6", 67); + SetPRCSwitch("PRC_FILE_END_packftbardg", 42); + SetPRCSwitch("PRC_FILE_END_packftblck", 335); + SetPRCSwitch("PRC_FILE_END_packftcler1", 161); + SetPRCSwitch("PRC_FILE_END_packftcler2", 166); + SetPRCSwitch("PRC_FILE_END_packftcler3", 165); + SetPRCSwitch("PRC_FILE_END_packftcler4", 165); + SetPRCSwitch("PRC_FILE_END_packftcler5", 163); + SetPRCSwitch("PRC_FILE_END_packftcler6", 160); + SetPRCSwitch("PRC_FILE_END_packftclere", 46); + SetPRCSwitch("PRC_FILE_END_packftcrea1", 23); + SetPRCSwitch("PRC_FILE_END_packftdrdis", 254); + SetPRCSwitch("PRC_FILE_END_packftdruid1", 196); + SetPRCSwitch("PRC_FILE_END_packftdruid2", 204); + SetPRCSwitch("PRC_FILE_END_packftdruid3", 199); + SetPRCSwitch("PRC_FILE_END_packftdruid4", 198); + SetPRCSwitch("PRC_FILE_END_packftdruid5", 198); + SetPRCSwitch("PRC_FILE_END_packftdruid6", 196); + SetPRCSwitch("PRC_FILE_END_packftdwdef", 337); + SetPRCSwitch("PRC_FILE_END_packftfight1", 382); + SetPRCSwitch("PRC_FILE_END_packftfight2", 383); + SetPRCSwitch("PRC_FILE_END_packftfight3", 383); + SetPRCSwitch("PRC_FILE_END_packftfight4", 383); + SetPRCSwitch("PRC_FILE_END_packftfight5", 382); + SetPRCSwitch("PRC_FILE_END_packftfight6", 383); + SetPRCSwitch("PRC_FILE_END_packftfightc", 45); + SetPRCSwitch("PRC_FILE_END_packftharp", 191); + SetPRCSwitch("PRC_FILE_END_packftmonk1", 275); + SetPRCSwitch("PRC_FILE_END_packftmonk2", 275); + SetPRCSwitch("PRC_FILE_END_packftmonk3", 275); + SetPRCSwitch("PRC_FILE_END_packftmonk4", 275); + SetPRCSwitch("PRC_FILE_END_packftmonk5", 275); + SetPRCSwitch("PRC_FILE_END_packftmonk6", 275); + SetPRCSwitch("PRC_FILE_END_packftpala1", 213); + SetPRCSwitch("PRC_FILE_END_packftpala2", 213); + SetPRCSwitch("PRC_FILE_END_packftpala3", 212); + SetPRCSwitch("PRC_FILE_END_packftpala4", 212); + SetPRCSwitch("PRC_FILE_END_packftpalah", 32); + SetPRCSwitch("PRC_FILE_END_packftrang1", 155); + SetPRCSwitch("PRC_FILE_END_packftrang2", 158); + SetPRCSwitch("PRC_FILE_END_packftrang3", 158); + SetPRCSwitch("PRC_FILE_END_packftrang4", 159); + SetPRCSwitch("PRC_FILE_END_packftrang5", 158); + SetPRCSwitch("PRC_FILE_END_packftrang6", 157); + SetPRCSwitch("PRC_FILE_END_packftrog1", 298); + SetPRCSwitch("PRC_FILE_END_packftrog2", 303); + SetPRCSwitch("PRC_FILE_END_packftrog3", 302); + SetPRCSwitch("PRC_FILE_END_packftrog5", 322); + SetPRCSwitch("PRC_FILE_END_packftrog6", 315); + SetPRCSwitch("PRC_FILE_END_packftrog7", 321); + SetPRCSwitch("PRC_FILE_END_packftrogd", 37); + SetPRCSwitch("PRC_FILE_END_packftshad", 287); + SetPRCSwitch("PRC_FILE_END_packftshift", 148); + SetPRCSwitch("PRC_FILE_END_packftsor1", 174); + SetPRCSwitch("PRC_FILE_END_packftsor2", 157); + SetPRCSwitch("PRC_FILE_END_packftsor3", 157); + SetPRCSwitch("PRC_FILE_END_packftsor4", 157); + SetPRCSwitch("PRC_FILE_END_packftsor5", 157); + SetPRCSwitch("PRC_FILE_END_packftsor6", 172); + SetPRCSwitch("PRC_FILE_END_packftsor7", 170); + SetPRCSwitch("PRC_FILE_END_packftsor8", 174); + SetPRCSwitch("PRC_FILE_END_packftsor9", 199); + SetPRCSwitch("PRC_FILE_END_packftsora", 174); + SetPRCSwitch("PRC_FILE_END_packfttorm", 360); + SetPRCSwitch("PRC_FILE_END_packftwiz1", 174); + SetPRCSwitch("PRC_FILE_END_packftwiz2", 157); + SetPRCSwitch("PRC_FILE_END_packftwiz3", 157); + SetPRCSwitch("PRC_FILE_END_packftwiz4", 157); + SetPRCSwitch("PRC_FILE_END_packftwiz5", 157); + SetPRCSwitch("PRC_FILE_END_packftwiz6", 172); + SetPRCSwitch("PRC_FILE_END_packftwiz7", 170); + SetPRCSwitch("PRC_FILE_END_packftwiz8", 176); + SetPRCSwitch("PRC_FILE_END_packftwiz9", 199); + SetPRCSwitch("PRC_FILE_END_packftwiza", 174); + SetPRCSwitch("PRC_FILE_END_packftwizb", 43); + SetPRCSwitch("PRC_FILE_END_packftwm", 259); + SetPRCSwitch("PRC_FILE_END_packskarch", 10); + SetPRCSwitch("PRC_FILE_END_packskassa", 13); + SetPRCSwitch("PRC_FILE_END_packskbarb1", 20); + SetPRCSwitch("PRC_FILE_END_packskbarb2", 20); + SetPRCSwitch("PRC_FILE_END_packskbarb3", 20); + SetPRCSwitch("PRC_FILE_END_packskbarb4", 20); + SetPRCSwitch("PRC_FILE_END_packskbarb5", 20); + SetPRCSwitch("PRC_FILE_END_packskbarb6", 20); + SetPRCSwitch("PRC_FILE_END_packskbarb7", 8); + SetPRCSwitch("PRC_FILE_END_packskbard1", 22); + SetPRCSwitch("PRC_FILE_END_packskbard2", 22); + SetPRCSwitch("PRC_FILE_END_packskbard3", 22); + SetPRCSwitch("PRC_FILE_END_packskbard4", 22); + SetPRCSwitch("PRC_FILE_END_packskbard5", 22); + SetPRCSwitch("PRC_FILE_END_packskbard6", 22); + SetPRCSwitch("PRC_FILE_END_packskbard7", 12); + SetPRCSwitch("PRC_FILE_END_packskblck", 9); + SetPRCSwitch("PRC_FILE_END_packskcler1", 22); + SetPRCSwitch("PRC_FILE_END_packskcler2", 21); + SetPRCSwitch("PRC_FILE_END_packskcler3", 21); + SetPRCSwitch("PRC_FILE_END_packskcler4", 21); + SetPRCSwitch("PRC_FILE_END_packskcler5", 4); + SetPRCSwitch("PRC_FILE_END_packskcrea1", 4); + SetPRCSwitch("PRC_FILE_END_packskdrdis", 10); + SetPRCSwitch("PRC_FILE_END_packskdruid1", 21); + SetPRCSwitch("PRC_FILE_END_packskdruid2", 21); + SetPRCSwitch("PRC_FILE_END_packskdruid3", 21); + SetPRCSwitch("PRC_FILE_END_packskdruid4", 21); + SetPRCSwitch("PRC_FILE_END_packskdruid5", 22); + SetPRCSwitch("PRC_FILE_END_packskdwdef", 9); + SetPRCSwitch("PRC_FILE_END_packskfight1", 20); + SetPRCSwitch("PRC_FILE_END_packskfight2", 20); + SetPRCSwitch("PRC_FILE_END_packskfight3", 21); + SetPRCSwitch("PRC_FILE_END_packskfight5", 20); + SetPRCSwitch("PRC_FILE_END_packskfight6", 4); + SetPRCSwitch("PRC_FILE_END_packskharp", 11); + SetPRCSwitch("PRC_FILE_END_packskmonk1", 19); + SetPRCSwitch("PRC_FILE_END_packskmonk6", 20); + SetPRCSwitch("PRC_FILE_END_packskpala1", 18); + SetPRCSwitch("PRC_FILE_END_packskpala4", 19); + SetPRCSwitch("PRC_FILE_END_packskpalah", 21); + SetPRCSwitch("PRC_FILE_END_packskrang1", 24); + SetPRCSwitch("PRC_FILE_END_packskrang2", 24); + SetPRCSwitch("PRC_FILE_END_packskrang3", 24); + SetPRCSwitch("PRC_FILE_END_packskrog1", 23); + SetPRCSwitch("PRC_FILE_END_packskrog2", 23); + SetPRCSwitch("PRC_FILE_END_packskrog3", 23); + SetPRCSwitch("PRC_FILE_END_packskrog4", 22); + SetPRCSwitch("PRC_FILE_END_packskrog5", 21); + SetPRCSwitch("PRC_FILE_END_packskrog6", 13); + SetPRCSwitch("PRC_FILE_END_packskrog7", 23); + SetPRCSwitch("PRC_FILE_END_packskshad", 11); + SetPRCSwitch("PRC_FILE_END_packsksor10", 19); + SetPRCSwitch("PRC_FILE_END_packsktorm", 23); + SetPRCSwitch("PRC_FILE_END_packskwiz1", 19); + SetPRCSwitch("PRC_FILE_END_packskwizb", 23); + SetPRCSwitch("PRC_FILE_END_packspbar1", 67); + SetPRCSwitch("PRC_FILE_END_packspbar2", 47); + SetPRCSwitch("PRC_FILE_END_packspbar3", 34); + SetPRCSwitch("PRC_FILE_END_packspcleric1", 56); + SetPRCSwitch("PRC_FILE_END_packspcleric2", 53); + SetPRCSwitch("PRC_FILE_END_packspdruid1", 56); + SetPRCSwitch("PRC_FILE_END_packspnpc1", 101); + SetPRCSwitch("PRC_FILE_END_packsppala1", 26); + SetPRCSwitch("PRC_FILE_END_packsprang1", 63); + SetPRCSwitch("PRC_FILE_END_packspwiz1", 146); + SetPRCSwitch("PRC_FILE_END_packspwiz2", 47); + SetPRCSwitch("PRC_FILE_END_packspwiz3", 47); + SetPRCSwitch("PRC_FILE_END_packspwiz4", 50); + SetPRCSwitch("PRC_FILE_END_packspwiz5", 48); + SetPRCSwitch("PRC_FILE_END_packspwiz6", 47); + SetPRCSwitch("PRC_FILE_END_packspwiz7", 47); + SetPRCSwitch("PRC_FILE_END_packspwiz8", 49); + SetPRCSwitch("PRC_FILE_END_packspwiz9", 44); + SetPRCSwitch("PRC_FILE_END_packspwizb", 35); + SetPRCSwitch("PRC_FILE_END_parts_belt", 200); + SetPRCSwitch("PRC_FILE_END_parts_bicep", 200); + SetPRCSwitch("PRC_FILE_END_parts_chest", 200); + SetPRCSwitch("PRC_FILE_END_parts_foot", 200); + SetPRCSwitch("PRC_FILE_END_parts_forearm", 200); + SetPRCSwitch("PRC_FILE_END_parts_hand", 200); + SetPRCSwitch("PRC_FILE_END_parts_legs", 200); + SetPRCSwitch("PRC_FILE_END_parts_neck", 200); + SetPRCSwitch("PRC_FILE_END_parts_pelvis", 200); + SetPRCSwitch("PRC_FILE_END_parts_robe", 38); + SetPRCSwitch("PRC_FILE_END_parts_shin", 200); + SetPRCSwitch("PRC_FILE_END_parts_shoulder", 200); + SetPRCSwitch("PRC_FILE_END_phenotype", 33); + SetPRCSwitch("PRC_FILE_END_placeableobjsnds", 50); + SetPRCSwitch("PRC_FILE_END_placeables", 846); + SetPRCSwitch("PRC_FILE_END_placeabletypes", 8); + SetPRCSwitch("PRC_FILE_END_poison", 146); + SetPRCSwitch("PRC_FILE_END_poisontypedef", 3); + SetPRCSwitch("PRC_FILE_END_polymorph", 155); + SetPRCSwitch("PRC_FILE_END_portraits", 1300); + SetPRCSwitch("PRC_FILE_END_prc_craft_alchem", 37); + SetPRCSwitch("PRC_FILE_END_prc_craft_gen_it", 204); + SetPRCSwitch("PRC_FILE_END_prc_craft_poison", 62); + SetPRCSwitch("PRC_FILE_END_prc_domains", 59); + SetPRCSwitch("PRC_FILE_END_prc_familiar", 10); + SetPRCSwitch("PRC_FILE_END_prc_polymorph", 401); + SetPRCSwitch("PRC_FILE_END_prc_rune_craft", 7); + SetPRCSwitch("PRC_FILE_END_prc_spells", 4071); + SetPRCSwitch("PRC_FILE_END_prc_weap_items", 58); + SetPRCSwitch("PRC_FILE_END_pregen", 44); + SetPRCSwitch("PRC_FILE_END_prioritygroups", 21); + SetPRCSwitch("PRC_FILE_END_pvpsettings", 3); + SetPRCSwitch("PRC_FILE_END_racialappear", 254); + SetPRCSwitch("PRC_FILE_END_racialtypes", 254); + SetPRCSwitch("PRC_FILE_END_ranges", 13); + SetPRCSwitch("PRC_FILE_END_repadjust", 3); + SetPRCSwitch("PRC_FILE_END_replacetexture", 1); + SetPRCSwitch("PRC_FILE_END_repute", 4); + SetPRCSwitch("PRC_FILE_END_resistancecost", -1); + SetPRCSwitch("PRC_FILE_END_restduration", 60); + SetPRCSwitch("PRC_FILE_END_rrf_nss", 19); + SetPRCSwitch("PRC_FILE_END_rrf_wav", 40); + SetPRCSwitch("PRC_FILE_END_shft_packages", 9); + SetPRCSwitch("PRC_FILE_END_shft_pk_animal", 67); + SetPRCSwitch("PRC_FILE_END_shft_pk_constrct", 15); + SetPRCSwitch("PRC_FILE_END_shft_pk_dragon", 49); + SetPRCSwitch("PRC_FILE_END_shft_pk_element", 15); + SetPRCSwitch("PRC_FILE_END_shft_pk_gen1", 30); + SetPRCSwitch("PRC_FILE_END_shft_pk_gen2", 43); + SetPRCSwitch("PRC_FILE_END_shft_pk_outsider", 47); + SetPRCSwitch("PRC_FILE_END_shft_pk_rogue", 66); + SetPRCSwitch("PRC_FILE_END_shft_pk_undead", 43); + SetPRCSwitch("PRC_FILE_END_shft_pk_warrior", 88); + SetPRCSwitch("PRC_FILE_END_shifter_abilitie", 119); + SetPRCSwitch("PRC_FILE_END_shifter_feats", 425); + SetPRCSwitch("PRC_FILE_END_shifter_races", 30); + SetPRCSwitch("PRC_FILE_END_skills", 38); + SetPRCSwitch("PRC_FILE_END_skillvsitemcost", 55); + SetPRCSwitch("PRC_FILE_END_skyboxes", 6); + SetPRCSwitch("PRC_FILE_END_soundcatfilters", 14); + SetPRCSwitch("PRC_FILE_END_sounddefaultspos", 3); + SetPRCSwitch("PRC_FILE_END_sounddefaultstim", 5); + SetPRCSwitch("PRC_FILE_END_soundeax", 112); + SetPRCSwitch("PRC_FILE_END_soundgain", 14); + SetPRCSwitch("PRC_FILE_END_soundprovider", 12); + SetPRCSwitch("PRC_FILE_END_soundset", 453); + SetPRCSwitch("PRC_FILE_END_soundsettype", 4); + SetPRCSwitch("PRC_FILE_END_soundtypes", 1); + SetPRCSwitch("PRC_FILE_END_spells", 19348); + //SetPRCSwitch("PRC_FILE_END_spellschools", 9); + SetPRCSwitch("PRC_FILE_END_statescripts", 35); + SetPRCSwitch("PRC_FILE_END_stringtokens", 92); + SetPRCSwitch("PRC_FILE_END_surfacemat", 30); + SetPRCSwitch("PRC_FILE_END_swearfilter", 171); + SetPRCSwitch("PRC_FILE_END_tailmodel", 572); + SetPRCSwitch("PRC_FILE_END_tbw01_edge", 7); + SetPRCSwitch("PRC_FILE_END_tcn01doors", 0); + SetPRCSwitch("PRC_FILE_END_tcn01_edge", 27); + SetPRCSwitch("PRC_FILE_END_tdc01_edge", 12); + SetPRCSwitch("PRC_FILE_END_tde01_edge", 12); + SetPRCSwitch("PRC_FILE_END_tdm01_edge", 13); + SetPRCSwitch("PRC_FILE_END_tdr01_edge", 18); + SetPRCSwitch("PRC_FILE_END_tds01_edge", 12); + SetPRCSwitch("PRC_FILE_END_tdt01_edge", 11); + SetPRCSwitch("PRC_FILE_END_templates", 250); + SetPRCSwitch("PRC_FILE_END_tib01_edge", 3); + SetPRCSwitch("PRC_FILE_END_tic01_edge", 18); + SetPRCSwitch("PRC_FILE_END_tid01_edge", 10); + SetPRCSwitch("PRC_FILE_END_tii01_edge", 6); + SetPRCSwitch("PRC_FILE_END_tilecolor", 15); + SetPRCSwitch("PRC_FILE_END_tin01doors", 0); + SetPRCSwitch("PRC_FILE_END_tin01_edge", 15); + SetPRCSwitch("PRC_FILE_END_tms01_edge", 6); + SetPRCSwitch("PRC_FILE_END_tni01_edge", 15); + SetPRCSwitch("PRC_FILE_END_tni02_edge", 19); + SetPRCSwitch("PRC_FILE_END_tno01_edge", 67); + SetPRCSwitch("PRC_FILE_END_traps", 101); + SetPRCSwitch("PRC_FILE_END_treasurescale", 4); + SetPRCSwitch("PRC_FILE_END_tsw01_edge", 12); + SetPRCSwitch("PRC_FILE_END_ttd01_edge", 12); + SetPRCSwitch("PRC_FILE_END_ttf01_edge", 12); + SetPRCSwitch("PRC_FILE_END_tti01_edge", 10); + SetPRCSwitch("PRC_FILE_END_ttr01doors", 0); + SetPRCSwitch("PRC_FILE_END_ttr01_edge", 23); + SetPRCSwitch("PRC_FILE_END_tts01doors", 2); + SetPRCSwitch("PRC_FILE_END_tts01_edge", 25); + SetPRCSwitch("PRC_FILE_END_ttu01_edge", 36); + SetPRCSwitch("PRC_FILE_END_ttz01_edge", 28); + SetPRCSwitch("PRC_FILE_END_twc03_edge", 12); + SetPRCSwitch("PRC_FILE_END_unarmed_dmg", 13); + SetPRCSwitch("PRC_FILE_END_vfx_fire_forget", 16); + SetPRCSwitch("PRC_FILE_END_vfx_persistent", 254); + SetPRCSwitch("PRC_FILE_END_videoquality", 9); + SetPRCSwitch("PRC_FILE_END_visualeffects", 1373); + SetPRCSwitch("PRC_FILE_END_waypoint", 4); + SetPRCSwitch("PRC_FILE_END_weaponsounds", 21); + SetPRCSwitch("PRC_FILE_END_wingmodel", 89); + SetPRCSwitch("PRC_FILE_END_x3restrict", 899); + SetPRCSwitch("PRC_FILE_END_xpbaseconst", 16); + SetPRCSwitch("PRC_FILE_END_xptable", 39); + //END AUTO-GENERATED FILEENDS + + + //Overriding fileends + SetPRCSwitch("PRC_FILE_END_feat", 390); + SetPRCSwitch("PRC_FILE_END_spells", 539); + //SetPRCSwitch("PRC_FILE_END_iprp_feats", 17993); + SetPRCSwitch("PRC_FILE_END_iprp_feats", 40); + //SetPRCSwitch("PRC_FILE_END_iprp_spells", 1379); + + + //there is also the fileends.2da file, but that + //isnt read in here yet. may be later though + if(GetPRCSwitch(FILE_END_MANUAL)) + return; + SetPRCSwitch(FILE_END_CLASSES, PRCGetFileEnd("classes")); + SetPRCSwitch(FILE_END_RACIALTYPES, PRCGetFileEnd("racialtypes")); + SetPRCSwitch(FILE_END_GENDER, 1);//overriden to 1 for convoCC m/f only choice + SetPRCSwitch(FILE_END_PORTRAITS, PRCGetFileEnd("portraits")); + SetPRCSwitch(FILE_END_SKILLS, PRCGetFileEnd("skills")); + SetPRCSwitch(FILE_END_CLASS_FEAT, 600); + SetPRCSwitch(FILE_END_CLASS_POWER, 300); + SetPRCSwitch(FILE_END_CLASS_SPELLBOOK, PRCGetFileEnd("cls_spell_archv")); // + SetPRCSwitch(FILE_END_FEAT, PRCGetFileEnd("feat")); + SetPRCSwitch(FILE_END_FAMILIAR, PRCGetFileEnd("hen_familiar")); + SetPRCSwitch(FILE_END_ANIMALCOMP, PRCGetFileEnd("hen_companion")); + SetPRCSwitch(FILE_END_DOMAINS, PRCGetFileEnd("domains")); + SetPRCSwitch(FILE_END_SOUNDSET, PRCGetFileEnd("soundset")); + SetPRCSwitch(FILE_END_SPELLS, PRCGetFileEnd("spells")); + //SetPRCSwitch(FILE_END_SPELLSCHOOL, PRCGetFileEnd("spellschools")); + SetPRCSwitch(FILE_END_APPEARANCE, PRCGetFileEnd("appearance")); + SetPRCSwitch(FILE_END_WINGS, PRCGetFileEnd("wingmodel")); + SetPRCSwitch(FILE_END_TAILS, PRCGetFileEnd("tailmodel")); + SetPRCSwitch(FILE_END_BASEITEMS, PRCGetFileEnd("baseitems")); +} + +void CreateSwitchNameArray() +{ + object oWP = GetWaypointByTag("PRC_Switch_Name_WP"); + if(!GetIsObjectValid(oWP)) + oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "NW_WAYPOINT001", GetStartingLocation(), FALSE, "PRC_Switch_Name_WP"); + if(!GetIsObjectValid(oWP)) + PrintString("CreateSwitchNameArray: Problem creating waypoint."); + array_create(oWP, "Switch_Name"); + //if you add more switches, add them to this list + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEBUG); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_COMBAT_DEBUG); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_MATERIAL_COMPONENTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_COMPONENTS_SHOP); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_TRUESEEING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_TRUESEEING_SPOT_BONUS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_GRRESTORE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_HEAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_MASS_HEAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_HARM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_NEUTRALIZE_POISON); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_REMOVE_DISEASE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_BIOWARE_DURATION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_LOCAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_NO_HOSTILE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_BLANK_PC); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_AFTS_EXTRA_DAMAGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_ELEMENTAL_SWARM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_FEAR_AURAS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_TENSERS_TRANSFORMATION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_BLACK_BLADE_OF_DISASTER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_FIND_TRAPS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_DARKNESS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_DARKNESS_35ED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_ANIMATE_DEAD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_UNDEAD_PERMANENT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_UNDEAD_UNCONTROLLED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_NEC_TERM_PERMADEATH); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SPELL_ALIGNMENT_RESTRICT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_35ED_WORD_OF_FAITH); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SLEEP_NO_HD_CAP); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_165_DEATH_IMMUNITY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CASTERLEVEL_FIRST_CLASS_RULE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USE_NEW_IMBUE_ARROW); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DRAGON_DISCIPLE_SIZE_CHANGES); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SAMURAI_ALLOW_STOLEN_SACRIFICE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SAMURAI_ALLOW_UNIDENTIFIED_SACRIFICE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SAMURAI_SACRIFICE_SCALAR_x100); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SAMURAI_VALUE_SCALAR_x100); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ORC_WARLORD_COHORT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LICH_ALTER_SELF_DISABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TRUE_NECROMANCER_ALTERNATE_VISUAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_THRALLHERD_LEADERSHIP); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_XP_COSTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_TAKE_TEN_RULE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_PRIMARY_ABILITY_MODIFIER_RULE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_BACKLASH_DAMAGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_FOCI_ADJUST_DC); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_GOLD_MULTIPLIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_XP_FRACTION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_FAILURE_FRACTION_GOLD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_BOOK_DESTRUCTION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_UNIMPINGED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_IMPENETRABILITY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_DULLBLADES); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_CHAMPIONS_VALOR); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_EPIC_CONVO_LEARNING_DISABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_STAFF_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_NPC_HAS_PC_SPELLCASTING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ECL_USES_XP_NOT_HD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_DEMILICH); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SPELLSLAB); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SPELLSLAB_NOSCROLLS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_ABILITY_DAMAGE_EFFECTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SUPPLY_BASED_REST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_REST_HEALING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_REST_TIME); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_REST_LIMIT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_SPELL_SCHOOLS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PLAYER_TIME); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_SOMATIC_COMPOMENTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_SOMATIC_ITEMS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_MULTISUMMON); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SUMMON_ROUND_PER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ENABLE_SPELL_SHARING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_ANIMAL_COMPANIONS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_FAMILIARS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_FAMILIAR_FEEDING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_FAMILIARS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_NO_FREE_WIZ_SPELLS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_POWER_ATTACK); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_POWER_ATTACK_STACK_WITH_BW); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_NO_PETRIFY_GUI); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_SWITCH_CHANGING_CONVO); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_DOMAIN_ENFORCEMENT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BONUS_COHORTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CUSTOM_COHORTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_STANDARD_COHORTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_REGISTER_COHORTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_SLINGS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BEBILITH_CLAWS_DESTROY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_AUTO_IDENTIFY_ON_ACQUIRE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_AUTO_UNIDENTIFY_ON_UNACQUIRE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SPELLFIRE_DISALLOW_CHARGE_SELF); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SPELLFIRE_DISALLOW_DRAIN_SCROLL_POTION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SORC_DISALLOW_NEWSPELLBOOK); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BARD_DISALLOW_NEWSPELLBOOK); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BARD_LIGHT_ARMOR_SPELLCASTING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CWSAMURAI_NO_HEIRLOOM_DAISHO); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CONVO_TEMPLATE_GAIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_ARMOR_SPEED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_RACIAL_SPEED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_FAST_TRAVEL_SPEED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_REMOVE_PLAYER_SPEED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ENFORCE_RACIAL_APPEARANCE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_NPC_FORCE_RACE_ACQUIRE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DYNAMIC_CLOAK_AUTOCOLOUR_DISABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_STABLE_TO_DISABLED_CHANCE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_STABLE_TO_BLEED_CHANCE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_BLEED_TO_STABLE_CHANCE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_HEAL_FROM_STABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_DAMAGE_FROM_STABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_DAMAGE_FROM_BLEEDING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEATH_OR_BLEED); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_DEATH_ENABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ACP_MANUAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ACP_AUTOMATIC); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ACP_NPC_AUTOMATIC); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ACP_DELAY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USE_TAGBASED_INDEX_FOR_POISON); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_ITEM_POISON_COUNT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_ITEM_POISON_DIE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOW_ONLY_SHARP_WEAPONS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOW_ALL_POISONS_ON_WEAPONS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEXBASED_WEAPON_POISONING_FAILURE_CHANCE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_WEAPON_POISON_COUNT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_WEAPON_POISON_DIE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_POISON_ALLOW_CLEAN_IN_EQUIP); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_POISON_USE_INGREDIENST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_ASTRAL_CONSTRUCT_USE_2DA); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_RAPID_METABOLISM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_IMP_METAPSIONICS_USE_SUM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_USECR); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_HUGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_LARGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_MEDIUM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_SMALL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_TINY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_OUTSIDER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ELEMENTAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_CONSTRUCT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_UNDEAD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_DRAGON); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ABERRATION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_OOZE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_MAGICALBEAST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_GIANT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_VERMIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_BEAST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ANIMAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_MONSTROUSHUMANOID); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_HUMANOID); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_ELEMENTAL_DAMAGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SPELL_SNEAK_DISABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_3_5e_FIST_DAMAGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BRAWLER_SIZE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_APPEARANCE_SIZE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_MONK_ATTACKS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SMALL_CREATURE_FINESSE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_DIVINE_POWER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOW_SWITCH_OF_TARGET); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_COUP_DE_GRACE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_FLAME_WEAPON_DAMAGE_MAX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DARKFIRE_DAMAGE_MAX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TELEPORT_MAX_TARGET_LOCATIONS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_TELEPORTATION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_TIME); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_PC_AUTOEXPORT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_HP_TRACKING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_LOCATION_TRACKING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_MAPPIN_TRACKING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_DEATH_TRACKING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PW_SPELL_TRACKING); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_XP_USE_BIOWARE_XPTABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_XP_USE_SIMPLE_LA); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_XP_USE_SIMPLE_RACIAL_HD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_XP_USE_SIMPLE_RACIAL_HD_NO_FREE_XP); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_XP_USE_SIMPLE_RACIAL_HD_NO_SELECTION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_XP_USE_SETXP); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USE_DATABASE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USE_BIOWARE_DATABASE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DB_PRECACHE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DB_SQLITE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DB_SQLITE_INTERVAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DB_MYSQL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USE_LETOSCRIPT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_PHEONIX_SYNTAX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_FIX_ABILITIES); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_UNICORN_SQL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_GETNEWESTBIC); + +//craft + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CRAFT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MULTIPLIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MAX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BREW_POTION_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SCRIBE_SCROLL_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_WAND_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_ROD_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_STAFF_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_BASE_ITEMS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), X2_CI_BREWPOTION_MAXLEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), X2_CI_BREWPOTION_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), X2_CI_SCRIBESCROLL_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), X2_CI_CRAFTWAND_MAXLEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), X2_CI_CRAFTWAND_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_ARBITRARY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_COST_SCALE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_TIME_SCALE); + +//spells + +//shifter + +//general + +//PW + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ENABLE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_AVARIEL_WINGS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_FEYRI_WINGS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_AASIMAR_WINGS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_FEYRI_TAIL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_TIEFLING_TAIL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_DROW_ENFORCE_GENDER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_GENASI_ENFORCE_DOMAINS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_RAKSHASA_FEMALE_APPEARANCE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ENFORCE_PNP_RACIAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ENFORCE_FEATS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_DISALLOW_CUSTOMISE_WINGS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_DISALLOW_CUSTOMISE_TAIL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_DISALLOW_CUSTOMISE_MODEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_USE_RACIAL_APPEARANCES); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_USE_RACIAL_PORTRAIT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ONLY_PLAYER_VOICESETS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_RESTRICT_VOICESETS_BY_SEX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ALLOW_TO_KEEP_VOICESET); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ALLOW_TO_KEEP_PORTRAIT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_RESTRICT_PORTRAIT_BY_SEX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ENABLE_RACIAL_HITDICE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ALLOW_SKILL_POINT_ROLLOVER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_USE_XP_FOR_NEW_CHAR); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_ENCRYPTION_KEY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_STAT_POINTS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_BONUS_FEATS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_MAX_STAT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_SKILL_MULTIPLIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_SKILL_BONUS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CONVOCC_CUSTOM_EXIT_SCRIPT); + + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TRUENAME_CR_MULTIPLIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TRUENAME_LEVEL_BONUS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TRUENAME_DC_CONSTANT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PERFECTED_MAP_CONSTANT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PERFECTED_MAP_MULTIPLIER); +} diff --git a/src/include/inc_target_list.nss b/src/include/inc_target_list.nss new file mode 100644 index 0000000..327f6ae --- /dev/null +++ b/src/include/inc_target_list.nss @@ -0,0 +1,239 @@ +//:://///////////////////////////////////////////// +//:: Target list management functions include +//:: inc_target_list +//:://///////////////////////////////////////////// +/** @file + This is a set of functions intended to be used in + spellscripts for getting a set of targets according + to CR. + + The list is built on the objects making up the list, + so it should be extracted from this system if it is + to be used for longer than the duration of a single + spellscript. + This is because the system cleans up after itself + and the object references from which the list is + built up of are deleted when current script execution + ends. + + Also, do not manipulate the list structure with means + other than the functions provided here. + + Any particular list should be built using only a signle + bias and ordering direction. + + + Behavior in circumstances other than the recommended + is non-deterministic. In other words, you've been warned :D + + + One can utilise the insertion bias constants to change + the ordering of the creatures in the list. + All orders are descending by default. + + + @author Ornedan + @date Created - 18.01.2005 + @date Modified - 26.06.2005 + @date Modified - 21.01.2006 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Public constants */ +////////////////////////////////////////////////// + +/// Inserts based on Challenge Rating +const int INSERTION_BIAS_CR = 1; + +/// Inserts based on Hit Dice +const int INSERTION_BIAS_HD = 2; + +/// Inserts based on the ratio of CurrentHP / MaxHP +const int INSERTION_BIAS_HP_RATIO = 3; + +/// Inserts based on distance from the object list is being built on +const int INSERTION_BIAS_DISTANCE = 4; + +/// Inserts based on the current amount of HP +const int INSERTION_BIAS_HP = 5; + +////////////////////////////////////////////////// +/* Public functions */ +////////////////////////////////////////////////// + +/** + * Adds the given object to a list. If no list exists when this is called, + * it is created. + * If either oInsert or oCaster is not valid, nothing happens. + * + * @param oInsert The object to insert into the list + * @param oCaster The object that holds the head of the list. + * This should be whatever object is casting the + * spell that uses the list. + * @param nInsertionBias The insertion bias to use, one of the + * INSERTION_BIAS_* constants + * @param bDescendingOrder Whether to sort the targets into ascending or + * descending order. + */ +void AddToTargetList(object oInsert, object oCaster, int nInsertionBias = INSERTION_BIAS_CR, int bDescendingOrder = TRUE); + + +/** + * Gets the head a target list. + * Returns the head of the list built on oCaster and removes it + * from the list. If there are no more entries in the list, + * return OBJECT_INVALID. + * + * @param oCaster An object a target list has been built on. + * @return The current head of the target list, which + * is then removed from the list. Or + * OBJECT_INVALID when no more objects remain + * in the list. + */ +object GetTargetListHead(object oCaster); + + + +////////////////////////////////////////////////// +/* Private constants */ +////////////////////////////////////////////////// + +const string TARGET_LIST_HEAD = "TargetListHead"; +const string TARGET_LIST_NEXT = "TargetListNext_"; +const string TARGET_LIST_PURGE_CALLED = "TargetListPurgeCalled"; + + +////////////////////////////////////////////////// +/* Private functions */ +////////////////////////////////////////////////// + +int GetIsInsertPosition(object oInsert, object oCompare, object oCaster, int nInsertionBias, int bDescendingOrder); + +void PurgeTargetList(object oCaster); + +////////////////////////////////////////////////// +/* function definitions */ +////////////////////////////////////////////////// + +void AddToTargetList(object oInsert, object oCaster, int nInsertionBias = INSERTION_BIAS_CR, int bDescendingOrder = TRUE) +{ + if(!GetIsObjectValid(oInsert) || + !GetIsObjectValid(oCaster)) + { + WriteTimestampedLogEntry("AddToTargetList called with an invalid parameter"); + return; + } + + object oCurrent = GetLocalObject(oCaster, TARGET_LIST_HEAD); + + // If the queue is empty, or the insertable just happens to belong at the head + if(GetIsInsertPosition(oInsert, oCurrent, oCaster, nInsertionBias, bDescendingOrder)) + { + SetLocalObject(oCaster, TARGET_LIST_HEAD, oInsert); + SetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oInsert), oCurrent); + }// end if - insertable is the new head of the list + else + { + object oNext = GetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oCurrent)); + int bDone = FALSE; + while(!bDone) + { + if(GetIsInsertPosition(oInsert, oNext, oCaster, nInsertionBias, bDescendingOrder)) + { + SetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oCurrent), oInsert); + // Some paranoia to make sure the last element of the list always points + // to invalid + if(GetIsObjectValid(oNext)){ + SetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oInsert), oNext); + } + else + DeleteLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oInsert)); + + bDone = TRUE; + }// end if - this is the place to insert + else + { + oCurrent = oNext; + oNext = GetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oCurrent)); + }// end else - get next object in the list + }// end while - loop through the list, looking for the position to insert this creature + }// end else - the insertable creature is to be in a position other than the head + + // Schedule clearing the list away once the current script has finished if it hasn't been done already + if(!GetLocalInt(oCaster, TARGET_LIST_PURGE_CALLED)) + { + DelayCommand(0.0f, PurgeTargetList(oCaster)); + SetLocalInt(oCaster, TARGET_LIST_PURGE_CALLED, TRUE); + } +} + + + +object GetTargetListHead(object oCaster) +{ + object oReturn = GetLocalObject(oCaster, TARGET_LIST_HEAD); + SetLocalObject(oCaster, TARGET_LIST_HEAD, GetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oReturn))); + DeleteLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oReturn)); + + return oReturn; +} + + +/* Removes the list of target objects held by oCaster + * This should be called once the list is no longer used by the script that needed it + * Failure to do so may cause problems + */ +void PurgeTargetList(object oCaster) +{ + object oCurrent = GetLocalObject(oCaster, TARGET_LIST_HEAD); + DeleteLocalObject(oCaster, TARGET_LIST_HEAD); + object oNext; + while(GetIsObjectValid(oCurrent)) + { + oNext = GetLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oCurrent)); + DeleteLocalObject(oCaster, TARGET_LIST_NEXT + ObjectToString(oCurrent)); + oCurrent = oNext; + }// end while - loop through the list erasing the links + + DeleteLocalInt(oCaster, TARGET_LIST_PURGE_CALLED); +} + + +// This is an internal function intended only for use in inc_target_list.nss +int GetIsInsertPosition(object oInsert, object oCompare, object oCaster, int nInsertionBias, int bDescendingOrder) +{ + // Special case - A valid object is always inserted before an invalid one + if(!GetIsObjectValid(oCompare)) + return TRUE; + + int bReturn; + + switch(nInsertionBias) + { + case INSERTION_BIAS_CR: + bReturn = GetChallengeRating(oInsert) > GetChallengeRating(oCompare); + break; + case INSERTION_BIAS_HD: + bReturn = GetHitDice(oInsert) > GetHitDice(oCompare); + break; + case INSERTION_BIAS_HP_RATIO:// A bit of trickery to avoid possible division by zero, which would happen if a non-creature got passed for insertion + bReturn = (IntToFloat(GetCurrentHitPoints(oInsert)) / ((GetMaxHitPoints(oInsert) > 0) ? IntToFloat(GetMaxHitPoints(oInsert)) : 0.001f)) + > + (IntToFloat(GetCurrentHitPoints(oCompare)) / ((GetMaxHitPoints(oCompare) > 0) ? IntToFloat(GetMaxHitPoints(oCompare)) : 0.001f)); + break; + case INSERTION_BIAS_DISTANCE: + bReturn = GetDistanceBetween(oInsert, oCaster) > GetDistanceBetween(oCompare, oCaster); + break; + case INSERTION_BIAS_HP: + bReturn = GetCurrentHitPoints(oInsert) > GetCurrentHitPoints(oCompare); + break; + default: + WriteTimestampedLogEntry("Invalid target selection bias given. Value: " + IntToString(nInsertionBias)); + return TRUE; + } + + return bDescendingOrder ? bReturn : !bReturn; +} \ No newline at end of file diff --git a/src/include/inc_threads.nss b/src/include/inc_threads.nss new file mode 100644 index 0000000..a172694 --- /dev/null +++ b/src/include/inc_threads.nss @@ -0,0 +1,405 @@ +//::////////////////////////////////////////////// +//:: Thread include +//:: inc_threads +//::////////////////////////////////////////////// +/** @file + A simple set of functions for creating, + controlling and destroying threads that + repeatedly run a given script. + A thread is implemented as a pseudo-hb that + executes a given script on each of it's + beats. + + Threads may be in one of 3 states: + THREAD_STATE_DEAD: + Equivalent to the thread not existing at + all. + + THREAD_STATE_RUNNING: + The thread is alive, and will execute it's + script on each of the underlying pseudo-hb's + beats. + + THREAD_STATE_SLEEPING: + The thread is alive, but will not execute + it's script on the pseudo-hb's beats. + + + The thread's script will be ExecuteScripted on + the object that the thread is running on. This + is the same object that also stores the + thread's state (all of 3 local variables). + + + @author Ornedan + @date Created - 14.03.2005 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constant declarations */ +////////////////////////////////////////////////// + +// Thread state constants + +/// Thread state - Dead or non-existent +const int THREAD_STATE_DEAD = 0; +/// Thread state - Running at the moment +const int THREAD_STATE_RUNNING = 1; +/// Thread state - Sleeping +const int THREAD_STATE_SLEEPING = 2; + + +// Internal constants. Nothing to see here. <.< >.> + +const string PREFIX = "prc_thread_"; +const string SUFFIX_SCRIPT = "_script"; +const string SUFFIX_INTERVAL = "_interval"; +const string SUFFIX_ITERATION = "_iteration"; +const string CUR_THREAD = "current_thread"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Creates a new thread. + * + * @param sName Name of thread to create. Must be non-empty + * @param sScript Name of script to run. Must be non-empty + * @param fExecutionInterval Amount of time that passes between executions of sScript. + * Only values > 0.0 allowed + * @param oToRunOn Object that stores the thread state values, and that + * sScript will be ExecuteScripted on. + * If this is OBJECT_INVALID, the module will be used to hold + * the thread + * + * @return TRUE if the thread creation was successfull. Possible reasons of failure: + * - One or more parameters were invalid + * - A thread by the given name was already running on oToRunOn + */ +int SpawnNewThread(string sName, string sScript, float fExecutionInterval = 6.0f, object oToRunOn = OBJECT_INVALID); + +/** + * Inspects the state of the given thread. + * + * @param sName Name of thread to inspect. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + * + * @return One of the THREAD_STATE_* constants. Inspecting a non- + * existent thread, or thread that was running on an object that + * was destroyed will return THREAD_STATE_DEAD + */ +int GetThreadState(string sName, object oRunningOn = OBJECT_INVALID); + +/** + * Gets the name of the script the given thread is running. + * + * @param sName Name of thread to inspect. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + * + * @return The name of the the given thread executes on success, "" + * on failure (querying with invalid parameters, or on a dead thread) + */ +string GetThreadScript(string sName, object oRunningOn = OBJECT_INVALID); + +/** Gets the execution interval for the given thread + * + * @param sName Name of thread to inspect. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + * + * @return The time between the given thread executing it's script. + * On failure, 0.0f is returned. + */ +float GetThreadExecutionInterval(string sName, object oRunningOn = OBJECT_INVALID); + +/** + * Gets the name of the thread whose script is currently being executed. + * + * @return The name of the thread being executed at the time of the call, + * or "" if no thread is being executed when this is called. + */ +string GetCurrentThread(); + +/** + * Gets the object currently running thread is executing on + * + * @return The object the currently executing thread is being executed on + * or OBJECT_INVALID if no thread is being executed when this is called. + */ +object GetCurrentThreadObject(); + +/** + * Stops further execution of the given thread and removes it's data + * from the object it was running on. + * + * @param sName Name of thread to terminate. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + */ +void TerminateThread(string sName, object oRunningOn = OBJECT_INVALID); + +/** + * Stops further execution of the thread currently being executed. + * A convenience wrapper for TerminateThread to be called from a + * threadscript. + */ +void TerminateCurrentThread(); + +/** + * Sets the stae of the given thread to sleeping. + * + * @param sName Name of thread to set sleeping. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + * + * @return Whether the operation was successfull. Failure indicates + * that the thread was dead. + */ +int SleepThread(string sName, object oRunningOn = OBJECT_INVALID); + +/** + * Awakens the given thread. + * + * @param sName Name of thread to set back running. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + * + * @return Whether the operation was successfull. Failure indicates + * that the thread was dead. + */ +int AwakenThread(string sName, object oRunningOn = OBJECT_INVALID); + +/** + * Changes the execution interval of the given thread. + * + * @param sName Name of thread to set back running. Must be non-empty + * @param oRunningOn Object that the thread is running on. If this + * is OBJECT_INVALID, the module will be used. + * @param fNewInterval The amount of time between executions of the + * threadscript that will used from next execution + * onwards. + * + * @return Returns whether the operation was successfull. Failure indicates + * that the thread was dead. + */ +int ChangeExecutionInterval(string sName, float fNewInterval, object oRunningOn = OBJECT_INVALID); + +/* + * Internal function. This is the pseudo-hb function that calls itself. + * + * @param sName name of the thread to run. Used to build local variable names + * @param oRunningOn object that stores the variables, and the one that will + * be passed to ExecuteScript + */ +void RunThread(string sName, object oRunningOn, int iIteration); + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + + +int SpawnNewThread(string sName, string sScript, float fExecutionInterval = 6.0f, object oToRunOn = OBJECT_INVALID){ + if(oToRunOn == OBJECT_INVALID) + oToRunOn = GetModule(); + + // Check paramaeters for correctness + if(sName == "" || + sScript == "" || + fExecutionInterval <= 0.0f || + !GetIsObjectValid(oToRunOn)) + return FALSE; + + // Make sure there is no thread by this name already running + // if(GetLocalInt(oToRunOn, PREFIX + sName)) + // return FALSE; + // use iterations in place of the above to make it more reliable in case of a PC thread timing out while not logged in + int iIteration = GetLocalInt(oToRunOn, PREFIX + sName + SUFFIX_ITERATION); + + // Set the thread variables + SetLocalInt(oToRunOn, PREFIX + sName, THREAD_STATE_RUNNING); + SetLocalString(oToRunOn, PREFIX + sName + SUFFIX_SCRIPT, sScript); + SetLocalFloat(oToRunOn, PREFIX + sName + SUFFIX_INTERVAL, fExecutionInterval); + + // Start thread execution + DelayCommand(fExecutionInterval, RunThread(sName, oToRunOn, iIteration)); + + // All done successfully + return TRUE; +} + + +int GetThreadState(string sName, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness + if(sName == "" || + !GetIsObjectValid(oRunningOn)) + return FALSE; + + // Return the local determining if the thread exists + return GetLocalInt(oRunningOn, PREFIX + sName); +} + + +string GetThreadScript(string sName, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness + if(sName == "" || + !GetIsObjectValid(oRunningOn)) + return ""; + + return GetLocalString(oRunningOn, PREFIX + sName + SUFFIX_SCRIPT); +} + + +float GetThreadExecutionInterval(string sName, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness + if(sName == "" || + !GetIsObjectValid(oRunningOn)) + return 0.0f; + + return GetLocalFloat(oRunningOn, PREFIX + sName + SUFFIX_INTERVAL); +} + + +string GetCurrentThread(){ + return GetLocalString(GetModule(), PREFIX + CUR_THREAD); +} + + +object GetCurrentThreadObject(){ + return GetIsObjectValid(GetLocalObject(GetModule(), PREFIX + CUR_THREAD)) ? + GetLocalObject(GetModule(), PREFIX + CUR_THREAD) : + OBJECT_INVALID; +} + + +void TerminateThread(string sName, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness. Just an optimization here, since + // if either of these were not valid, nothing would happen. + if(sName == "" || + !GetIsObjectValid(oRunningOn)) + return; + + // Remove the thread variables + DeleteLocalInt(oRunningOn, PREFIX + sName); + DeleteLocalString(oRunningOn, PREFIX + sName + SUFFIX_SCRIPT); + DeleteLocalFloat(oRunningOn, PREFIX + sName + SUFFIX_INTERVAL); + + // Increase the iteration so that any lingering runthread fail to fire if the thread is restarted + int iExpectedIteration = GetLocalInt(oRunningOn, PREFIX + sName + SUFFIX_ITERATION); + iExpectedIteration++; + SetLocalInt(oRunningOn, PREFIX + sName + SUFFIX_ITERATION, iExpectedIteration); +} + + +void TerminateCurrentThread(){ + TerminateThread(GetLocalString(GetModule(), PREFIX + CUR_THREAD), + GetLocalObject(GetModule(), PREFIX + CUR_THREAD) + ); +} + + +int SleepThread(string sName, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness + if(sName == "" || + !GetIsObjectValid(oRunningOn)) + return FALSE; + + // Change thread state + SetLocalInt(oRunningOn, PREFIX + sName, THREAD_STATE_SLEEPING); + + return TRUE; +} + + +int AwakenThread(string sName, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness + if(sName == "" || + !GetIsObjectValid(oRunningOn)) + return FALSE; + + // Change thread state + SetLocalInt(oRunningOn, PREFIX + sName, THREAD_STATE_RUNNING); + + return TRUE; +} + + +int ChangeExecutionInterval(string sName, float fNewInterval, object oRunningOn = OBJECT_INVALID){ + if(oRunningOn == OBJECT_INVALID) + oRunningOn = GetModule(); + + // Check paramaeters for correctness + if(!GetThreadState(sName, oRunningOn) || + fNewInterval <= 0.0f) + return FALSE; + + + SetLocalFloat(oRunningOn, PREFIX + sName + SUFFIX_INTERVAL, fNewInterval); + return TRUE; +} + + +void RunThread(string sName, object oRunningOn, int iIteration){ + // Abort if we're on the wrong iteration, this allows us to + // be liberal about spawning threads in case they've timed + // out while a PC was logged out + int iExpectedIteration = GetLocalInt(oRunningOn, PREFIX + sName + SUFFIX_ITERATION); + if(iIteration != iExpectedIteration) + return; + iExpectedIteration++; + SetLocalInt(oRunningOn, PREFIX + sName + SUFFIX_ITERATION, iExpectedIteration); + + // Abort if the object we're running on has ceased to exist + // or if the thread has been terminated + int nThreadState = GetThreadState(sName, oRunningOn); + if(nThreadState == THREAD_STATE_DEAD) + return; + + // Mark this thread as running + SetLocalString(GetModule(), PREFIX + CUR_THREAD, sName); + SetLocalObject(GetModule(), PREFIX + CUR_THREAD, oRunningOn); + + // Execute the threadscript if the thread is running atm + if(nThreadState == THREAD_STATE_RUNNING){ + string sScript = GetLocalString(oRunningOn, PREFIX + sName + SUFFIX_SCRIPT); + ExecuteScript(sScript, oRunningOn); + } + + // Schedule next execution, unless we've been terminated + if(GetThreadState(sName, oRunningOn) != THREAD_STATE_DEAD){ + DelayCommand(GetLocalFloat(oRunningOn, PREFIX + sName + SUFFIX_INTERVAL), RunThread(sName, oRunningOn, iExpectedIteration)); + } + + // Clean up the module variables + DeleteLocalString(GetModule(), PREFIX + CUR_THREAD); + DeleteLocalObject(GetModule(), PREFIX + CUR_THREAD); +} + + +// Test main +//void main(){} diff --git a/src/include/inc_time.nss b/src/include/inc_time.nss new file mode 100644 index 0000000..a027732 --- /dev/null +++ b/src/include/inc_time.nss @@ -0,0 +1,501 @@ +/** @file + This set of functions controlls time for players. + When recalculate time is called, the clock advances to that of the most behind player. + + This system works best with single party modules. If you have multiple parties and player + you may find that when the server catches up it may disorent players for a time. + + This is disabled unless the switch PRC_PLAYER_TIME is true + + Also in this include is a time struct and various functions for expressing time + As part of this, there is some custom tokens that can be setup + //82001 part of day + //82002 date + //82003 month + //82004 year + //82005 hour + //82006 minutes + //82007 seconds + + @author Primogenitor +*/ + +////////////////////////////////////////////////// +/* Function Prototypes */ +////////////////////////////////////////////////// + + +struct time +{ + int nYear; + int nMonth; + int nDay; + int nHour; + int nMinute; + int nSecond; + int nMillisecond; +}; + +struct time TimeCheck(struct time tTime); +struct time GetLocalTime(object oObject, string sName); +void SetLocalTime(object oObject, string sName, struct time tTime); +void DeleteLocalTime(object oObject, string sName); +struct time GetPersistantLocalTime(object oObject, string sName); +void SetPersistantLocalTime(object oObject, string sName, struct time tTime); +string TimeToString(struct time tTime); +struct time StringToTime(string sIn); + +void SetTimeAndDate(struct time tTime); +int GetIsTimeAhead(struct time tTime1, struct time tTime2); + +void RecalculateTime(int nMaxStep = 6); +void AdvanceTimeForPlayer(object oPC, float fSeconds); + + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "prc_inc_switch" +#include "inc_persist_loca" + + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +struct time TimeAdd(struct time tTime1, struct time tTime2) +{ + tTime1 = TimeCheck(tTime1); + tTime2 = TimeCheck(tTime2); + tTime1.nYear += tTime2.nYear; + tTime1.nMonth += tTime2.nMonth; + tTime1.nDay += tTime2.nDay; + tTime1.nHour += tTime2.nHour; + tTime1.nMinute += tTime2.nMinute; + tTime1.nSecond += tTime2.nSecond; + tTime1.nMillisecond += tTime2.nMillisecond; + tTime1 = TimeCheck(tTime1); + return tTime1; +} + +struct time TimeSubtract(struct time tTime1, struct time tTime2) +{ + tTime1 = TimeCheck(tTime1); + tTime2 = TimeCheck(tTime2); + tTime1.nYear -= tTime2.nYear; + tTime1.nMonth -= tTime2.nMonth; + tTime1.nDay -= tTime2.nDay; + tTime1.nHour -= tTime2.nHour; + tTime1.nMinute -= tTime2.nMinute; + tTime1.nSecond -= tTime2.nSecond; + tTime1.nMillisecond -= tTime2.nMillisecond; + tTime1 = TimeCheck(tTime1); + return tTime1; +} + +struct time GetLocalTime(object oObject, string sName) +{ + struct time tTime; + tTime.nYear = GetLocalInt(oObject, sName+".nYear"); + tTime.nMonth = GetLocalInt(oObject, sName+".nMonth"); + tTime.nDay = GetLocalInt(oObject, sName+".nDay"); + tTime.nHour = GetLocalInt(oObject, sName+".nHour"); + tTime.nMinute = GetLocalInt(oObject, sName+".nMinute"); + tTime.nSecond = GetLocalInt(oObject, sName+".nSecond"); + tTime.nMillisecond = GetLocalInt(oObject, sName+".nMillisecond"); + tTime = TimeCheck(tTime); + return tTime; +} + +void SetLocalTime(object oObject, string sName, struct time tTime) +{ + tTime = TimeCheck(tTime); + SetLocalInt(oObject, sName+".nYear", tTime.nYear); + SetLocalInt(oObject, sName+".nMonth", tTime.nMonth); + SetLocalInt(oObject, sName+".nDay", tTime.nDay); + SetLocalInt(oObject, sName+".nHour", tTime.nHour); + SetLocalInt(oObject, sName+".nMinute", tTime.nMinute); + SetLocalInt(oObject, sName+".nSecond", tTime.nSecond); + SetLocalInt(oObject, sName+".nMillisecond", tTime.nMillisecond); +} + +void DeleteLocalTime(object oObject, string sName) +{ + DeleteLocalInt(oObject, sName+".nYear"); + DeleteLocalInt(oObject, sName+".nMonth"); + DeleteLocalInt(oObject, sName+".nDay"); + DeleteLocalInt(oObject, sName+".nHour"); + DeleteLocalInt(oObject, sName+".nMinute"); + DeleteLocalInt(oObject, sName+".nSecond"); + DeleteLocalInt(oObject, sName+".nMillisecond"); +} + +struct time GetPersistantLocalTime(object oObject, string sName) +{ + struct time tTime; + tTime.nYear = GetPersistantLocalInt(oObject, sName+".nYear"); + tTime.nMonth = GetPersistantLocalInt(oObject, sName+".nMonth"); + tTime.nDay = GetPersistantLocalInt(oObject, sName+".nDay"); + tTime.nHour = GetPersistantLocalInt(oObject, sName+".nHour"); + tTime.nMinute = GetPersistantLocalInt(oObject, sName+".nMinute"); + tTime.nSecond = GetPersistantLocalInt(oObject, sName+".nSecond"); + tTime.nMillisecond = GetPersistantLocalInt(oObject, sName+".nMillisecond"); + tTime = TimeCheck(tTime); + return tTime; +} + +void SetPersistantLocalTime(object oObject, string sName, struct time tTime) +{ + tTime = TimeCheck(tTime); + SetPersistantLocalInt(oObject, sName+".nYear", tTime.nYear); + SetPersistantLocalInt(oObject, sName+".nMonth", tTime.nMonth); + SetPersistantLocalInt(oObject, sName+".nDay", tTime.nDay); + SetPersistantLocalInt(oObject, sName+".nHour", tTime.nHour); + SetPersistantLocalInt(oObject, sName+".nMinute", tTime.nMinute); + SetPersistantLocalInt(oObject, sName+".nSecond", tTime.nSecond); + SetPersistantLocalInt(oObject, sName+".nMillisecond", tTime.nMillisecond); +} + +string TimeToString(struct time tTime) +{ + string sReturn; + sReturn += IntToString(tTime.nYear)+"|"; + sReturn += IntToString(tTime.nMonth)+"|"; + sReturn += IntToString(tTime.nDay)+"|"; + sReturn += IntToString(tTime.nHour)+"|"; + sReturn += IntToString(tTime.nMinute)+"|"; + sReturn += IntToString(tTime.nSecond)+"|"; + sReturn += IntToString(tTime.nMillisecond); + return sReturn; +} + +struct time StringToTime(string sIn) +{ + struct time tTime; + int nPos; + string sID; + nPos = FindSubString(sIn, "|"); + tTime.nYear = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + nPos = FindSubString(sIn, "|"); + tTime.nMonth = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + nPos = FindSubString(sIn, "|"); + tTime.nDay = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + nPos = FindSubString(sIn, "|"); + tTime.nHour = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + nPos = FindSubString(sIn, "|"); + tTime.nMinute = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + nPos = FindSubString(sIn, "|"); + tTime.nSecond = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + nPos = FindSubString(sIn, "|"); + tTime.nMillisecond = StringToInt(GetStringLeft(sIn, nPos)); + sIn = GetStringRight(sIn, GetStringLength(sIn)-nPos-1); + + return tTime; +} + +struct time TimeCheck(struct time tTime) +{ + while(tTime.nMillisecond > 1000) + { + tTime.nSecond += 1; + tTime.nMillisecond -= 1000; + } + while(tTime.nSecond > 60) + { + tTime.nMinute += 1; + tTime.nSecond -= 60; + } + while(tTime.nMinute > FloatToInt(HoursToSeconds(1))/60) + { + tTime.nHour += 1; + tTime.nMinute -= FloatToInt(HoursToSeconds(1))/60; + } + while(tTime.nHour > 24) + { + tTime.nDay += 1; + tTime.nHour -= 24; + } + while(tTime.nDay > 28) + { + tTime.nMonth += 1; + tTime.nDay -= 28; + } + while(tTime.nMonth > 12) + { + tTime.nYear += 1; + tTime.nMonth -= 12; + } + //decreases + while(tTime.nMillisecond < 1) + { + tTime.nSecond -= 1; + tTime.nMillisecond += 1000; + } + while(tTime.nSecond < 1) + { + tTime.nMinute -= 1; + tTime.nSecond += 60; + } + while(tTime.nMinute < 1) + { + tTime.nHour -= 1; + tTime.nMinute += FloatToInt(HoursToSeconds(1))/60; + } + while(tTime.nHour < 1) + { + tTime.nDay -= 1; + tTime.nHour += 24; + } + while(tTime.nDay < 1) + { + tTime.nMonth -= 1; + tTime.nDay += 28; + } + while(tTime.nMonth < 1) + { + tTime.nYear -= 1; + tTime.nMonth += 12; + } + return tTime; +} + +struct time GetTimeAndDate() +{ + struct time tTime; + tTime.nYear = GetCalendarYear(); + tTime.nMonth = GetCalendarMonth(); + tTime.nDay = GetCalendarDay(); + tTime.nHour = GetTimeHour(); + tTime.nMinute = GetTimeMinute(); + tTime.nSecond = GetTimeSecond(); + tTime.nMillisecond = GetTimeMillisecond(); + tTime = TimeCheck(tTime); + return tTime; +} + +void SetTimeAndDate(struct time tTime) +{ + tTime = TimeCheck(tTime); + SetCalendar(tTime.nYear, tTime.nMonth, tTime.nDay); + SetTime(tTime.nHour, tTime.nMinute, tTime.nSecond, tTime.nMillisecond); +} + +int GetIsTimeAhead(struct time tTime1, struct time tTime2) +{ + if(tTime1.nYear > tTime2.nYear) + return TRUE; + else if(tTime1.nYear < tTime2.nYear) + return FALSE; + //equal + if(tTime1.nMonth > tTime2.nMonth) + return TRUE; + else if(tTime1.nMonth < tTime2.nMonth) + return FALSE; + //equal + if(tTime1.nDay > tTime2.nDay) + return TRUE; + else if(tTime1.nDay < tTime2.nDay) + return FALSE; + //equal + if(tTime1.nHour > tTime2.nHour) + return TRUE; + else if(tTime1.nHour < tTime2.nHour) + return FALSE; + //equal + if(tTime1.nMinute > tTime2.nMinute) + return TRUE; + else if(tTime1.nMinute < tTime2.nMinute) + return FALSE; + //equal + if(tTime1.nSecond > tTime2.nSecond) + return TRUE; + else if(tTime1.nSecond < tTime2.nSecond) + return FALSE; + //equal + if(tTime1.nMillisecond > tTime2.nMillisecond) + return TRUE; + else if(tTime1.nMillisecond < tTime2.nMillisecond) + return FALSE; + //must be exactly the same + return FALSE; +} + +void RecalculateTime(int nMaxStep = 6) +{ + if(!GetPRCSwitch(PRC_PLAYER_TIME)) + return; + //get the earliest time ahead of all PCs + object oPC = GetFirstPC(); + struct time tTimeAhead = GetLocalTime(oPC, "TimeAhead"); + while(GetIsObjectValid(oPC)) + { + struct time tTest = GetLocalTime(oPC, "TimeAhead"); + if(!GetIsTimeAhead(tTimeAhead, tTest)) + tTimeAhead = tTest; + oPC = GetNextPC(); + } + //if its zero, abort + struct time tNULL; + if(tNULL == tTimeAhead) + return; + //if its not zero, recalulate it till it is + DelayCommand(0.01, RecalculateTime());//do it again until caught up + //create the steps to use + struct time tStep; + tStep.nSecond = nMaxStep; + //make sure you dont skip more than a step at a time + if(GetIsTimeAhead(tTimeAhead, tStep)) + tTimeAhead = tStep; + //set the new real time + struct time tNewTime = GetTimeAndDate(); + tNewTime = TimeAdd(tNewTime, tTimeAhead); + SetTimeAndDate(tNewTime); + + //update the stored values + oPC = GetFirstPC(); + while(GetIsObjectValid(oPC)) + { + struct time tTest = GetLocalTime(oPC, "TimeAhead"); + tTest = TimeSubtract(tTest, tTimeAhead); + SetLocalTime(oPC, "TimeAhead", tTest); + oPC = GetNextPC(); + } +} + + +void AdvanceTimeForPlayer(object oPC, float fSeconds) +{ + struct time tTime = GetLocalTime(oPC, "TimeAhead"); + tTime.nSecond += FloatToInt(fSeconds); + SetLocalTime(oPC, "TimeAhead", tTime); + DelayCommand(0.01, RecalculateTime()); +} + +void AssembleTokens(struct time tTime) +{ + tTime = TimeCheck(tTime); + //setup time tokens + //82001 part of day + //82002 date + //82003 month + //82004 year + //82005 hour + //82006 minutes + //82007 seconds + //82008 24 hour clock + //82009 timer + + //this assumes default time settings + //Dawn, 06:00 + //Dusk, 18:00 + if(tTime.nHour == 6) + SetCustomToken(82001, "dawn"); + else if(tTime.nHour == 18) + SetCustomToken(82001, "dusk"); + else if(tTime.nHour >= 19 || tTime.nHour <= 5) + SetCustomToken(82001, "night"); + else if(tTime.nHour >= 7 && tTime.nHour < 13) + SetCustomToken(82001, "morning"); + else if(tTime.nHour >= 13 && tTime.nHour < 18) + SetCustomToken(82001, "afternoon"); + + string sDay = IntToString(tTime.nDay); + if(tTime.nDay == 1 + //|| tTime.nDay == 11 //this is 11th + || tTime.nDay == 21) + sDay += "st"; + else if(tTime.nDay == 2 + //|| tTime.nDay == 12 //this is 12th + || tTime.nDay == 22) + sDay += "nd"; + else if(tTime.nDay == 3 + //|| tTime.nDay == 13 //this is 13th + || tTime.nDay == 23) + sDay += "rd"; + else + sDay += "th"; + SetCustomToken(82002, sDay); + + string sMonth; + switch(tTime.nMonth) + { + case 1: sMonth = "January"; break; + case 2: sMonth = "Febuary"; break; + case 3: sMonth = "March"; break; + case 4: sMonth = "April"; break; + case 5: sMonth = "May"; break; + case 6: sMonth = "June"; break; + case 7: sMonth = "July"; break; + case 8: sMonth = "August"; break; + case 9: sMonth = "September"; break; + case 10: sMonth = "October"; break; + case 11: sMonth = "November"; break; + case 12: sMonth = "December"; break; + } + SetCustomToken(82003, sMonth); + + SetCustomToken(82004, IntToString(tTime.nYear)); + SetCustomToken(82005, IntToString(tTime.nHour)); + SetCustomToken(82006, IntToString(tTime.nMinute)); + SetCustomToken(82007, IntToString(tTime.nSecond)); + SetCustomToken(82008, IntToString(tTime.nHour)+":"+IntToString(tTime.nMinute)); + + string sTimer; + if(!tTime.nYear) + { + if(tTime.nYear > 1) + sTimer += IntToString(tTime.nYear)+" years"; + else + sTimer += IntToString(tTime.nYear)+" year"; + } + if(!tTime.nMonth) + { + if(sTimer != "") + sTimer += ", "; + if(tTime.nMonth > 1) + sTimer += IntToString(tTime.nMonth)+" months"; + else + sTimer += IntToString(tTime.nMonth)+" month"; + } + if(!tTime.nDay) + { + if(sTimer != "") + sTimer += ", "; + if(tTime.nDay > 1) + sTimer += IntToString(tTime.nDay)+" days"; + else + sTimer += IntToString(tTime.nMonth)+" day"; + } + if(!tTime.nHour) + { + if(sTimer != "") + sTimer += ", "; + if(tTime.nHour > 1) + sTimer += IntToString(tTime.nHour)+" hours"; + else + sTimer += IntToString(tTime.nMonth)+" hour"; + } + if(!tTime.nMinute) + { + if(sTimer != "") + sTimer += ", "; + if(tTime.nMinute > 1) + sTimer += IntToString(tTime.nMinute)+" minutes"; + else + sTimer += IntToString(tTime.nMonth)+" minute"; + } + SetCustomToken(82009, sTimer); +} diff --git a/src/include/inc_timestop.nss b/src/include/inc_timestop.nss new file mode 100644 index 0000000..bd624e9 --- /dev/null +++ b/src/include/inc_timestop.nss @@ -0,0 +1,83 @@ + +void DoTimestopEquip(object oPC, object oItem); +void DoTimestopUnEquip(object oPC, object oItem); +void ApplyTSToObject(object oTarget); +void RemoveTSFromObject(object oTarget); + +#include "prc_x2_itemprop" +#include "prc_inc_switch" +#include "inc_prc_npc" +//#include "inc_utility" + +void RemoveTimestopEquip() +{ + int i; + for (i=0;i<18;i++) + { + IPRemoveMatchingItemProperties(GetItemInSlot(i), ITEM_PROPERTY_NO_DAMAGE, DURATION_TYPE_TEMPORARY); + } +} + +void DoTimestopEquip(object oPC, object oItem) +{ + if(GetPRCSwitch(PRC_TIMESTOP_NO_HOSTILE)) + { + if(GetHasSpellEffect(SPELL_TIME_STOP, oPC) + || GetHasSpellEffect(4032, oPC) + || GetHasSpellEffect(14236, oPC)) + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyNoDamage(), oItem, 9999.0); + /*else if(GetHasSpellEffect(POWER_ID, oPC)) + { + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyNoDamage(), oItem, 9999.0); + //stuff for AC negation + }*/ + } +} + +void DoTimestopUnEquip(object oPC, object oItem) +{ + if(GetPRCSwitch(PRC_TIMESTOP_NO_HOSTILE)) + { + if(GetHasSpellEffect(SPELL_TIME_STOP, oPC) + || GetHasSpellEffect(4032, oPC) + || GetHasSpellEffect(14236, oPC)) + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_NO_DAMAGE, DURATION_TYPE_TEMPORARY); + /*else if(GetHasSpellEffect(POWER_ID, oPC)) + { + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyNoDamage(), oItem, 9999.0); + //stuff for AC negation removal + }*/ + } +} + +void ApplyTSToObject(object oTarget) +{ + effect eTS = EffectVisualEffect(VFX_DUR_FREEZE_ANIMATION); + effect eCSP = EffectCutsceneParalyze(); + effect eLink = EffectLinkEffects(eTS, eCSP); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget); + if(GetIsPC(oTarget) && GetPRCSwitch(PRC_TIMESTOP_BLANK_PC)) + BlackScreen(oTarget); + AssignCommand(oTarget, ClearAllActions(FALSE)); + SetCommandable(FALSE, oTarget); +} + +void RemoveTSFromObject(object oTarget) +{ + effect eTest = GetFirstEffect(oTarget); + while(GetIsEffectValid(eTest)) + { + int nSpellId = GetEffectSpellId(eTest); + if(nSpellId == SPELL_TIME_STOP + || nSpellId == 4032 //epic TS + || nSpellId == 14205 // POWER_TEMPORALACCELERATION + || nSpellId == 17366 // MOVE_DM_MOMENT_ALACRITY + || nSpellId == 17511 // MOVE_WR_WHITE_RAVEN_TACTICS + ) + RemoveEffect(oTarget, eTest); + eTest = GetNextEffect(oTarget); + } + if(GetIsPC(oTarget)) + StopFade(oTarget); + SetCommandable(TRUE, oTarget); +} diff --git a/src/include/inc_uniqueid.nss b/src/include/inc_uniqueid.nss new file mode 100644 index 0000000..b5cbb75 --- /dev/null +++ b/src/include/inc_uniqueid.nss @@ -0,0 +1,100 @@ +//:://///////////////////////////////////////////// +//:: Unique identifier generation include +//:: inc_uniqueid +//:://///////////////////////////////////////////// +/** @file inc_uniqueid + Contains functions for generating unique IDs + within the scope of one module instance. + + An ID is a string of format: + PRC_UID_X + where X is the concatenation of the values of + one or more running integer counters. + + The uniqueness is quaranteed by using a + set of local integers stored on the module + object as counters. + At first, the set contains a single integer, + initialised to 0. Each UID generation + increments it's value by 1. Once the value + reaches the maximum an NWN integer type may + have (0xFFFFFFFF), a new integer is added + to the set, again initialised to 0. + + + NOTE: The generated strings are only unique + withing a single module instance. Reloading + a module (new game / server reset) will + reset the counter due to the counter array + being lost. + As such, UIDs should not be stored persistently. + + @author Ornedan + @date Created - 2006.06.30 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string PRC_UID_PREFIX = "PRC_UID_"; +const string PRC_UID_ARRAY = "PRC_UID_Counters"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Generates an UID, as described in the header comments. + * + * @return A string unique within a single module instance. + */ +string GetUniqueID(); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "inc_utility" +#include "prc_inc_array" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +string GetUniqueID() +{ + object oModule = GetModule(); + string sReturn = PRC_UID_PREFIX; + // Init if this is the first call + if(!array_exists(oModule, PRC_UID_ARRAY)) + { + array_create(oModule, PRC_UID_ARRAY); + array_set_int(oModule, PRC_UID_ARRAY, 0, 0); + } + + // Loop over all the integers and concatenate them onto the UID being generated + int i, nMax = array_get_size(oModule, PRC_UID_ARRAY); + for(i=0; i < nMax; i++) + sReturn += IntToString(array_get_int(oModule, PRC_UID_ARRAY, i)); + + // Increment the counters + if((i = array_get_int(oModule, PRC_UID_ARRAY, nMax - 1)) < 0xFFFFFFFF) + // We're below maximum integer size, just increment the stored value + array_set_int(oModule, PRC_UID_ARRAY, nMax - 1, i + 1); + else + // We need to add a new integer to the set + array_set_int(oModule, PRC_UID_ARRAY, nMax, 0); + + // Return the generated value + return sReturn; +} + +// Test main +//void main(){} diff --git a/src/include/inc_utility.nss b/src/include/inc_utility.nss new file mode 100644 index 0000000..9f3a9e2 --- /dev/null +++ b/src/include/inc_utility.nss @@ -0,0 +1,1741 @@ +//::////////////////////////////////////////////// +//:: General utility functions +//:: inc_utility +//::////////////////////////////////////////////// +/** @file + An include file for various small and generally + useful functions. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +/**********************\ +* Constant Definitions * +\**********************/ + +const int ARMOR_TYPE_CLOTH = 0; +const int ARMOR_TYPE_LIGHT = 1; +const int ARMOR_TYPE_MEDIUM = 2; +const int ARMOR_TYPE_HEAVY = 3; + +const int ACTION_USE_ITEM_TMI_LIMIT = 1500; + +/*********************\ +* Function Prototypes * +\*********************/ + +/** + * Returns the greater of the two values passed to it. + * + * @param a An integer + * @param b Another integer + * @return a iff a is greater than b, otherwise b + */ +int PRCMax(int a, int b); + +/** + * Returns the lesser of the two values passed to it. + * + * @param a An integer + * @param b Another integer + * @return a iff a is lesser than b, otherwise b + */ +int PRCMin(int a, int b); + +/** + * Returns the greater of the two values passed to it. + * + * @param a A float + * @param b Another float + * @return a iff a is greater than b, otherwise b + */ +float PRCFmax(float a, float b); + +/** + * Returns the lesser of the two values passed to it. + * + * @param a A float + * @param b Another float + * @return a iff a is lesser than b, otherwise b + */ +float PRCFmin(float a, float b); + +/** + * Takes a string in the standard hex number format (0x####) and converts it + * into an integer type value. Only the last 8 characters are parsed in order + * to avoid overflows. + * If the string is not parseable (empty or contains characters other than + * those used in hex notation), the function errors and returns 0. + * + * Full credit to Axe Murderer + * + * @param sHex The string to convert + * @return Integer value of sHex or 0 on error + */ +int HexToInt(string sHex); + +/* NOTE: the following 2 functions don't actually do what they say and + use real time not game time. Possibly because by default, 1 in-game minute + is 2 seconds. As real-time minutes to sec function exists (TurnsToSeconds()), + this is possibly redundant and should be replaced. + + // Use HoursToSeconds to figure out how long a scaled minute + // is and then calculate the number of real seconds based + // on that. + float scaledMinute = HoursToSeconds(1) / 60.0; + float totalMinutes = minutes * scaledMinute; + + // Return our scaled duration, but before doing so check to make sure + // that it is at least as long as a round / level (time scale is in + // the module properties, it's possible a minute / level could last less + // time than a round / level !, so make sure they get at least as much + // time as a round / level. + float totalRounds = RoundsToSeconds(minutes); + float result = totalMinutes > totalRounds ? totalMinutes : totalRounds; + return result; +*/ + +/** + * Takes an int representing the number of scaled 1 minute intervals wanted + * and converts to seconds with 1 turn = 1 minute + * @param minutes The number of 1 min intervals (typically caster level) + * @return Float of duration in seconds + */ +float MinutesToSeconds(int minutes); + +/** + * Takes an int representing the number of scaled 10 minute intervals wanted + * and converts to seconds with 1 turn = 1 minute + * @param tenMinutes The number of 10 min intervals (typically caster level) + * @return Float of duration in seconds + */ +float TenMinutesToSeconds(int tenMinutes); + +/** + * Converts metres to feet. Moved from prc_inc_util. + * @param fMeters distance in metres + * @return float of distance in feet + */ +float MetersToFeet(float fMeters); + +/** + * Checks whether an alignment matches given restrictions. + * For example + * GetIsValidAlignment (ALIGNMENT_CHAOTIC, ALIGNMENT_GOOD, 21, 3, 0 ); + * should return FALSE. + * + * Credit to Joe Travel + * + * @param iLawChaos ALIGNMENT_* constant + * @param iGoodEvil ALIGNMENT_* constant + * @param iAlignRestrict Similar format as the restrictions in classes.2da + * @param iAlignRstrctType Similar format as the restrictions in classes.2da + * @param iInvertRestriction Similar format as the restrictions in classes.2da + * + * @return TRUE if the alignment does not break the restrictions, + * FALSE otherwise. + */ +int GetIsValidAlignment( int iLawChaos, int iGoodEvil, int iAlignRestrict, int iAlignRstrctType, int iInvertRestriction ); + +/** + * Gets a random location within an circular area around a base location. + * + * by Mixcoatl + * download from + * http://nwvault.ign.com/Files/scripts/data/1065075424375.shtml + * + * @param lBase The center of the circle. + * @param fDistance The radius of the circle. ie, the maximum distance the + * new location may be from lBase. + * + * @return A location in random direction from lBase between + * 0 and fDistance meters away. + */ +location GetRandomCircleLocation(location lBase, float fDistance=1.0); + +/** + * Gets a location relative to the first location + * Includes rotating additional location based on facing of the first + * + * @param lMaster The starting location + * @param lAdd The location to add + * + * @return A location in random direction from lBase between + * 0 and fDistance meters away. + */ +location AddLocationToLocation(location lMaster, location lAdd); + +/** + * Genji Include Color gen_inc_color + * first: 1-4-03 + * simple function to use the name of a item holding escape sequences that, though they will not compile, + * they can be interpreted at run time and produce rbg scales between 32 and 255 in increments. + * -- allows 3375 colors to be made. + * for example SendMessageToPC(pc,PRCGetRGB(15,15,1)+ "Help, I'm on fire!") will produce yellow text. + * more examples: + * + * PRCGetRGB() := WHITE // no parameters, default is white + * PRCGetRGB(15,15,1):= YELLOW + * PRCGetRGB(15,5,1) := ORANGE + * PRCGetRGB(15,1,1) := RED + * PRCGetRGB(7,7,15) := BLUE + * PRCGetRGB(1,15,1) := NEON GREEN + * PRCGetRGB(1,11,1) := GREEN + * PRCGetRGB(9,6,1) := BROWN + * PRCGetRGB(11,9,11):= LIGHT PURPLE + * PRCGetRGB(12,10,7):= TAN + * PRCGetRGB(8,1,8) := PURPLE + * PRCGetRGB(13,9,13):= PLUM + * PRCGetRGB(1,7,7) := TEAL + * PRCGetRGB(1,15,15):= CYAN + * PRCGetRGB(1,1,15) := BRIGHT BLUE + * + * issues? contact genji@thegenji.com + * special thanks to ADAL-Miko and Rich Dersheimer in the bio forums. + */ +string PRCGetRGB(int red = 15,int green = 15,int blue = 15); + +/** + * Checks if any PCs (or optionally their NPC party members) are in the + * given area. + * + * @param oArea The area to check + * @param bNPCPartyMembers Whether to check the PC's party members, too + */ +int GetIsAPCInArea(object oArea, int bNPCPartyMembers = TRUE); + +/** + * Converts the given integer to string as IntToString and then + * pads the left side until it's nLength characters long. If sign + * is specified, the first character is reserved for it, and it is + * always present. + * Strings longer than the given length are trunctated to their nLength + * right characters. + * + * credit goes to Pherves, who posted the original in homebrew scripts forum sticky + * + * @param nX The integer to convert + * @param nLength The length of the resulting string + * @param nSigned If this is TRUE, a sign character is inserted as the leftmost + * character. Doing so leaves one less character for use as a digit. + * + * @return The string that results from conversion as specified above. + */ +string IntToPaddedString(int nX, int nLength = 4, int nSigned = FALSE); + +/** + * Looks through the given string, replacing all instances of sToReplace with + * sReplacement. If such a replacement creates another instance of sToReplace, + * it, too is replaced. Be aware that you can cause an infinite loop with + * properly constructed parameters due to this. + * + * @param sString The string to modify + * @param sToReplace The substring to replace + * @param sReplacement The replacement string + * @return sString with all instances of sToReplace replaced + * with sReplacement + */ +string ReplaceChars(string sString, string sToReplace, string sReplacement); + +/** + * A wrapper for DestroyObject(). Attempts to bypass any + * conditions that might prevent destroying the object. + * + * WARNING: This will destroy any object that can at all be + * destroyed by DestroyObject(). In other words, you + * can clobber critical bits with careless use. + * Only the module, PCs and areas are unaffected. Using this + * function on any of those will cause an infinite + * DelayCommand loop that will eat up resources, though. + * + * + * @param oObject The object to destroy + */ +void MyDestroyObject(object oObject); + +/** + * Checks to see if oPC has an item created by sResRef in his/her/it's inventory + * + * @param oPC The creature whose inventory to search. + * @param sResRef The resref to look for in oPC's items. + * @return TRUE if any items matching sResRef were found, FALSE otherwise. + */ +int GetHasItem(object oPC, string sResRef); + +/** + * Calculates the base AC of the given armor. + * + * @param oArmor An item of type BASE_ITEM_ARMOR + * @return The base AC of oArmor, or -1 on error + */ +int GetItemACBase(object oArmor); + +/** + * Gets the type of the given armor based on it's base AC. + * + * @param oArmor An item of type BASE_ITEM_ARMOR + * @return ARMOR_TYPE_* constant of the armor, or -1 on error + */ +int GetArmorType(object oArmor); + +/** + * Calculates the number of steps along both moral and ethical axes that + * the two target's alignments' differ. + * + * @param oSource A creature + * @param oTarget Another creature + * @return The number of steps the target's alignment differs + */ +int CompareAlignment(object oSource, object oTarget); + +/** + * Repeatedly assigns an equipping action to equip the given item until + * it is equipped. Used for getting around the fact that a player can + * cancel the action. They will give up eventually :D + * + * WARNING: Note that forcing an equip into offhand when mainhand is empty + * will result in an infinite loop. So will attempting to equip an item + * into a slot it can't be equipped in. + * + * @param oPC The creature to do the equipping. + * @param oItem The item to equip. + * @param nSlot INVENTORY_SLOT_* constant of the slot to equip into. + * @param nThCall Internal parameter, leave as default. This determines + * how many times ForceEquip has called itself. + */ +void ForceEquip(object oPC, object oItem, int nSlot, int nThCall = 0); + +/** + * Repeatedly attempts to unequip the given item until it is no longer + * in the slot given. Used for getting around the fact that a player can + * cancel the action. They will give up eventually :D + * + * @param oPC The creature to do the unequipping. + * @param oItem The item to unequip. + * @param nSlot INVENTORY_SLOT_* constant of the slot containing oItem. + * @param nThCall Internal parameter, leave as default. This determines + * how many times ForceUnequip has called itself. + */ +void ForceUnequip(object oPC, object oItem, int nSlot, int nThCall = 0); + +/** + * Checks either of the given creature's hand slots are empty. + * + * @param oCreature Creature whose hand slots to check + * @return TRUE if either hand slot is empty, FALSE otherwise + */ +int GetHasFreeHand(object oCreature); + +/** + * Determines whether the creature is encumbered by it's carried items. + * + * @param oCreature Creature whose encumberment to determine + * @return TRUE if the creature is encumbered, FALSE otherwise + */ +int GetIsEncumbered(object oCreature); + +/** + * Try to identify all unidentified objects within the given creature's inventory + * using it's skill ranks in lore. + * + * @param oPC The creature whose items to identify + */ +void TryToIDItems(object oPC = OBJECT_SELF); + +/** + * Converts a boolean to a string. + * + * @param bool The boolean value to convert. 0 is considered false + * and everything else is true. + * @param bTLK Whether to use english strings or get the values from + * the TLK. If TRUE, the return values are retrieved + * from TLK indices 8141 and 8142. If FALSE, return values + * are either "True" or "False". + * Defaults to FALSE. + * @see DebugBool2String() in inc_debug for debug print purposes + */ +string BooleanToString(int bool, int bTLK = FALSE); + +/** + * Returns a copy of the string, with leading and trailing whitespace omitted. + * + * @param s The string to trim. + */ +string PRCTrimString(string s); + +/** + * Compares the given two strings lexicographically. + * Returns -1 if the first string precedes the second. + * Returns 0 if the strings are equal + * Returns 1 if the first string follows the second. + * + * Examples: + * + * StringCompare("a", "a") = 0 + * StringCompare("a", "b") = -1 + * StringCompare("b", "a") = 1 + * StringCompare("a", "1") = 1 + * StringCompare("A", "a") = -1 + * StringCompare("Aa", "A") = 1 + */ +int StringCompare(string s1, string s2); + +/** + * Finds first occurrence of string sFind + * in string sString and replaces it with + * sReplace and returns the result. + * If sFind is not found, sString is returned. + * + * Examples: + * + * StringCompare("aabb", "a", "y") = "yabb" + * StringCompare("aabb", "x", "y") = "aabb" + */ +string ReplaceString(string sString, string sFind, string sReplace); + +/** + * Determines the angle between two given locations. Angle returned + * is relative to the first location. + * + * @param lFrom The base location + * @param lTo The other location + * @return The angle between the two locations, relative to lFrom + */ +float GetRelativeAngleBetweenLocations(location lFrom, location lTo); + +/** + * Returns the same string you would get if you examined the item in-game + * Uses 2da & tlk lookups and should work for custom itemproperties too + * + * @param ipTest Itemproperty you want to get the string of + * + * @return A string of the itemproperty, including spaces and bracket where appropriate + */ +string ItemPropertyToString(itemproperty ipTest); + + +/** + * Tests if a creature can burn the amount of XP specified without loosing a level + * + * @param oPC Creature to test, can be an NPC or a PC + * @param nCost Amount of XP to chck for + * + * @return TRUE/FALSE + */ +int GetHasXPToSpend(object oPC, int nCost); + + +/** + * Removes an amount of XP via SetXP() + * + * @param oPC Creature to remove XP from, can be an NPC or a PC + * @param nCost Amount of XP to remove for + */ +void SpendXP(object oPC, int nCost); + + +/** + * Tests if a creature can burn the amount of Gold specified + * + * @param oPC Creature to test, can be an NPC or a PC + * @param nCost Amount of Gold to chck for + * + * @return TRUE/FALSE + */ +int GetHasGPToSpend(object oPC, int nCost); + + +/** + * Removes an amount of Gold + * + * @param oPC Creature to remove Gold from, can be an NPC or a PC + * @param nCost Amount of Gold to remove for + */ +void SpendGP(object oPC, int nCost); + +/* + * Convinence function for testing off-hand weapons + */ +int isNotShield(object oItem); + +/** + * Makes self use a specific itemproperty on an object + * + * Note: This uses a loop so vulnerable to TMI errors + * Note: This is not 100% reliable, for example if uses/day finished + * Note: Uses talent system. Unsure what would happen if the creature + * can cast the same spell from some other means or if they + * had multiple items with the same spell on them + * + * @param oItem Item to use + * @param ipIP Itemproperty to use + * @param oTarget Target object + */ +void ActionUseItemPropertyAtObject(object oItem, itemproperty ipIP, object oTarget = OBJECT_SELF); + +/** + * Makes self use a specific itemproperty at a location + * + * Note: This uses a loop so vulnerable to TMI errors + * Note: This is not 100% reliable, for example if uses/day finished + * Note: Uses talent system. Unsure what would happen if the creature + * can cast the same spell from some other means or if they + * had multiple items with the same spell on them + * + * @param oItem Item to use + * @param ipIP Itemproperty to use + * @param lTarget Target location + */ +void ActionUseItemPropertyAtLocation(object oItem, itemproperty ipIP, location lTarget); + +// Checks the target for a specific EFFECT_TYPE constant value +int PRCGetHasEffect(int nEffectType, object oTarget = OBJECT_SELF); + +//Does a check to determine if the NPC has an attempted +//spell or attack target +int PRCGetIsFighting(object oFighting = OBJECT_SELF); + +// Returns TRUE if the player is polymorphed. +int GetIsPolyMorphedOrShifted(object oCreature); + +/** + * Gets a random delay based on the parameters passed in. + * + * @author Bioware (GetRandomDelay() from nw_i0_spells) + * + * @param fMinimumTime lower limit for the random time + * @param fMaximumTime upper limit for the random time + * + * @return random float between the limits given + */ +float PRCGetRandomDelay(float fMinimumTime = 0.4, float fMaximumTime = 1.1); + +//this is here rather than inc_utility because it uses creature size and screws compiling if its elsewhere +/** + * Returns the skill rank adjusted according to the given parameters. + * Using the default values, the result is the same as using GetSkillRank(). + * + * @param oObject subject to get skill of + * @param nSkill SKILL_* constant + * @param bSynergy include any applicable synergy bonus + * @param bSize include any applicable size bonus + * @param bAbilityMod include relevant ability modification (including effects on that ability) + * @param bEffect include skill changing effects and itemproperties + * @param bArmor include armor mod if applicable (excluding shield) + * @param bShield include shield mod if applicable (excluding armor) + * @param bFeat include any applicable feats, including racial ones + * + * @return subject's rank in the given skill, modified according to + * the above parameters. If the skill is trained-only and the + * subject does not have any ranks in it, returns 0. + */ +int GetSkill(object oObject, int nSkill, int bSynergy = FALSE, int bSize = FALSE, + int bAbilityMod = TRUE, int bEffect = TRUE, int bArmor = TRUE, + int bShield = TRUE, int bFeat = TRUE); + +/** + * Repeatedly attempts to put down the given item until it is no longer + * in the slot given. Used for getting around the fact that a player can + * cancel the action. They will give up eventually :D + * + * @param oPC The creature to do the putting down. + * @param oItem The item to put down. + * @param nSlot INVENTORY_SLOT_* constant of the slot containing oItem. + * @param nThCall Internal parameter, leave as default. This determines + * how many times ForcePutDown has called itself. + */ +void ForcePutDown(object oPC, object oItem, int nSlot, int nThCall = 0); + +/////////////////////////////////////// +/* Constant declarations */ +/////////////////////////////////////// + +const int ERROR_CODE_5_ONCE_MORE = -1; +const int ERROR_CODE_5_ONCE_MORE2 = -1; +const int ERROR_CODE_5_ONCE_MORE3 = -1; +const int ERROR_CODE_5_ONCE_MORE4 = -1; +const int ERROR_CODE_5_ONCE_MORE5 = -1; + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +// The following files have no dependecies, or self-contained dependencies that do not require looping via this file +// inc_debug is available via inc_2dacache +//#include "inc_debug" + +#include "prc_inc_nwscript" +#include "prc_class_const" +#include "inc_target_list" +#include "inc_logmessage" +#include "inc_threads" +#include "prc_inc_actions" +#include "inc_time" +#include "inc_draw_prc" +#include "inc_eventhook" +#include "inc_metalocation" +#include "inc_array_sort" // Depends on prc_inc_array and inc_debug +#include "inc_uniqueid" // Depends on prc_inc_array +#include "inc_set" // Depends on prc_inc_array, inc_heap + + +/**********************\ +* Function Definitions * +\**********************/ + +int PRCMax(int a, int b) {return (a > b ? a : b);} + +int PRCMin(int a, int b) {return (a < b ? a : b);} + +float PRCFmax(float a, float b) {return (a > b ? a : b);} + +float PRCFmin(float a, float b) {return (a < b ? a : b);} + +int HexToInt_old(string sHex) +{ + if(sHex == "") return 0; // Some quick optimisation for empty strings + sHex = GetStringRight(GetStringLowerCase(sHex), 8); // Truncate to last 8 characters and convert to lowercase + if(GetStringLeft(sHex, 2) == "0x") // Cut out '0x' if it's present + sHex = GetStringRight(sHex, GetStringLength(sHex) - 2); + string sConvert = "0123456789abcdef"; // The string to index using the characters in sHex + int nReturn, nHalfByte; + while(sHex != "") + { + nHalfByte = FindSubString(sConvert, GetStringLeft(sHex, 1)); // Get the value of the next hexadecimal character + if(nHalfByte == -1) return 0; // Invalid character in the string! + nReturn = nReturn << 4; // Rightshift by 4 bits + nReturn |= nHalfByte; // OR in the next bits + sHex = GetStringRight(sHex, GetStringLength(sHex) - 1); // Remove the parsed character from the string + } + + return nReturn; +} + +const string sHexDigits = "0123456789abcdef"; + +int HexToInt(string sHex) +{ + if(sHex == "") return 0; + int nVal = 0; + string sDig = GetStringLowerCase(sHex); + int nLen = GetStringLength(sDig); + int nIdx = 2; + while(nIdx < nLen) + { + nVal = (nVal << 4) + FindSubString(sHexDigits,GetSubString(sDig,nIdx++,1)); + } + return nVal; +} + +float TenMinutesToSeconds(int tenMinutes) +{ + return TurnsToSeconds(tenMinutes) * 10; +} + +float MinutesToSeconds(int minutes) +{ + return TurnsToSeconds(minutes); +} + +float MetersToFeet(float fMeters) +{ + fMeters *= 3.281; + return fMeters; +} + +int GetIsValidAlignment ( int iLawChaos, int iGoodEvil,int iAlignRestrict, int iAlignRstrctType, int iInvertRestriction ) +{ + //deal with no restrictions first + if(iAlignRstrctType == 0) + return TRUE; + //convert the ALIGNMENT_* into powers of 2 + iLawChaos = FloatToInt(pow(2.0, IntToFloat(iLawChaos-1))); + iGoodEvil = FloatToInt(pow(2.0, IntToFloat(iGoodEvil-1))); + //initialise result varaibles + int iAlignTest, iRetVal = TRUE; + //do different test depending on what type of restriction + if(iAlignRstrctType == 1 || iAlignRstrctType == 3) //I.e its 1 or 3 + iAlignTest = iLawChaos; + if(iAlignRstrctType == 2 || iAlignRstrctType == 3) //I.e its 2 or 3 + iAlignTest = iAlignTest | iGoodEvil; + //now the real test. + if(iAlignRestrict & iAlignTest)//bitwise AND comparison + iRetVal = FALSE; + //invert it if applicable + if(iInvertRestriction) + iRetVal = !iRetVal; + //and return the result + return iRetVal; +} + + +location GetRandomCircleLocation(location lBase, float fDistance=1.0) +{ + // Pick a random angle for the location. + float fAngle = IntToFloat(Random(3600)) / 10.0; + + // Pick a random facing for the location. + float fFacing = IntToFloat(Random(3600)) / 10.0; + + // Pick a random distance from the base location. + float fHowFar = IntToFloat(Random(FloatToInt(fDistance * 10.0))) / 10.0; + + // Retreive the position vector from the location. + vector vPosition = GetPositionFromLocation(lBase); + + // Modify the base x/y position by the distance and angle. + vPosition.y += (sin(fAngle) * fHowFar); + vPosition.x += (cos(fAngle) * fHowFar); + + // Return the new random location. + return Location(GetAreaFromLocation(lBase), vPosition, fFacing); +} + +location AddLocationToLocation(location lMaster, location lAdd) +{ + //firstly rotate lAdd according to lMaster + vector vAdd = GetPositionFromLocation(lAdd); + //zero is +y in NWN convert zero to +x + float fAngle = GetFacingFromLocation(lMaster); + //convert angle to radians + fAngle = ((fAngle-90)/360.0)*2.0*PI; + vector vNew; + vNew.x = (vAdd.x*cos(fAngle))-(vAdd.y*sin(fAngle)); + vNew.y = (vAdd.x*sin(fAngle))+(vAdd.y*cos(fAngle)); + vNew.z = vAdd.z; + + //now just add them on + vector vMaster = GetPositionFromLocation(lMaster); + vNew.x += vMaster.x; + vNew.y += vMaster.y; + vNew.z += vMaster.z; + float fNew = GetFacingFromLocation(lAdd)+GetFacingFromLocation(lMaster); + + //return a location + location lReturn = Location(GetAreaFromLocation(lMaster), vNew, fNew); + return lReturn; +} + + + + + +string PRCGetRGB(int red = 15,int green = 15,int blue = 15) +{ + object coloringBook = GetObjectByTag("ColoringBook"); + if (coloringBook == OBJECT_INVALID) + coloringBook = CreateObject(OBJECT_TYPE_ITEM,"gen_coloringbook",GetLocation(GetObjectByTag("HEARTOFCHAOS"))); + string buffer = GetName(coloringBook); + if(red > 15) red = 15; if(green > 15) green = 15; if(blue > 15) blue = 15; + if(red < 1) red = 1; if(green < 1) green = 1; if(blue < 1) blue = 1; + return ""; +} + +int GetIsAPCInArea(object oArea, int bNPCPartyMembers = TRUE) +{ + object oPC = GetFirstPC(); + while (GetIsObjectValid(oPC)) + { + if(bNPCPartyMembers) + { + object oFaction = GetFirstFactionMember(oPC, FALSE); + while(GetIsObjectValid(oFaction)) + { + if (GetArea(oFaction) == oArea) + return TRUE; + oFaction = GetNextFactionMember(oPC, FALSE); + } + } + oPC = GetNextPC(); + } + return FALSE; +} + +string IntToPaddedString(int nX, int nLength = 4, int nSigned = FALSE) +{ + if(nSigned) + nLength--;//to allow for sign + string sResult = IntToString(nX); + // Trunctate to nLength rightmost characters + if(GetStringLength(sResult) > nLength) + sResult = GetStringRight(sResult, nLength); + // Pad the left side with zero + while(GetStringLength(sResult) < nLength) + { + sResult = "0" +sResult; + } + if(nSigned) + { + if(nX>=0) + sResult = "+"+sResult; + else + sResult = "-"+sResult; + } + return sResult; +} + +string ReplaceChars(string sString, string sToReplace, string sReplacement) +{ + int nInd; + while((nInd = FindSubString(sString, sToReplace)) != -1) + { + sString = GetStringLeft(sString, nInd) + + sReplacement + + GetSubString(sString, + nInd + GetStringLength(sToReplace), + GetStringLength(sString) - nInd - GetStringLength(sToReplace) + ); + } + return sString; +} + +void MyDestroyObject(object oObject) +{ + if(GetIsObjectValid(oObject)) + { + SetCommandable(TRUE ,oObject); + AssignCommand(oObject, ClearAllActions()); + AssignCommand(oObject, SetIsDestroyable(TRUE, FALSE, FALSE)); + AssignCommand(oObject, DestroyObject(oObject)); + // May not necessarily work on first iteration + DestroyObject(oObject); + DelayCommand(0.1f, MyDestroyObject(oObject)); + } +} + +int GetHasItem(object oPC, string sResRef) +{ + object oItem = GetFirstItemInInventory(oPC); + + while(GetIsObjectValid(oItem) && GetResRef(oItem) != sResRef) + oItem = GetNextItemInInventory(oPC); + + return GetResRef(oItem) == sResRef; +} + +int GetItemACBase(object oArmor) +{ + int nBonusAC = 0; + + // oItem is not armor then return an error + if(GetBaseItemType(oArmor) != BASE_ITEM_ARMOR) + return -1; + + // check each itemproperty for AC Bonus + itemproperty ipAC = GetFirstItemProperty(oArmor); + + while(GetIsItemPropertyValid(ipAC)) + { + int nType = GetItemPropertyType(ipAC); + + // check for ITEM_PROPERTY_AC_BONUS + if(nType == ITEM_PROPERTY_AC_BONUS) + { + nBonusAC = GetItemPropertyCostTableValue(ipAC); + break; + } + + // get next itemproperty + ipAC = GetNextItemProperty(oArmor); + } + + // return base AC + return GetItemACValue(oArmor) - nBonusAC; +} + +// returns -1 on error, or the const int ARMOR_TYPE_* +int GetArmorType(object oArmor) +{ + int nType = -1; + + // get and check Base AC + switch(GetItemACBase(oArmor) ) + { + case 0: nType = ARMOR_TYPE_CLOTH; break; + case 1: nType = ARMOR_TYPE_LIGHT; break; + case 2: nType = ARMOR_TYPE_LIGHT; break; + case 3: nType = ARMOR_TYPE_LIGHT; break; + case 4: nType = ARMOR_TYPE_MEDIUM; break; + case 5: nType = ARMOR_TYPE_MEDIUM; break; + case 6: nType = ARMOR_TYPE_HEAVY; break; + case 7: nType = ARMOR_TYPE_HEAVY; break; + case 8: nType = ARMOR_TYPE_HEAVY; break; + } + + // return type + return nType; +} + +int CompareAlignment(object oSource, object oTarget) +{ + int iStepDif; + int iGE1 = GetAlignmentGoodEvil(oSource); + int iLC1 = GetAlignmentLawChaos(oSource); + int iGE2 = GetAlignmentGoodEvil(oTarget); + int iLC2 = GetAlignmentLawChaos(oTarget); + + if(iGE1 == ALIGNMENT_GOOD){ + if(iGE2 == ALIGNMENT_NEUTRAL) + iStepDif += 1; + else if(iGE2 == ALIGNMENT_EVIL) + iStepDif += 2; + } + else if(iGE1 == ALIGNMENT_NEUTRAL){ + if(iGE2 != ALIGNMENT_NEUTRAL) + iStepDif += 1; + } + else if(iGE1 == ALIGNMENT_EVIL){ + if(iLC2 == ALIGNMENT_NEUTRAL) + iStepDif += 1; + else if(iLC2 == ALIGNMENT_GOOD) + iStepDif += 2; + } + if(iLC1 == ALIGNMENT_LAWFUL){ + if(iLC2 == ALIGNMENT_NEUTRAL) + iStepDif += 1; + else if(iLC2 == ALIGNMENT_CHAOTIC) + iStepDif += 2; + } + else if(iLC1 == ALIGNMENT_NEUTRAL){ + if(iLC2 != ALIGNMENT_NEUTRAL) + iStepDif += 1; + } + else if(iLC1 == ALIGNMENT_CHAOTIC){ + if(iLC2 == ALIGNMENT_NEUTRAL) + iStepDif += 1; + else if(iLC2 == ALIGNMENT_LAWFUL) + iStepDif += 2; + } + return iStepDif; +} + +void ForceEquip(object oPC, object oItem, int nSlot, int nThCall = 0) +{ + // Sanity checks + // Make sure the parameters are valid + if(!GetIsObjectValid(oPC)) return; + if(!GetIsObjectValid(oItem)) return; + // Make sure that the object we are attempting equipping is the latest one to be ForceEquipped into this slot + if(GetIsObjectValid(GetLocalObject(oPC, "ForceEquipToSlot_" + IntToString(nSlot))) + && + GetLocalObject(oPC, "ForceEquipToSlot_" + IntToString(nSlot)) != oItem + ) + return; + // Fail on non-commandable NPCs after ~1min + if(!GetIsPC(oPC) && !GetCommandable(oPC) && nThCall > 60) + { + WriteTimestampedLogEntry("ForceEquip() failed on non-commandable NPC: " + DebugObject2Str(oPC) + " for item: " + DebugObject2Str(oItem)); + return; + } + + float fDelay; + + // Check if the equipping has already happened + if(GetItemInSlot(nSlot, oPC) != oItem) + { + // Test and increment the control counter + if(nThCall++ == 0) + { + // First, try to do the equipping non-intrusively and give the target a reasonable amount of time to do it + AssignCommand(oPC, ActionEquipItem(oItem, nSlot)); + fDelay = 1.0f; + + // Store the item to be equipped in a local variable to prevent contest between two different calls to ForceEquip + SetLocalObject(oPC, "ForceEquipToSlot_" + IntToString(nSlot), oItem); + } + else + { + // Nuke the target's action queue. This should result in "immediate" equipping of the item + if(GetIsPC(oPC) || nThCall > 5) // Skip nuking NPC action queue at first, since that can cause problems. 5 = magic number here. May need adjustment + { + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionEquipItem(oItem, nSlot)); + } + // Use a lenghtening delay in order to attempt handling lag and possible other interference. From 0.1s to 1s + fDelay = PRCMin(nThCall, 10) / 10.0f; + } + + // Loop + DelayCommand(fDelay, ForceEquip(oPC, oItem, nSlot, nThCall)); + } + // It has, so clean up + else + DeleteLocalObject(oPC, "ForceEquipToSlot_" + IntToString(nSlot)); +} + +void ForceUnequip(object oPC, object oItem, int nSlot, int nThCall = 0) +{ + // Sanity checks + if(!GetIsObjectValid(oPC)) return; + if(!GetIsObjectValid(oItem)) return; + // Fail on non-commandable NPCs after ~1min + if(!GetIsPC(oPC) && !GetCommandable(oPC) && nThCall > 60) + { + WriteTimestampedLogEntry("ForceUnequip() failed on non-commandable NPC: " + DebugObject2Str(oPC) + " for item: " + DebugObject2Str(oItem)); + return; + } + + float fDelay; + + // Delay the first unequipping call to avoid a bug that occurs when an object that was just equipped is unequipped right away + // - The item is not unequipped properly, leaving some of it's effects in the creature's stats and on it's model. + if(nThCall == 0) + { + //DelayCommand(0.5, ForceUnequip(oPC, oItem, nSlot, FALSE)); + fDelay = 0.5; + } + else if(GetItemInSlot(nSlot, oPC) == oItem) + { + // Attempt to avoid interference by not clearing actions before the first attempt + if(nThCall > 1) + if(GetIsPC(oPC) || nThCall > 5) // Skip nuking NPC action queue at first, since that can cause problems. 5 = magic number here. May need adjustment + AssignCommand(oPC, ClearAllActions()); + + AssignCommand(oPC, ActionUnequipItem(oItem)); + + // Ramp up the delay if the action is not getting through. Might let whatever is intefering finish + fDelay = PRCMin(nThCall, 10) / 10.0f; + } + // The item has already been unequipped + else + return; + + // Loop + DelayCommand(fDelay, ForceUnequip(oPC, oItem, nSlot, ++nThCall)); +} + +int GetHasFreeHand(object oCreature) +{ + return !GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature)) + || !GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature)); +} + +int GetIsEncumbered(object oCreature) +{ + int iStrength = GetAbilityScore(oCreature, ABILITY_STRENGTH); + if(iStrength > 50) + return FALSE; // encumbrance.2da doesn't go that high, so automatic success + + return GetWeight(oCreature) > StringToInt(Get2DACache("encumbrance", "Normal", iStrength)); +} + +void TryToIDItems(object oPC = OBJECT_SELF) +{ + int nGP; + string sMax = Get2DACache("SkillVsItemCost", "DeviceCostMax", GetSkillRank(SKILL_LORE, oPC)); + int nMax = StringToInt(sMax); + if (sMax == "") nMax = 120000000; + object oItem = GetFirstItemInInventory(oPC); + while(GetIsObjectValid(oItem)) + { + if(!GetIdentified(oItem)) + { + // Check for the value of the item first. + SetIdentified(oItem, TRUE); + nGP = GetGoldPieceValue(oItem); + // If oPC has enough Lore skill to ID the item, then do so. + if(nMax >= nGP) + SendMessageToPC(oPC, GetStringByStrRef(16826224) + " " + GetName(oItem) + " " + GetStringByStrRef(16826225)); + else + SetIdentified(oItem, FALSE); + } + oItem = GetNextItemInInventory(oPC); + } +} + +string BooleanToString(int bool, int bTLK = FALSE) +{ + return bTLK ? + (bool ? GetStringByStrRef(8141) : GetStringByStrRef(8142)): + (bool ? "True" : "False"); +} + +string PRCTrimString(string s) +{ + int nCrop = 0; + string temp; + // Find end of the leading whitespace + while(TRUE) + { + // Get the next character in the string, starting from the beginning + temp = GetSubString(s, nCrop, 1); + if(temp == " " || // Space + temp == "\n") // Line break + nCrop++; + else + break; + } + // Crop the leading whitespace + s = GetSubString(s, nCrop, GetStringLength(s) - nCrop); + + // Find the beginning of the trailing whitespace + nCrop = 0; + while(TRUE) + { + // Get the previous character in the string, starting from the end + temp = GetSubString(s, GetStringLength(s) - 1 - nCrop, 1); + if(temp == " " || // Space + temp == "\n") // Line break + nCrop++; + else + break; + } + // Crop the trailing whitespace + s = GetSubString(s, 0, GetStringLength(s) - nCrop); + + return s; +} + +int GetFirstCharacterIndex(string s1) +{ + object oLookup = GetWaypointByTag("prc_str_lookup"); + if(!GetIsObjectValid(oLookup)) + oLookup = CreateObject(OBJECT_TYPE_WAYPOINT, "prc_str_lookup", GetLocation(GetObjectByTag("HEARTOFCHAOS"))); + + return GetLocalInt(oLookup, GetStringUpperCase(GetSubString(s1, 0, 1))); +} + +int StringCompare(string s1, string s2) +{ + object oLookup = GetWaypointByTag("prc_str_lookup"); + if(!GetIsObjectValid(oLookup)) + oLookup = CreateObject(OBJECT_TYPE_WAYPOINT, "prc_str_lookup", GetLocation(GetObjectByTag("HEARTOFCHAOS"))); + + // Start comparing + int nT, + i = 0, + nMax = PRCMin(GetStringLength(s1), GetStringLength(s2)); + while(i < nMax) + { + // Get the difference between the values of i:th characters + nT = GetLocalInt(oLookup, GetSubString(s1, i, 1)) - GetLocalInt(oLookup, GetSubString(s2, i, 1)); + i++; + if(nT < 0) + return -1; + if(nT == 0) + continue; + if(nT > 0) + return 1; + } + + // The strings have the same base. Of such, the shorter precedes + nT = GetStringLength(s1) - GetStringLength(s2); + if(nT < 0) + return -1; + if(nT > 0) + return 1; + + // The strings were equal + return 0; +} + +string ReplaceString(string sString, string sFind, string sReplace) +{ + int n = FindSubString(sString, sFind); + if(n!=-1) + return GetStringLeft(sString, n) + sReplace + GetStringRight(sString, GetStringLength(sString) - GetStringLength(sFind) - n); + else + return sString; +} + +float GetRelativeAngleBetweenLocations(location lFrom, location lTo) +{ + vector vPos1 = GetPositionFromLocation(lFrom); + vector vPos2 = GetPositionFromLocation(lTo); + //sanity check + if(GetDistanceBetweenLocations(lFrom, lTo) == 0.0) + return 0.0; + + float fAngle = acos((vPos2.x - vPos1.x) / GetDistanceBetweenLocations(lFrom, lTo)); + // The above formula only returns values [0, 180], so test for negative y movement + if((vPos2.y - vPos1.y) < 0.0f) + fAngle = 360.0f -fAngle; + + return fAngle; +} + +string ItemPropertyToString(itemproperty ipTest) +{ + int nIPType = GetItemPropertyType(ipTest); + string sName = GetStringByStrRef(StringToInt(Get2DACache("itempropdef", "GameStrRef", nIPType))); + if(GetItemPropertySubType(ipTest) != -1)//nosubtypes + { + string sSubTypeResRef =Get2DACache("itempropdef", "SubTypeResRef", nIPType); + int nTlk = StringToInt(Get2DACache(sSubTypeResRef, "Name", GetItemPropertySubType(ipTest))); + if(nTlk > 0) + sName += " "+GetStringByStrRef(nTlk); + } + if(GetItemPropertyParam1(ipTest) != -1) + { + string sParamResRef =Get2DACache("iprp_paramtable", "TableResRef", GetItemPropertyParam1(ipTest)); + if(Get2DACache("itempropdef", "SubTypeResRef", nIPType) != "" + && Get2DACache(Get2DACache("itempropdef", "SubTypeResRef", nIPType), "TableResRef", GetItemPropertyParam1(ipTest)) != "") + sParamResRef =Get2DACache(Get2DACache("itempropdef", "SubTypeResRef", nIPType), "TableResRef", GetItemPropertyParam1(ipTest)); + int nTlk = StringToInt(Get2DACache(sParamResRef, "Name", GetItemPropertyParam1Value(ipTest))); + if(nTlk > 0) + sName += " "+GetStringByStrRef(nTlk); + } + if(GetItemPropertyCostTable(ipTest) != -1) + { + string sCostResRef =Get2DACache("iprp_costtable", "Name", GetItemPropertyCostTable(ipTest)); + int nTlk = StringToInt(Get2DACache(sCostResRef, "Name", GetItemPropertyCostTableValue(ipTest))); + if(nTlk > 0) + sName += " "+GetStringByStrRef(nTlk); + } + return sName; +} + +//Check for XP +int GetHasXPToSpend(object oPC, int nCost) +{ + // To be TRUE, make sure that oPC wouldn't lose a level by spending nCost. + int nHitDice = GetHitDice(oPC); + int nHitDiceXP = (500 * nHitDice * (nHitDice - 1)); // simplification of the sum + //get current XP + int nXP = GetXP(oPC); + if(!nXP) + nXP = GetLocalInt(oPC, "NPC_XP"); + //the test + if (nXP >= (nHitDiceXP + nCost)) + return TRUE; + return FALSE; +} + +//Spend XP +void SpendXP(object oPC, int nCost) +{ + if(nCost > 0) + { + if(GetXP(oPC)) + SetXP(oPC, GetXP(oPC) - nCost); + else if(GetLocalInt(oPC, "NPC_XP")) + SetLocalInt(oPC, "NPC_XP", GetLocalInt(oPC, "NPC_XP")-nCost); + } +} + +//Check for GP +int GetHasGPToSpend(object oPC, int nCost) +{ + //if its a NPC, get master + while(!GetIsPC(oPC) + && GetIsObjectValid(GetMaster(oPC))) + { + oPC = GetMaster(oPC); + } + //test if it has gold + if(GetIsPC(oPC)) + { + return GetGold(oPC) >= nCost; + } + //NPC in NPC faction + //cannot posses gold + return FALSE; +} + +//Spend GP +void SpendGP(object oPC, int nCost) +{ + if(nCost > 0) + { + //if its a NPC, get master + while(!GetIsPC(oPC) + && GetIsObjectValid(GetMaster(oPC))) + { + oPC = GetMaster(oPC); + } + TakeGoldFromCreature(nCost, oPC, TRUE); + } +} + + + +int isNotShield(object oItem) +{ + int iType = GetBaseItemType(oItem); + + return !(iType == BASE_ITEM_LARGESHIELD + || iType == BASE_ITEM_TOWERSHIELD + || iType == BASE_ITEM_SMALLSHIELD + // Added torches to the check as they should not count either + || iType == BASE_ITEM_TORCH); +} + + +void ActionUseItemPropertyAtObject(object oItem, itemproperty ipIP, object oTarget = OBJECT_SELF) +{ + int nIPSpellID = GetItemPropertySubType(ipIP); + string sSpellID = Get2DACache("iprp_spells", "SpellIndex", nIPSpellID); + int nSpellID = StringToInt(sSpellID); + string sCategory = Get2DACache("spells", "Category", nSpellID); + int nCategory = StringToInt(sCategory); + int nCategoryPotionRandom = FALSE; + //potions are strange + //seem to be hardcoded to certain categories + if(GetBaseItemType(oItem) == BASE_ITEM_POTIONS) + { + //potions are self-only + if(oTarget != OBJECT_SELF) + return; + + if(nCategory == TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT + || nCategory == TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH) + nCategory = TALENT_CATEGORY_BENEFICIAL_HEALING_POTION; + else if(nCategory == TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT + || nCategory == TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE) + nCategory = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_POTION; + else if(nCategory == TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT + || nCategory == TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE + || nCategory == TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF) + nCategory = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_POTION; + else if(nCategory == TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF + || nCategory == TALENT_CATEGORY_BENEFICIAL_PROTECTION_SINGLE + || nCategory == TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT) + nCategory = TALENT_CATEGORY_BENEFICIAL_PROTECTION_POTION; + else + { + //something odd here add strage randomized coding inside the loop + nCategoryPotionRandom = TRUE; + nCategory = TALENT_CATEGORY_BENEFICIAL_HEALING_POTION; + } + + } + + talent tItem; + tItem = GetCreatureTalentRandom(nCategory); + int nCount = 0; + while(GetIsTalentValid(tItem) + && nCount < ACTION_USE_ITEM_TMI_LIMIT) //this is the TMI limiting thing, change as appropriate + { + if(nCategoryPotionRandom) + { + switch(d4()) + { + default: + case 1: nCategory = TALENT_CATEGORY_BENEFICIAL_HEALING_POTION; break; + case 2: nCategory = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_POTION; break; + case 3: nCategory = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_POTION; break; + case 4: nCategory = TALENT_CATEGORY_BENEFICIAL_PROTECTION_POTION; break; + } + } + + if(GetTypeFromTalent(tItem) == TALENT_TYPE_SPELL + && GetIdFromTalent(tItem) == nSpellID) + { + ActionUseTalentOnObject(tItem, oTarget); + //end while loop + return; + } + nCount++; + tItem = GetCreatureTalentRandom(nCategory); + } + //if you got to this point, something whent wrong + //rather than failing silently, well log it + DoDebug("ERROR: ActionUseItemProperty() failed for "+GetName(OBJECT_SELF)+" using "+GetName(oItem)+" to cast "+IntToString(nSpellID)); +} + + +void ActionUseItemPropertyAtLocation(object oItem, itemproperty ipIP, location lTarget) +{ + int nIPSpellID = GetItemPropertySubType(ipIP); + string sSpellID = Get2DACache("iprp_spells", "SpellIndex", nIPSpellID); + int nSpellID = StringToInt(sSpellID); + string sCategory = Get2DACache("spells", "Category", nSpellID); + int nCategory = StringToInt(sCategory); + + //potions are odd + //but since they are self-only it doesnt matter + + talent tItem; + tItem = GetCreatureTalentRandom(nCategory); + int nCount = 0; + while(GetIsTalentValid(tItem) + && nCount < ACTION_USE_ITEM_TMI_LIMIT) //this is the TMI limiting thing, change as appropriate + { + if(GetTypeFromTalent(tItem) == TALENT_TYPE_SPELL + && GetIdFromTalent(tItem) == nSpellID) + { + ActionUseTalentAtLocation(tItem, lTarget); + //end while loop + return; + } + nCount++; + tItem = GetCreatureTalentRandom(nCategory); + } + //if you got to this point, something whent wrong + //rather than failing silently, well log it + DoDebug("ERROR: ActionUseItemProperty() failed for "+GetName(OBJECT_SELF)+" using "+GetName(oItem)+" to cast "+IntToString(nSpellID)); +} + + +//:://///////////////////////////////////////////// +//:: Get Has Effect +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Checks to see if the target has a given + spell effect +*/ +//::////////////////////////////////////////////// +//:: Created By: Preston Watamaniuk +//:: Created On: Oct 26, 2001 +//::////////////////////////////////////////////// +int PRCGetHasEffect(int nEffectType, object oTarget = OBJECT_SELF) +{ + effect eCheck = GetFirstEffect(oTarget); + while(GetIsEffectValid(eCheck)) + { + if(GetEffectType(eCheck) == nEffectType) + { + return TRUE; + } + eCheck = GetNextEffect(oTarget); + } + return FALSE; +} +// Test main +//void main(){} + +//:://///////////////////////////////////////////// +//:: PRCGetIsFighting +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Checks if the passed object has an Attempted + Attack or Spell Target +*/ +//::////////////////////////////////////////////// +//:: Created By: Preston Watamaniuk +//:: Created On: March 13, 2002 +//::////////////////////////////////////////////// +int PRCGetIsFighting(object oFighting = OBJECT_SELF) +{ + return GetIsObjectValid(GetAttemptedAttackTarget()) + || GetIsObjectValid(GetAttemptedSpellTarget()); +} + +// Determine whether the character is polymorphed or shfited. +int GetIsPolyMorphedOrShifted(object oCreature) +{ + int bPoly = FALSE; + + effect eChk = GetFirstEffect(oCreature); + + while (GetIsEffectValid(eChk)) + { + if (GetEffectType(eChk) == EFFECT_TYPE_POLYMORPH) + bPoly = TRUE; + + eChk = GetNextEffect(oCreature); + } + + if (GetPersistantLocalInt(oCreature, "nPCShifted")) + bPoly = TRUE; + + return bPoly; +} + +float PRCGetRandomDelay(float fMinimumTime = 0.4, float fMaximumTime = 1.1) +{ + float fRandom = fMaximumTime - fMinimumTime; + if(fRandom < 0.0) + { + return 0.0; + } + else + { + int nRandom; + nRandom = FloatToInt(fRandom * 10.0); + nRandom = Random(nRandom) + 1; + fRandom = IntToFloat(nRandom); + fRandom /= 10.0; + return fRandom + fMinimumTime; + } +} + +int GetSkill(object oObject, int nSkill, int bSynergy = FALSE, int bSize = FALSE, int bAbilityMod = TRUE, int bEffect = TRUE, int bArmor = TRUE, int bShield = TRUE, int bFeat = TRUE) +{ + if(!GetIsObjectValid(oObject)) + return 0; + if(!GetHasSkill(nSkill, oObject)) + return 0;//no skill set it to zero + int nSkillRank; //get the current value at the end, after effects are applied + if(bSynergy) + { + if(nSkill == SKILL_SET_TRAP + && GetSkill(oObject, SKILL_DISABLE_TRAP, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE) >= 5) + nSkillRank += 2; + if(nSkill == SKILL_DISABLE_TRAP + && GetSkill(oObject, SKILL_SET_TRAP, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE) >= 5) + nSkillRank += 2; + } + if(bSize) + if(nSkill == SKILL_HIDE)//only hide is affected by size + nSkillRank += (PRCGetCreatureSize(oObject)-3)*(-4); + if(!bAbilityMod) + { + string sAbility = Get2DACache("skills", "KeyAbility", nSkill); + int nAbility; + if(sAbility == "STR") + nAbility = ABILITY_STRENGTH; + else if(sAbility == "DEX") + nAbility = ABILITY_DEXTERITY; + else if(sAbility == "CON") + nAbility = ABILITY_CONSTITUTION; + else if(sAbility == "INT") + nAbility = ABILITY_INTELLIGENCE; + else if(sAbility == "WIS") + nAbility = ABILITY_WISDOM; + else if(sAbility == "CHA") + nAbility = ABILITY_CHARISMA; + nSkillRank -= GetAbilityModifier(nAbility, oObject); + } + if(!bEffect) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSkillIncrease(nSkill, 30), oObject, 0.001); + nSkillRank -= 30; + } + if(!bArmor + && GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CHEST, oObject)) + && Get2DACache("skills", "ArmorCheckPenalty", nSkill) == "1") + { + object oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oObject); + // Get the torso model number + int nTorso = GetItemAppearance( oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_TORSO); + // Read 2DA for base AC + // Can also use "parts_chest" which returns it as a "float" + int nACBase = StringToInt(Get2DACache( "des_crft_appear", "BaseAC", nTorso)); + int nSkillMod; + switch(nACBase) + { + case 0: nSkillMod = 0; break; + case 1: nSkillMod = 0; break; + case 2: nSkillMod = 0; break; + case 3: nSkillMod = -1; break; + case 4: nSkillMod = -2; break; + case 5: nSkillMod = -5; break; + case 6: nSkillMod = -7; break; + case 7: nSkillMod = -7; break; + case 8: nSkillMod = -8; break; + } + nSkillRank -= nSkillMod; + } + if(!bShield + && GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oObject)) + && Get2DACache("skills", "ArmorCheckPenalty", nSkill) == "1") + { + object oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oObject); + int nBase = GetBaseItemType(oItem); + int nSkillMod; + switch(nBase) + { + case BASE_ITEM_TOWERSHIELD: nSkillMod = -10; break; + case BASE_ITEM_LARGESHIELD: nSkillMod = -2; break; + case BASE_ITEM_SMALLSHIELD: nSkillMod = -1; break; + } + nSkillRank -= nSkillMod; + } + if(!bFeat) + { + int nSkillMod; + int nEpicFeat; + int nFocusFeat; + switch(nSkill) + { + case SKILL_ANIMAL_EMPATHY: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_ANIMAL_EMPATHY; + nFocusFeat = FEAT_SKILL_FOCUS_ANIMAL_EMPATHY; + break; + case SKILL_APPRAISE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_APPRAISE; + nFocusFeat = FEAT_SKILLFOCUS_APPRAISE; + if(GetHasFeat(FEAT_SILVER_PALM, oObject)) + nSkillMod += 2; + break; + case SKILL_BLUFF: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_BLUFF; + nFocusFeat = FEAT_SKILL_FOCUS_BLUFF; + break; + case SKILL_CONCENTRATION: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_CONCENTRATION; + nFocusFeat = FEAT_SKILL_FOCUS_CONCENTRATION; + if(GetHasFeat(FEAT_SKILL_AFFINITY_CONCENTRATION, oObject)) + nSkillMod += 2; + break; + case SKILL_CRAFT_ARMOR: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_CRAFT_ARMOR; + nFocusFeat = FEAT_SKILL_FOCUS_CRAFT_ARMOR; + break; + case SKILL_CRAFT_TRAP: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_CRAFT_TRAP; + nFocusFeat = FEAT_SKILL_FOCUS_CRAFT_TRAP; + break; + case SKILL_CRAFT_WEAPON: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_CRAFT_WEAPON; + nFocusFeat = FEAT_SKILL_FOCUS_CRAFT_WEAPON; + break; + case SKILL_DISABLE_TRAP: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_DISABLETRAP; + nFocusFeat = FEAT_SKILL_FOCUS_DISABLE_TRAP; + break; + case SKILL_DISCIPLINE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_DISCIPLINE; + nFocusFeat = FEAT_SKILL_FOCUS_DISCIPLINE; + break; + case SKILL_HEAL: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_HEAL; + nFocusFeat = FEAT_SKILL_FOCUS_HEAL; + break; + case SKILL_HIDE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_HIDE; + nFocusFeat = FEAT_SKILL_FOCUS_HIDE; + break; + case SKILL_INTIMIDATE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_INTIMIDATE; + nFocusFeat = FEAT_SKILL_FOCUS_INTIMIDATE; + break; + case SKILL_LISTEN: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_LISTEN; + nFocusFeat = FEAT_SKILL_FOCUS_LISTEN; + if(GetHasFeat(FEAT_SKILL_AFFINITY_LISTEN, oObject)) + nSkillMod += 2; + if(GetHasFeat(FEAT_PARTIAL_SKILL_AFFINITY_LISTEN, oObject)) + nSkillMod += 1; + break; + case SKILL_LORE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_LORE; + nFocusFeat = FEAT_SKILL_FOCUS_LORE; + if(GetHasFeat(FEAT_SKILL_AFFINITY_LORE, oObject)) + nSkillMod += 2; + if(GetHasFeat(FEAT_COURTLY_MAGOCRACY, oObject)) + nSkillMod += 2; + if(GetHasFeat(FEAT_BARDIC_KNOWLEDGE, oObject)) + nSkillMod += GetLevelByClass(CLASS_TYPE_BARD, oObject) + +GetLevelByClass(CLASS_TYPE_HARPER, oObject) + +GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oObject) + +GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oObject); + break; + case SKILL_MOVE_SILENTLY: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_MOVESILENTLY; + nFocusFeat = FEAT_SKILL_FOCUS_MOVE_SILENTLY; + if(GetHasFeat(FEAT_SKILL_AFFINITY_MOVE_SILENTLY, oObject)) + nSkillMod += 2; + break; + case SKILL_OPEN_LOCK: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_OPENLOCK; + nFocusFeat = FEAT_SKILL_FOCUS_OPEN_LOCK; + break; + case SKILL_PARRY: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_PARRY; + nFocusFeat = FEAT_SKILL_FOCUS_PARRY; + break; + case SKILL_PERFORM: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_PERFORM; + nFocusFeat = FEAT_SKILL_FOCUS_PERFORM; + if(GetHasFeat(FEAT_ARTIST, oObject)) + nSkillMod += 2; + break; + case SKILL_PERSUADE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_PERSUADE; + nFocusFeat = FEAT_SKILL_FOCUS_PERSUADE; + if(GetHasFeat(FEAT_SILVER_PALM, oObject)) + nSkillMod += 2; + break; + case SKILL_PICK_POCKET: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_PICKPOCKET; + nFocusFeat = FEAT_SKILL_FOCUS_PICK_POCKET; + break; + case SKILL_SEARCH: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_SEARCH; + nFocusFeat = FEAT_SKILL_FOCUS_SEARCH; + if(GetHasFeat(FEAT_SKILL_AFFINITY_SEARCH, oObject)) + nSkillMod += 2; + if(GetHasFeat(FEAT_PARTIAL_SKILL_AFFINITY_SEARCH, oObject)) + nSkillMod += 1; + break; + case SKILL_SET_TRAP: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_SETTRAP; + nFocusFeat = FEAT_SKILL_FOCUS_SET_TRAP; + break; + case SKILL_SPELLCRAFT: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_SPELLCRAFT; + nFocusFeat = FEAT_SKILL_FOCUS_SPELLCRAFT; + if(GetHasFeat(FEAT_COURTLY_MAGOCRACY, oObject)) + nSkillMod += 2; + break; + case SKILL_SPOT: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_SPOT; + nFocusFeat = FEAT_SKILL_FOCUS_SPOT; + if(GetHasFeat(FEAT_SKILL_AFFINITY_SPOT, oObject)) + nSkillMod += 2; + if(GetHasFeat(FEAT_PARTIAL_SKILL_AFFINITY_SPOT, oObject)) + nSkillMod += 1; + if(GetHasFeat(FEAT_ARTIST, oObject)) + nSkillMod += 2; + if(GetHasFeat(FEAT_BLOODED, oObject)) + nSkillMod += 2; + break; + case SKILL_TAUNT: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_TAUNT; + nFocusFeat = FEAT_SKILL_FOCUS_TAUNT; + break; + case SKILL_TUMBLE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_TUMBLE; + nFocusFeat = FEAT_SKILL_FOCUS_TUMBLE; + break; + case SKILL_USE_MAGIC_DEVICE: + nEpicFeat = FEAT_EPIC_SKILL_FOCUS_USEMAGICDEVICE; + nFocusFeat = FEAT_SKILL_FOCUS_USE_MAGIC_DEVICE; + break; + } + if(nEpicFeat != 0 + && GetHasFeat(nEpicFeat, oObject)) + nSkillMod += 10; + if(nFocusFeat != 0 + && GetHasFeat(nFocusFeat, oObject)) + nSkillMod += 3; + nSkillRank -= nSkillMod; + } + //add this at the end so any effects applied are counted + nSkillRank += GetSkillRank(nSkill, oObject); + return nSkillRank; +} + +void ForcePutDown(object oPC, object oItem, int nSlot, int nThCall = 0) +{ + // Sanity checks + if(!GetIsObjectValid(oPC)) return; + if(!GetIsObjectValid(oItem)) return; + // Fail on non-commandable NPCs after ~1min + if(!GetIsPC(oPC) && !GetCommandable(oPC) && nThCall > 60) + { + WriteTimestampedLogEntry("ForcePutDown() failed on non-commandable NPC: " + DebugObject2Str(oPC) + " for item: " + DebugObject2Str(oItem)); + return; + } + + float fDelay; + + if(GetItemInSlot(nSlot, oPC) == oItem) + { + // Attempt to avoid interference by not clearing actions before the first attempt + if(nThCall > 1) + if(GetIsPC(oPC) || nThCall > 5) // Skip nuking NPC action queue at first, since that can cause problems. 5 = magic number here. May need adjustment + AssignCommand(oPC, ClearAllActions()); + + AssignCommand(oPC, ActionPutDownItem(oItem)); + + // Ramp up the delay if the action is not getting through. Might let whatever is intefering finish + fDelay = PRCMin(nThCall, 10) / 10.0f; + } + // The item has already been unequipped + else + return; + + // Loop + DelayCommand(fDelay, ForcePutDown(oPC, oItem, nSlot, ++nThCall)); +} + +// Test main +//void main() {} diff --git a/src/include/inc_vfx_const.nss b/src/include/inc_vfx_const.nss new file mode 100644 index 0000000..c69616d --- /dev/null +++ b/src/include/inc_vfx_const.nss @@ -0,0 +1,675 @@ +//:://///////////////////////////////////////////// +//:: Visual Effect constant include +//:: inc_vfx_const +//:://///////////////////////////////////////////// +/* + Constants for vfx present in visualeffects.2da that + do not have a constant defined by bioware. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Unenumerated BW VFX */ +////////////////////////////////////////////////// + +const int VFX_COM_BLOOD_REG_WIMPY = 296; +const int VFX_COM_BLOOD_LRG_WIMPY = 297; +const int VFX_COM_BLOOD_CRT_WIMPY = 298; +const int VFX_COM_BLOOD_REG_WIMPG = 299; +const int VFX_COM_BLOOD_LRG_WIMPG = 300; +const int VFX_COM_BLOOD_CRT_WIMPG = 301; +const int VFX_COM_BLOOD_CRT_RED_HEAD = 491; +const int VFX_COM_BLOOD_CRT_GREEN_HEAD = 492; +const int VFX_COM_BLOOD_CRT_YELLOW_HEAD = 493; + +const int VFX_IMP_LEAF = 132; +const int VFX_IMP_CLOUD = 133; +const int VFX_IMP_WIND = 134; +const int VFX_IMP_ROCKEXPLODE = 135; +const int VFX_IMP_ROCKEXPLODE2 = 136; +const int VFX_IMP_ROCKSUP = 137; +const int VFX_IMP_DESTRUCTION_LOW = 302; +const int VFX_IMP_PULSE_BOMB = 469; +const int VFX_IMP_SILENCE_NO_SOUND = 470; + +const int VFX_BEAM_FLAME = 444; + +const int VFX_DUR_GHOSTLY_PULSE_QUICK = 295; +const int VFX_DUR_PROT_ACIDSHIELD = 448; +const int VFX_DUR_BARD_SONG_SILENT = 468; +const int VFX_DUR_CONECOLD_HEAD = 490; +const int VFX_DUR_BARD_SONG_EVIL = 507; + +const int VFX_FNF_SPELL_FAIL_HEA = 292; +const int VFX_FNF_SPELL_FAIL_HAND = 293; +const int VFX_FNF_HIGHLIGHT_FLASH_WHITE = 294; +const int VFX_FNF_SCREEN_SHAKE2 = 356; +const int VFX_FNF_HELLBALL = 464; +const int VFX_FNF_TELEPORT_IN = 471; +const int VFX_FNF_TELEPORT_OUT = 472; +const int VFX_FNF_DRAGBREATHGROUND = 494; + +const int VFX_CONJ_MIND = 466; +const int VFX_CONJ_FIRE = 467; + + +const int SCENE_WEIRD = 323; +const int SCENE_EVARD = 346; +const int SCENE_TOWER = 347; +const int SCENE_TEMPLE = 348; +const int SCENE_LAVA = 349; +const int SCENE_LAVA2 = 350; +const int SCENE_WATER = 401; +const int SCENE_GRASS = 402; +const int SCENE_FORMIAN1 = 404; +const int SCENE_FORMIAN2 = 405; +const int SCENE_PITTRAP = 406; +const int SCENE_ICE = 426; +const int SCENE_MFPillar = 427; +const int SCENE_MFWaterfall = 428; +const int SCENE_MFGroundCover = 429; +const int SCENE_MFGroundCover2 = 430; +const int SCENE_MF6 = 431; +const int SCENE_MF7 = 432; +const int SCENE_MF8 = 433; +const int SCENE_MF9 = 434; +const int SCENE_MF10 = 435; +const int SCENE_MF11 = 436; +const int SCENE_MF12 = 437; +const int SCENE_MF13 = 438; +const int SCENE_MF14 = 439; +const int SCENE_MF15 = 440; +const int SCENE_MF16 = 441; +const int SCENE_ICE_CLEAR = 442; +const int SCENE_EVIL_CASTLE_WALL = 443; +const int SCENE_BUILDING = 449; +const int SCENE_BURNED_RUBBLE = 450; +const int SCENE_BURNING_HALF_HOUSE = 451; +const int SCENE_RUINED_ARCH = 452; +const int SCENE_SOLID_ARCH = 453; +const int SCENE_BURNED_RUBBLE_2 = 454; +const int SCENE_MARKET_1 = 455; +const int SCENE_MARKET_2 = 456; +const int SCENE_GAZEBO = 457; +const int SCENE_WAGON = 458; +const int SCENE_SEWER_WATER = 461; +const int SCENE_BLACK_TILE = 506; +const int SCENE_PRISON_FLOOR = 511; + +const int WELL = 358; + +const int NORMAL_ARROW = 357; +const int NORMAL_DART = 359; + + + +////////////////////////////////////////////////// +/* shadguy's spell VFX (visual effects) library */ +////////////////////////////////////////////////// + + +// retextured magic missle effects +// +// Note: in game, these are a bit flakey; the engine tries to apply these as +// an impact VFX right away, even before the MIRV itself lands on target. +// +// I also tried Mirv's with chunk models - using the black skull from the mind +// fear effect. It looked even dumber manifesting at the target before impact. + +const int VFX_IMP_MIRV_SILENT = 1000; +const int VFX_IMP_MIRV_DN_YELLOW = 1001; +const int VFX_IMP_MIRV_DN_RED = 1002; +const int VFX_IMP_MIRV_DN_GREEN = 1003; +const int VFX_IMP_MIRV_DN_VIOLET = 1004; +const int VFX_IMP_MIRV_DN_CYAN = 1005; +const int VFX_IMP_MIRV_DN_PURPLE = 1006; +const int VFX_IMP_MIRV_DN_MAGENTA = 1007; +const int VFX_IMP_MIRV_DN_LAWNGREEN = 1008; +const int VFX_IMP_MIRV_DN_ORANGE = 1009; +const int VFX_IMP_MIRV_DN_SPRINGGREEN = 1010; +const int VFX_IMP_MIRV_DN_STEELBLUE = 1011; +const int VFX_IMP_MIRV_DN_ECTO = 1012; +const int VFX_IMP_MIRV_DN_SOUNDFX = 1013; + + +// recolored AID VFX: +const int VFX_IMP_HOLY_AID_DN_SILENT = 1017; +const int VFX_IMP_HOLY_AID_DN_GREEN = 1018; +const int VFX_IMP_HOLY_AID_DN_CYAN = 1019; +const int VFX_IMP_HOLY_AID_DN_ORANGE = 1020; +const int VFX_IMP_HOLY_AID_DN_RED = 1021; +const int VFX_IMP_HOLY_AID_DN_BLUE = 1022; +const int VFX_IMP_HOLY_AID_DN_PURPLE = 1023; +const int VFX_IMP_HOLY_AID_DN_SOUNDFX = 1024; + + +//recolored Word of Faith VFX, slightly bastardized; nwmax wouldn't import the higres version, so these are all based on teh low res version +const int VFX_FNF_WORD_DN_SILENT = 1028; +const int VFX_FNF_WORD_DN_GREEN = 1029; +const int VFX_FNF_WORD_DN_CYAN = 1030; +const int VFX_FNF_WORD_DN_BLUE = 1031; +const int VFX_FNF_WORD_DN_PURPLE = 1032; +const int VFX_FNF_WORD_DN_RED = 1033; +const int VFX_FNF_WORD_DN_ORANGE = 1034; +const int VFX_FNF_WORD_DN_SOUNDFX = 1035; + + +//attempted recolor of Power Word Stun that turned out screwy but useable; +//I'll recommend using them in combo with silent version of stuff like timestop below +const int VFX_FNF_PW_DN_YG = 1039; +const int VFX_FNF_PW_DN_GB = 1040; +const int VFX_FNF_PW_DN_BP = 1041; +const int VFX_FNF_PW_DN_PR = 1042; +const int VFX_FNF_PW_DN_RY = 1043; +const int VFX_FNF_PW_DN_STUN_SOUNDFX = 1044; +const int VFX_FNF_PW_DN_KILL_SOUNDFX = 1045; + + +// recolored time stop VFX +const int VFX_IMP_TIME_STOP_DN_SOUNDFX = 1049; +const int VFX_IMP_TIME_STOP_DN_RED = 1050; +const int VFX_IMP_TIME_STOP_DN_ORANGE = 1051; +const int VFX_IMP_TIME_STOP_DN_YELLOW = 1052; +const int VFX_IMP_TIME_STOP_DN_GREEN = 1053; +const int VFX_IMP_TIME_STOP_DN_BLUE = 1054; +const int VFX_IMP_TIME_STOP_DN_PURPLE = 1055; +const int VFX_IMP_TIME_STOP_DN_SILENT = 1056; + + +// recolored blindness and deafness +const int VFX_IMP_BLINDDEAD_DN_RED = 1060; +const int VFX_IMP_BLINDDEAD_DN_YELLOW = 1061; +const int VFX_IMP_BLINDDEAD_DN_GREEN = 1062; +const int VFX_IMP_BLINDDEAD_DN_CYAN = 1063; +const int VFX_IMP_BLINDDEAD_DN_BLUE = 1064; +const int VFX_IMP_BLINDDEAD_DN_PURPLE = 1065; +const int VFX_IMP_BLINDDEAD_DN_SOUNDFX = 1066; + + +// recolored magic eye VFX +const int VFX_MAGICAL_VISION_DN_GREEN = 1070; +const int VFX_MAGICAL_VISION_DN_RED = 1071; +const int VFX_MAGICAL_VISION_DN_SOUNDFX = 1075; + + +// recolored healing_s +const int VFX_IMP_HEALING_S_VIO = 1079; +const int VFX_IMP_HEALING_S_MAG = 1080; +const int VFX_IMP_HEALING_S_ORA = 1081; +const int VFX_IMP_HEALING_S_LAW = 1082; +const int VFX_IMP_HEALING_S_SPR = 1083; +const int VFX_IMP_HEALING_S_SILENT = 1094; +const int VFX_IMP_HEALING_S_RED = 1085; +const int VFX_IMP_HEALING_S_SOUNDFX = 1086; +const int VFX_IMP_HEALING_HARM_SOUNDFX = 1087; + + +// recolored healing_m +const int VFX_IMP_HEALING_M_PUR = 1091; +const int VFX_IMP_HEALING_M_MAG = 1092; +const int VFX_IMP_HEALING_M_YEL = 1093; +const int VFX_IMP_HEALING_M_CYA = 1094; +const int VFX_IMP_HEALING_M_SILENT = 1095; +const int VFX_IMP_HEALING_M_RED = 1096; +const int VFX_IMP_HEALING_M_SOUNDFX = 1097; + + +// recolored healing_l +const int VFX_IMP_HEALING_L_MAG = 1101; +const int VFX_IMP_HEALING_L_ORA = 1102; +const int VFX_IMP_HEALING_L_LAW = 1103; +const int VFX_IMP_HEALING_L_SPR = 1104; +const int VFX_IMP_HEALING_L_VIO = 1105; +const int VFX_IMP_HEALING_L_RED = 1106; +const int VFX_IMP_HEALING_L_SILENT = 1107; +const int VFX_IMP_HEALING_L_SOUNDFX = 1108; + + +// recolored healing_G +const int VFX_IMP_HEALING_G_MAG = 1112; +const int VFX_IMP_HEALING_G_LAW = 1113; +const int VFX_IMP_HEALING_G_SPR = 1114; +const int VFX_IMP_HEALING_G_VIO = 1115; +const int VFX_IMP_HEALING_G_RED = 1116; +const int VFX_IMP_HEALING_G_SILENT = 1117; +const int VFX_IMP_HEALING_G_SOUNDFX = 1118; + + +// recolored healing_X +const int VFX_IMP_HEALING_X_MAG = 1122; +const int VFX_IMP_HEALING_X_ORA = 1123; +const int VFX_IMP_HEALING_X_LAW = 1124; +const int VFX_IMP_HEALING_X_SPR = 1125; +const int VFX_IMP_HEALING_X_VIO = 1126; +const int VFX_IMP_HEALING_X_SILENT = 1127; +const int VFX_IMP_HEALING_X_SOUNDFX = 1128; + + +// recolored magic impact VFX - these are for use with the recolorded MIRV (magic missile) FX +const int VFX_IMP_MAGCYA = 1132; +const int VFX_IMP_MAGBLU = 1133; +const int VFX_IMP_MAGVIO = 1134; +const int VFX_IMP_MAGPUR = 1135; +const int VFX_IMP_MAGRED = 1136; +const int VFX_IMP_MAGMAG = 1137; +const int VFX_IMP_MAGORA = 1138; +const int VFX_IMP_MAGYEL = 1139; +const int VFX_IMP_MAGLAW = 1140; +const int VFX_IMP_MAGGRN = 1141; +const int VFX_IMP_MAG_SOUNDFX = 1142; + + +// tornadoheal VFX recolored +const int VFX_IMP_TORNADO_L_SILENT = 990; +const int VFX_IMP_TORNADO_L_MAG = 991; +const int VFX_IMP_TORNADO_L_ORA = 992; +const int VFX_IMP_TORNADO_L_LAW = 993; +const int VFX_IMP_TORNADO_L_SPR = 994; +const int VFX_IMP_TORNADO_L_VIO = 995; +const int VFX_IMP_TORNADO_L_RED = 996; +const int VFX_IMP_TORNADO_L_SOUNDFX = 997; + + +// recolored flame_M effects +const int VFX_IMP_FLAME_M_SILENT = 1157; +const int VFX_IMP_FLAME_M_CYAN = 1158; +const int VFX_IMP_FLAME_M_GREEN = 1159; +const int VFX_IMP_FLAME_M_MAGENTA = 1160; +const int VFX_IMP_FLAME_M_PURPLE = 1161; +const int VFX_IMP_FLAME_M_SOUNDFX = 1162; + + +// recolored spell mantle ground VFX. I tried the impact VFX to and didn't get them working yet. +const int VFX_DUR_SPELLTURNING_SILENT = 1166; +const int VFX_DUR_SPELLTURNING_P = 1167; +const int VFX_DUR_SPELLTURNING_R = 1168; +const int VFX_DUR_SPELLTURNING_Y = 1169; +const int VFX_DUR_SPELLTURNING_G = 1170; +const int VFX_DUR_SPELLTURNING_C = 1171; +const int VFX_DUR_SPELLTURNING_O = 1172; +const int VFX_DUR_SPELLTURNING_V = 1173; +const int VFX_DUR_SPELLTURNING_S = 1174; +const int VFX_DUR_SPELLTURNING_M = 1175; +const int VFX_DUR_SPELLTURNING_SOUNDFX = 1176; + + +// recolored Magic resistence VFX rings +const int VFX_DUR_MAGIC_RESISTANCE_SILENT = 1180; +const int VFX_DUR_MAGIC_RESISTANCE_G = 1181; +const int VFX_DUR_MAGIC_RESISTANCE_B = 1182; +const int VFX_DUR_MAGIC_RESISTANCE_P = 1183; +const int VFX_DUR_MAGIC_RESISTANCE_O = 1184; +const int VFX_DUR_MAGIC_RESISTANCE_Y = 1185; +const int VFX_DUR_MAGIC_RESISTANCE_SOUNDFX = 1186; + + +// recolored IMP Magic VFX (the impact for "Spell Resistence") +const int VFX_IMP_MAGIC_PROTECTION_SILENT = 1190; +const int VFX_IMP_MAGIC_PROTECTION_G = 1191; +const int VFX_IMP_MAGIC_PROTECTION_B = 1192; +const int VFX_IMP_MAGIC_PROTECTION_P = 1193; +const int VFX_IMP_MAGIC_PROTECTION_O = 1194; +const int VFX_IMP_MAGIC_PROTECTION_Y = 1195; +const int VFX_IMP_MAGIC_PROTECTION_SOUND = 1196; + + +// recolored holy strike VFX +const int VFX_FNF_STRIKE_HOLY_SILENT = 1200; +const int VFX_FNF_STRIKE_HOLY_G = 1201; +const int VFX_FNF_STRIKE_HOLY_C = 1202; +const int VFX_FNF_STRIKE_HOLY_B = 1203; +const int VFX_FNF_STRIKE_HOLY_P = 1204; +const int VFX_FNF_STRIKE_HOLY_R = 1205; +const int VFX_FNF_STRIKE_HOLY_O = 1206; +const int VFX_FNF_STRIKE_HOLY_SOUNDFX = 1207; + + +// recolored ability score buff VFX +const int VFX_IMP_IMPROVE_ABILITY_SCORE_SILENT = 1211; +const int VFX_IMP_IMPROVE_ABILITY_SCORE_A = 1212; +const int VFX_IMP_IMPROVE_ABILITY_SCORE_B = 1213; +const int VFX_IMP_IMPROVE_ABILITY_SCORE_C = 1214; +const int VFX_IMP_IMPROVE_ABILITY_SCORE_D = 1215; +const int VFX_IMP_IMPROVE_ABILITY_SCORE_E = 1216; +const int VFX_IMP_IMPROVE_ABILITY_SCORE_SOUNDFX = 1217; + + +// recolroed "reduce ability score" vfx, with the darkness FX removed +const int VFX_IMP_REDUCE_ABILITY_SCORE_RED = 1221; +const int VFX_IMP_REDUCE_ABILITY_SCORE_YEL = 1222; +const int VFX_IMP_REDUCE_ABILITY_SCORE_ORA = 1223; +const int VFX_IMP_REDUCE_ABILITY_SCORE_GRN = 1224; +const int VFX_IMP_REDUCE_ABILITY_SCORE_CYA = 1225; +const int VFX_IMP_REDUCE_ABILITY_SCORE_BLU = 1226; +const int VFX_IMP_REDUCE_ABILITY_SCORE_PUR = 1227; +const int VFX_IMP_REDUCE_ABILITY_SCORE_SOUNDFX = 1228; + +// Nother Set of Shadguy/DN VFX +const int TOF_VFX_ALARM_AUDIBLE = 1229; +const int TOF_VFX_ALARM_MENTAL = 1230; +const int VFX_COM_HIT_NEGATIVE2 = 1231; +const int VFX_COM_HIT_POSITIVE = 1232; +const int VFX_IMP_UNSUMMON_RED = 1233; +const int VFX_IMP_UNSUMMON_YEL = 1234; +const int VFX_FNF_LOS_GREEN_10 = 1235; +const int VFX_FNF_LOS_GREEN_20 = 1236; +const int VFX_FNF_LOS_GREEN_30 = 1237; +const int VFX_FNF_LOS_BLUE_10 = 1238; +const int VFX_FNF_LOS_BLUE_20 = 1239; +const int VFX_FNF_LOS_BLUE_30 = 1240; +const int TOF_VFX_FNF_ALARM_DINGDONG = 1241; +const int VFX_IMP_LONGSTRIDER = 1242; +const int VFX_FNF_RUSTING_GRASP = 1243; +const int VFX_IMP_EXPRETREAT = 1244; +const int VFX_DUR_ROOTED_TO_SPOT = 1245; +const int VFX_IMP_DAWN = 1246; +const int VFX_DUR_MARK_OF_THE_HUNTER = 1247; +const int VFX_DUR_HEARD = 1248; +const int VFX_DUR_SEEN = 1249; +const int VFX_IMP_SOUND_SYMBOL_NEC = 1250; +const int VFX_IMP_SOUND_SYMBOL_EVO = 1251; +const int VFX_IMP_SOUND_SYMBOL_ENC = 1252; +const int VFX_IMP_SOUND_SYMBOL_DEATH = 1253; +const int VFX_IMP_SOUND_SYMBOL_INSANITY = 1254; +const int VFX_IMP_SOUND_SYMBOL_WEAKNESS = 1255; +const int VFX_IMP_SOUND_SYMBOL_STUNNING = 1256; +const int VFX_IMP_SOUND_SYMBOL_FEAR = 1257; +const int VFX_IMP_SOUND_SYMBOL_SLEEP = 1258; +const int VFX_IMP_SOUND_SYMBOL_PAIN = 1259; +const int VFX_FNF_MASS_CURE = 1260; +const int VFX_FNF_MASS_INFLICT = 1261; +const int VFX_IMP_HEALING_X_UNDEAD = 1262; +const int VFX_IMP_BONUS_STRENGTH = 1263; +const int VFX_IMP_BONUS_DEXTERITY = 1264; +const int VFX_IMP_BONUS_CONSTITUTION = 1265; +const int VFX_IMP_BONUS_INTELLIGENCE = 1266; +const int VFX_IMP_BONUS_WISDOM = 1267; +const int VFX_IMP_BONUS_CHARISMA = 1268; +const int VFX_DUR_BRIGHT_LIGHT_WHITE = 1269; +const int VFX_DUR_BRIGHT_LIGHT_RED = 1270; +const int VFX_DUR_BRIGHT_LIGHT_ORANGE = 1271; +const int VFX_DUR_BRIGHT_LIGHT_YELLOW = 1272; +const int VFX_DUR_BRIGHT_LIGHT_GREEN = 1273; +const int VFX_DUR_BRIGHT_LIGHT_BLUE = 1274; +const int VFX_DUR_BRIGHT_LIGHT_MAGENTA = 1275; +const int VFX_DUR_BRIGHT_LIGHT_RED_TO_ORANGE = 1276; +const int VFX_DUR_BRIGHT_LIGHT_ORANGE_TO_YELLOW = 1277; +const int VFX_DUR_BRIGHT_LIGHT_YELLOW_TO_GREEN = 1278; +const int VFX_DUR_BRIGHT_LIGHT_GREEN_TO_BLUE = 1279; +const int VFX_DUR_BRIGHT_LIGHT_BLUE_TO_MAGENTA = 1280; +const int VFX_DUR_BRIGHT_LIGHT_MAGENTA_TO_RED = 1281; +const int VFX_DUR_BRIGHT_LIGHT_CYAN = 1282; +const int VFX_DUR_BRIGHT_LIGHT_WHITE_PULSE_SLOW = 1283; +const int VFX_DUR_BRIGHT_LIGHT_RED_PULSE_SLOW = 1284; +const int VFX_DUR_BRIGHT_LIGHT_ORANGE_PULSE_SLOW = 1285; +const int VFX_DUR_BRIGHT_LIGHT_YELLOW_PULSE_SLOW = 1286; +const int VFX_DUR_BRIGHT_LIGHT_GREEN_PULSE_SLOW = 1287; +const int VFX_DUR_BRIGHT_LIGHT_BLUE_PULSE_SLOW = 1288; +const int VFX_DUR_BRIGHT_LIGHT_INDIGO_PULSE_SLOW = 1289; +const int VFX_DUR_BRIGHT_LIGHT_CYAN_PULSE_SLOW = 1290; +const int VFX_DUR_BRIGHT_LIGHT_WHITE_PULSE_FAST = 1291; +const int VFX_DUR_BRIGHT_LIGHT_RED_PULSE_FAST = 1292; +const int VFX_DUR_BRIGHT_LIGHT_ORANGE_PULSE_FAST = 1293; +const int VFX_DUR_BRIGHT_LIGHT_YELLOW_PULSE_FAST = 1294; +const int VFX_DUR_BRIGHT_LIGHT_GREEN_PULSE_FAST = 1295; +const int VFX_DUR_BRIGHT_LIGHT_BLUE_PULSE_FAST = 1296; +const int VFX_DUR_BRIGHT_LIGHT_INDIGO_PULSE_FAST = 1297; +const int VFX_DUR_BRIGHT_LIGHT_CYAN_PULSE_FAST = 1298; +const int VFX_DUR_BRIGHT_LIGHT_RED_ORANGE_DISCO = 1299; +const int VFX_DUR_BRIGHT_LIGHT_ORANGE_YELLOW_DISCO = 1300; +const int VFX_DUR_BRIGHT_LIGHT_YELLOW_GREEN_DISCO = 1301; +const int VFX_DUR_BRIGHT_LIGHT_GREEN_BLUE_DISCO = 1302; +const int VFX_DUR_BRIGHT_LIGHT_BLUE_MAGENTA_DISCO = 1303; +const int VFX_DUR_BRIGHT_LIGHT_MAGENTA_RED_DISCO = 1304; +const int VFX_DUR_BRIGHT_LIGHT_DISCO_DISCO_DISCO = 1305; +const int VFX_DUR_BRIGHT_LIGHT_WHITE_PULSE_STROBE = 1306; +const int VFX_DUR_BRIGHT_LIGHT_RED_PULSE_STROBE = 1307; +const int VFX_DUR_BRIGHT_LIGHT_ORANGE_PULSE_STROBE = 1308; +const int VFX_DUR_BRIGHT_LIGHT_YELLOW_PULSE_STROBE = 1309; +const int VFX_DUR_BRIGHT_LIGHT_GREEN_PULSE_STROBE = 1310; +const int VFX_DUR_BRIGHT_LIGHT_BLUE_PULSE_STROBE = 1311; +const int VFX_DUR_BRIGHT_LIGHT_INDIGO_PULSE_STROBE = 1312; +const int VFX_DUR_BRIGHT_LIGHT_CYAN_PULSE_STROBE = 1313; +const int VFX_DUR_BRIGHT_LIGHT_BRASS = 1314; +const int VFX_DUR_BRIGHT_LIGHT_GOLD = 1315; +const int VFX_DUR_BRIGHT_LIGHT_LIME = 1316; +const int VFX_DUR_BRIGHT_LIGHT_TURQUOISE = 1317; +const int VFX_DUR_BRIGHT_LIGHT_INDIGO = 1318; +const int VFX_DUR_BRIGHT_LIGHT_VIOLET = 1319; +const int VFX_FNF_LOS_WHITE_10 = 1320; +const int VFX_FNF_LOS_WHITE_20 = 1321; +const int VFX_FNF_LOS_WHITE_30 = 1322; +const int VFX_FNF_LOS_PURPLE_10 = 1323; +const int VFX_FNF_LOS_PURPLE_20 = 1324; +const int VFX_FNF_LOS_PURPLE_30 = 1325; +const int VFX_DUR_BRIGHT_LIGHT_DISCO_SINGLE = 1326; +const int VFX_FNF_LOS_PURPLE_30_SILENT = 1327; + +//Baelnorn eyes by Tenjac +const int VFX_DUR_BAELN_EYES = 808; + +//Supamans Custom VFX for psionics & epic spells +const int VFX_IMP_EPIC_GEM_EMERALD = 809; +const int VFX_IMP_EPIC_GEM_SAPPHIRE = 810; +const int VFX_IMP_EPIC_GEM_DIAMOND = 811; +const int VFX_PRC_FNF_EARTHQUAKE = 812; +const int PSI_IMP_ULTRABLAST = 813; +const int PSI_DUR_TIMELESS_BODY = 814; +const int PSI_DUR_TEMPORAL_ACCELERATION = 815; +const int PSI_DUR_SHADOW_BODY = 816; +const int PSI_FNF_PSYCHIC_CRUSH = 817; +const int EPIC_DUR_FLEETNESS_OF_FOOT = 818; +const int PSI_DUR_ENERGY_ADAPTATION_ALL = 819; +const int PSI_DUR_BURST = 823; +const int PSI_FNF_CRISIS_OF_LIFE = 824; +const int PSI_FNF_RECALL_AGONY = 825; +const int PSI_DUR_SYNESTHETE = 826; +const int PSI_IMP_CONCUSSION_BLAST = 827; +const int PSI_FNF_PSYCHIC_CHIRURGY = 829; +const int PSI_FNF_ASTRAL_SEED = 830; +const int PSI_DUR_INTELLECT_FORTRESS = 831; +const int PSI_DUR_DISPELLING_BUFFER = 832; +//Spellfire +const int VFX_FNF_SPELLF_EXP = 797; +const int VFX_IMP_SPELLF_FLAME = 798; +const int VFX_IMP_SPELLF_HEAL = 799; +const int VFX_BEAM_SPELLFIRE = 800; + +const int VFX_DUR_STONE3 = 834; +const int VFX_DUR_STONE4 = 835; +const int VFX_DUR_STONE5 = 836; +const int VFX_DUR_AIR1 = 837; +const int VFX_DUR_AIR2 = 838; + +//split-effects +const int VFX_DUR_PROT_PRC_STONESKIN = 839; +const int VFX_DUR_PROT_PRC_CIRCLEROCK = 840; +const int VFX_DUR_PROT_PRC_SHADOW_ARMOR = 841; +const int VFX_DUR_PROT_PRC_CIRCLESHAD = 842; + +////////////////////////////////////////////////// +/* Soopaman's VFX from SMP (Granted to us now) */ +////////////////////////////////////////////////// + +const int VFX_FNF_TORNADO = 851; +const int VFX_IMP_PWBLIND = 852; +const int VFX_IMP_RED_MISSLE = 853; +const int VFX_IMP_MAGRED_SMP = 854; +const int VFX_IMP_ICEWHIP = 855; +const int VFX_IMP_GRN_MISSLE = 856; +const int VFX_IMP_NEGBLAST_ENERGY = 857; +const int VFX_DUR_PRISMATIC_SPHERE = 858; +const int VFX_FNF_NEWWORD = 859; +const int VFX_DUR_BIGBYS_BIGBLUE_HAND2 = 860; +const int VFX_FNF_AWAKEN = 861; +const int VFX_FNF_CHAOSHAMMER = 862; +const int VFX_FNF_OTIL_COLDSPHERE = 863; +const int VFX_DUR_MAZE = 864; +const int VFX_DUR_CHILL_SHIELD = 865; +const int VFX_FNF_DRAGON_STRIKE = 866; +const int VFX_DUR_SHADOWS_ANTILIGHT = 867; +const int VFX_DUR_PROTECTION_ARROWS = 868; +const int VFX_FNF_HELLFIRE = 869; +const int VFX_FNF_HELLFIRESTORM = 870; +const int VFX_DUR_BLUESHIELDPROTECT = 871; +const int VFX_IMP_REGENERATE_IMPACT = 872; +const int VFX_FNF_BATSGIB = 873; +const int VFX_DUR_STORM_OF_VENGEANCE = 874; +const int VFX_IMP_FREEDOM = 875; +const int VIM_IMP_DIMENSIONDOOR_IN = 876; +const int VIM_IMP_DIMENSIONDOOR_OUT = 877; +const int VFX_DUR_ANTILIFE_SHELL = 878; +const int VFX_DUR_LIGHTNING_SHELL = 879; +const int VFX_IMP_DISENTIGRATION = 880; +const int VFX_IMP_DIMENSIONANCHOR = 881; +const int VFX_IMP_DIMENSIONLOCK = 882; +const int VFX_FNF_GLITTERDUST = 883; +const int VFX_FNF_INSANITY = 884; +const int VFX_IMP_IMPRISONMENT = 885; +const int VFX_DUR_PROTECTION_ENERGY_ACID = 886; +const int VFX_DUR_PROTECTION_ENERGY_COLD = 887; +const int VFX_DUR_PROTECTION_ENERGY_FIRE = 888; +const int VFX_DUR_PROTECTION_ENERGY_ELECT = 889; +const int VFX_DUR_PROTECTION_ENERGY_SONIC = 890; +const int VFX_DUR_PRISMATIC_WALL = 891; +const int VFX_FNF_FEEBLEMIND = 892; +const int VFX_FNF_SUMMON_NATURES_ALLY_1 = 893; +const int VFX_FNF_MAGIC_WEAPON = 894; +const int VFX_FNF_DEATH_WATCH = 895; +const int VFX_IMP_FAERIE_FIRE = 896; +const int VFX_DUR_RESISTANCE = 897; +const int VFX_IMP_EPIC_GEM_EMERALD_SMP = 898; +const int VFX_IMP_EPIC_GEM_SAPPHIRE_SMP = 899; +const int VFX_IMP_EPIC_GEM_DIAMOND_SMP = 900; +const int VFX_PERM_ELEMENTAL_SAVANT_WATER = 901; +const int VFX_PERM_ELEMENTAL_SAVANT_FIRE = 902; +const int VFX_FNF_SOUL_TRAP = 903; +const int VFX_DUR_AURA_LAW = 904; +const int VFX_DUR_SHIELD_OF_FAITH = 905; +const int VFX_FNF_CALM_ANIMALS = 906; +const int VFX_DUR_ENTROPIC_SHIELD = 907; +const int VFX_DUR_FLOATING_DISK = 908; +const int VFX_DUR_OBSCURING_MIST = 909; +const int VFX_IMP_MAGIC_ROCK = 910; +const int VFX_IMP_SHILLELAGH = 911; +const int VFX_FNF_METEORSWARM_IMPACT = 912; +const int VFX_FNF_SMP_GATE = 913; +const int VFX_FNF_ARMAGEDDON = 914; +const int VFX_DUR_SPHERE_OF_ANHILIATION = 915; +const int VFX_DUR_CHAOS_CLOAK = 916; +const int VFX_AOE_DESECRATE_20 = 917; +const int VFX_AOE_DESECRATE_100 = 918; +const int VFX_FNF_INVISIBILITY_SPHERE = 919; +const int VFX_DUR_DAYLIGHT = 920; +const int VFX_DUR_FLAMING_SPHERE = 921; +const int VFX_FNF_VAMPIRIC_DRAIN_PRC = 922; +const int VFX_FNF_BLASPHEMY = 923; +const int VFX_DUR_SHIELD_OF_LAW = 924; +const int VFX_DUR_UNHOLY_AURA_SMP = 925; +const int VFX_DUR_HOLY_AURA_SMP = 926; +const int VFX_DUR_PROT_IRON_SKIN = 927; +const int VFX_FNF_EARTHQUAKE_FISSURE = 928; +const int VFX_FNF_ORDERS_WRATH = 929; +const int VFX_DUR_RAINBOW_PATTERN = 930; +const int VFX_FNF_HOLY_SMITE_BATMAN = 931; +const int VFX_FNF_P2P_TESTER_OF_D3WM = 932; +const int VFX_FNF_PYRO_FIREWORKS_REDORANGE = 933; +const int VFX_DUR_BLOOD_FOUNTAIN = 934; +const int VFX_IMP_DISENTIGRATION_SMP = 935; +const int VFX_IMP_SPARKS = 936; +const int VFX_AOE_FORBIDDANCE = 937; +const int VFX_DUR_CROWN_OF_GLORY = 938; +const int VFX_DUR_ARMOR_OF_DARKNESS = 939; +const int VFX_FNF_MAGIC_VESTAMENT = 940; +const int VFX_DUR_FREEDOM_MOVEMENT = 941; +const int VFX_PRC_FNF_EARTHQUAKE_SMP = 942; +const int VFX_DUR_TEMPORAL_STASIS = 943; +const int VFX_DUR_RESILIENT_SPHERE = 944; +const int VFX_DUR_DEATHWARD = 945; +const int VFX_DUR_PHASE_DOOR = 946; +const int VFX_FNF_SCINTILLATING_PATTERN = 947; +const int VFX_IMP_DRAGONBLAST = 948; +const int VFX_FNF_DEEP_SLUMBER = 949; +const int VFX_AOE_ZONE_OF_TRUTH = 950; + +const int VFX_IMP_FAERIE_FIRE_BLUE = 951; +const int VFX_IMP_FAERIE_FIRE_GREEN = 952; +const int VFX_IMP_FAERIE_FIRE_VIOLET = 953; + +//Tenser's Floating discs +const int VFX_DUR_FLOATING_DISK_BLUE = 954; +const int VFX_DUR_FLOATING_DISK_GREEN = 955; +const int VFX_DUR_FLOATING_DISK_YELLOW = 956; +const int VFX_DUR_FLOATING_DISK_ORANGE = 957; +const int VFX_DUR_FLOATING_DISK_RED = 958; +const int VFX_DUR_FLOATING_DISK_PURPLE = 959; +const int VFX_DUR_FLOATING_DISK_GREY = 960; + +//Recolored Dragon Disciple breath weapon +const int VFX_FNF_DRAGBREATHACID = 961; +const int VFX_FNF_DRAGBREATHCOLD = 962; +const int VFX_FNF_DRAGBREATHELEC = 963; +const int VFX_FNF_DRAGBREATHSONIC = 964; +const int VFX_FNF_DRAGBREATHHOLY = 965; +const int VFX_FNF_DRAGBREATHGAS = 966; +const int VFX_FNF_DRAGBREATHMIND = 967; +const int VFX_FNF_DRAGBREATHODD = 968; + +//Consecrate/Desecrate Effects +const int VFX_TN_DES_20 = 801; +const int VFX_TN_DES_100 = 802; +const int VFX_CON_20 = 803; +const int VFX_DES_20 = 804; + +//ioun stones +const int VFX_IOUN_STONE_GREY = 969; +const int VFX_IOUN_STONE_ROSE = 970; +const int VFX_IOUN_STONE_CLEAR = 971; +const int VFX_IOUN_STONE_PALEBLUE = 972; +const int VFX_IOUN_STONE_SCARLETBLUE = 973; +const int VFX_IOUN_STONE_INCANBLUE = 974; +const int VFX_IOUN_STONE_RED = 975; +const int VFX_IOUN_STONE_PINK = 976; +const int VFX_IOUN_STONE_PINKGREEN = 977; +const int VFX_IOUN_STONE_BLUE = 978; +const int VFX_IOUN_STONE_PURPLE = 979; +const int VFX_IOUN_STONE_IRIDESCENT = 980; +const int VFX_IOUN_STONE_PALEGREEN = 981; +const int VFX_IOUN_STONE_WHITE = 982; +const int VFX_IOUN_STONE_LAVENDER = 983; +const int VFX_IOUN_STONE_LAVENDERGREEN = 984; + +const int VFX_FNF_ACIDSTORM = 1014; +const int VFX_FNF_EXPLOSION_ACID = 821; +const int VFX_FNF_EXPLOSION_COLD = 822; +const int VFX_DUR_SYMB_DEATH = 783; +const int VFX_DUR_SYMB_FEAR = 784; +const int VFX_DUR_SYMB_INSAN = 785; +const int VFX_DUR_SYMB_PAIN = 786; +const int VFX_DUR_SYMB_PERS = 787; +const int VFX_DUR_SYMB_SLEEP = 788; +const int VFX_DUR_SYMB_STUN = 789; +const int VFX_DUR_SYMB_WEAK = 790; +const int VFX_DUR_GLYPH_OF_WARDING_BLUE = 791; +const int VFX_DUR_GLYPH_OF_WARDING_COLD = 792; +const int VFX_DUR_GLYPH_OF_WARDING_RED = 793; +const int VFX_DUR_GLYPH_OF_WARDING_VIOLET = 794; +const int VFX_DUR_GLYPH_OF_WARDING_WHITE = 795; +const int VFX_DUR_GLYPH_OF_WARDING_YELLOW = 796; + +const int VFX_DUR_AURA_CHAOS = 752;//violet +const int VFX_DUR_AURA_EVIL = 753;//red +const int VFX_DUR_AURA_GOOD = 754;//yellow +const int VFX_DUR_AURA_LAW2 = 755;//blue +const int VFX_DUR_AURA_UNDEAD = 756;//? +const int VFX_DUR_GR_AURA_CHAOS = 757; +const int VFX_DUR_GR_AURA_EVIL = 758; +const int VFX_DUR_GR_AURA_GOOD = 759; +const int VFX_DUR_GR_AURA_LAW = 760; +const int VFX_DUR_GR_AURA_UNDEAD = 761; +const int VFX_DUR_DETECT = 762; +const int VFX_IMP_DIVINE_STRIKE_ACID = 764; +const int VFX_IMP_DIVINE_STRIKE_COLD = 765; +const int VFX_IMP_DIVINE_STRIKE_SONIC = 766; \ No newline at end of file diff --git a/src/include/inv_inc_blast.nss b/src/include/inv_inc_blast.nss new file mode 100644 index 0000000..683b9cd --- /dev/null +++ b/src/include/inv_inc_blast.nss @@ -0,0 +1,162 @@ +#include "prc_inc_clsfunc" + +int GetBlastDamageDices(object oInvoker, int nInvokerLevel) +{ + int nDmgDice; + if(nInvokerLevel < 13) + nDmgDice = (nInvokerLevel + 1) / 2; + else if(nInvokerLevel < 20) + nDmgDice = (nInvokerLevel + 7) / 3; + else + nDmgDice = 9 + (nInvokerLevel - 20) / 2; + + //check for the epic feats + if(GetHasFeat(FEAT_EPIC_ELDRITCH_BLAST_I, oInvoker)) + { + int nFeatAmt = 0; + int bDone = FALSE; + while(!bDone) + { if(nFeatAmt >= 9) + bDone = TRUE; + else if(GetHasFeat(FEAT_EPIC_ELDRITCH_BLAST_II + nFeatAmt, oInvoker)) + nFeatAmt++; + else + bDone = TRUE; + } + nDmgDice += nFeatAmt; + } + + return nDmgDice; +} + +// Spellblast should use only AoE spells but Dispel Magic can be cast as AoE or single target +// we make sure here that we use AoE version +int CheckSpecialTarget(int nSpellID) +{ + return nSpellID == SPELL_DISPEL_MAGIC + || nSpellID == SPELL_GREATER_DISPELLING + || nSpellID == SPELL_LESSER_DISPEL + || nSpellID == SPELL_MORDENKAINENS_DISJUNCTION + || nSpellID == SPELL_POWER_WORD_KILL; +} + +void DoSpellBlast(object oPC, int bHit) +{ + int nSpellbookID = GetLocalInt(oPC, "ET_SPELL_CURRENT"); +//DoDebug("nSpellbookID = "+IntToString(nSpellbookID)); + if(nSpellbookID) + { + object oTarget = GetSpellTargetObject(); + if(GetIsObjectValid(oTarget)) + { + nSpellbookID--; + DeleteLocalInt(oPC, "ET_SPELL_CURRENT"); + int nSpellID = GetLocalInt(oPC, "ET_REAL_SPELL_CURRENT"); +//DoDebug("nSpellID = "+IntToString(nSpellID)); + string sArray = GetLocalString(oPC, "ET_SPELL_CURRENT"); +//DoDebug("sArray = "+sArray); + int nUses = sArray == "" ? GetHasSpell(nSpellbookID, oPC) : + persistant_array_get_int(oPC, sArray, nSpellbookID); + + if(nUses) + { + // expend spell use + if(sArray == "") + { + DecrementRemainingSpellUses(oPC, nSpellID); + } + else + { + nUses--; + persistant_array_set_int(oPC, sArray, nSpellbookID, nUses); + } + + // use AoE Dispel Magic + int bTargetOverride = CheckSpecialTarget(nSpellID); + + if(bHit) + { + int nCastingClass = GetETArcaneClass(oPC); + int nDC = 10 + PRCGetSpellLevelForClass(nSpellID, nCastingClass) + GetDCAbilityModForClass(nCastingClass, oPC); + //clear action queue to apply spell effect right after blast effect + ClearAllActions(); + //override PRCDoMeleeTouchAttack() - we already know that blast hit + ActionDoCommand(SetLocalInt(oPC, "AttackHasHit", bHit)); + SetLocalInt(oPC, "EldritchSpellBlast", TRUE); + if(DEBUG) DoDebug("inv_inc_blast >> EldritchSpellBlast Set"); + ActionCastSpell(nSpellID, 0, nDC, 0, METAMAGIC_NONE, nCastingClass, FALSE, bTargetOverride); + ActionDoCommand(DeleteLocalInt(oPC, "AttackHasHit")); + DelayCommand(0.5, DeleteLocalInt(oPC, "EldritchSpellBlast")); + } + } + } + } +} + +void ApplyBlastDamage(object oCaster, object oTarget, int iAttackRoll, int iSR, int iDamage, int iDamageType, int iDamageType2, int nHellFire, int bSneak = TRUE, int nMsg = FALSE) +{ + if (DEBUG) DoDebug("ApplyBlastDamage oCaster "+GetName(oCaster)+" oTarget "+GetName(oTarget)+" iAttackRoll "+IntToString(iAttackRoll)+" iSR "+IntToString(iSR)+" iDamage "+IntToString(iDamage)+" iDamageType "+IntToString(iDamageType)+" iDamageType2 "+IntToString(iDamageType2)+" nHellFire "+IntToString(nHellFire)+" bSneak "+IntToString(bSneak)+" nMsg "+IntToString(nMsg)); + + // Is it a critical hit? + iDamage *= iAttackRoll; + if(iAttackRoll) + { + // Heal the Undead + if (iDamageType == DAMAGE_TYPE_NEGATIVE && (MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || GetLocalInt(oTarget, "AcererakHealing") || (GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD))) + { + //Set the heal effect + effect eHeal = EffectHeal(iDamage); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget); + } + else // Other targets + { + if(!GetPRCSwitch(PRC_SPELL_SNEAK_DISABLE) && bSneak) + iDamage += SpellSneakAttackDamage(oCaster, oTarget); + + effect eDamage; + if(!iSR) + { + if(iDamageType == iDamageType2) + eDamage = EffectDamage(iDamage, iDamageType); + else + { + eDamage = EffectDamage(iDamage / 2, iDamageType); + eDamage = EffectLinkEffects(eDamage, EffectDamage(iDamage / 2, iDamageType2)); + } + if(nHellFire) + eDamage = EffectLinkEffects(eDamage, EffectDamage(d6(nHellFire), DAMAGE_TYPE_DIVINE)); + } + else if(iDamageType == DAMAGE_TYPE_ACID || iDamageType2 == DAMAGE_TYPE_ACID) + { + if(iDamageType == iDamageType2) + eDamage = EffectDamage(iDamage, iDamageType); + else + eDamage = EffectDamage(iDamage / 2, iDamageType); + } + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget); + } + } +} + +int HellFireConDamage(object oPC) +{ + if(GetIsImmune(oPC, IMMUNITY_TYPE_ABILITY_DECREASE)) + { + if(DEBUG) DoDebug("HellFireConDamage: Immune to ability damage!"); + return FALSE; + } + + ApplyAbilityDamage(oPC, ABILITY_CONSTITUTION, 1, DURATION_TYPE_TEMPORARY, TRUE, -1.0); + return TRUE; +} + +int GetIsHellFireBlast(object oPC) +{ + if(GetLocalInt(oPC, "INV_HELLFIRE")) + { + DeleteLocalInt(oPC, "INV_HELLFIRE"); + return TRUE; + } + return FALSE; +} diff --git a/src/include/inv_inc_invfunc.nss b/src/include/inv_inc_invfunc.nss new file mode 100644 index 0000000..ba50935 --- /dev/null +++ b/src/include/inv_inc_invfunc.nss @@ -0,0 +1,887 @@ +//:://///////////////////////////////////////////// +//:: Invocation include: Miscellaneous +//:: inv_inc_invfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Invocation implementation. + + Also acts as inclusion nexus for the general + invocation includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Fox + @date Created - 2008.1.25 + + Updated for .35 by Jaysyn 2023/03/10 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int INVOCATION_DRACONIC = 1; +const int INVOCATION_WARLOCK = 2; + +const int INVOCATION_LEAST = 2; +const int INVOCATION_LESSER = 4; +const int INVOCATION_GREATER = 6; +const int INVOCATION_DARK = 8; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +//:: Updates the Invocation DC for Ability Focus feats. +int InvokerAbilityFocus(object oPC, int nEssence, int nEssence2 = -1); + +/** + * Determines from what class's invocation list the currently casted + * invocation is cast from. + * + * @param oInvoker A creature invoking at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetInvokingClass(object oInvoker = OBJECT_SELF); + +/** + * Determines the given creature's Invoker level. If a class is specified, + * then returns the Invoker level for that class. Otherwise, returns + * the Invoker level for the currently active invocation. + * + * @param oInvoker The creature whose Invoker level to determine + * @param nSpecificClass The class to determine the creature's Invoker + * level in. + * @param bPracticedInvoker If this is set, it will add the bunus from + * Practiced Invoker feat. + * @return The Invoker level + */ +int GetInvokerLevel(object oInvoker = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID, int bPracticedInvoker = TRUE); + +/** + * Determines whether a given creature uses Invocations. + * Requires either levels in an invocation-related class or + * natural Invocation ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use Invocations, FALSE otherwise. + */ +int GetIsInvocationUser(object oCreature); + +/** + * Determines the given creature's highest undmodified Invoker level among it's + * invoking classes. + * + * @param oCreature Creature whose highest Invoker level to determine + * @return The highest unmodified Invoker level the creature can have + */ +int GetHighestInvokerLevel(object oCreature); + +/** + * Determines whether a given class is an invocation-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is an invocation-related class, FALSE otherwise + */ +int GetIsInvocationClass(int nClass); + +/** + * Gets the level of the invocation being currently cast. + * WARNING: Return value is not defined when an invocation is not being cast. + * + * @param oInvoker The creature currently casting an invocation + * @return The level of the invocation being cast + */ +int GetInvocationLevel(object oInvoker); + +/** + * Returns the name of the invocation + * + * @param nSpellId SpellId of the invocation + */ +string GetInvocationName(int nSpellId); + +/** + * Calculates how many invoker levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added invoker levels for + * @return The number of invoker levels gained + */ +int GetInvocationPRCLevels(object oCaster); + +/** + * Determines which of the character's classes is their highest or first invocation + * casting class, if any. This is the one which gains invoker level raise benefits + * from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first invocation casting class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryInvocationClass(object oCreature = OBJECT_SELF); + +/** + * Determines the position of a creature's first invocation casting class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first invocation class {1, 2, 3} or 0 if + * the creature possesses no levels in invocation classes. + */ +int GetFirstInvocationClassPosition(object oCreature = OBJECT_SELF); + +/** + * Ruterns the number of damage dices that oInvokers eldritch blast has + * + * @param oInvoker Creature whose blast to test + * @param nInvokerLevel Invoker level + * @return The number of damage dices + */ +int GetBlastDamageDices(object oInvoker, int nInvokerLevel); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "prc_alterations" +#include "prc_feat_const" +#include "inv_inc_invknown" +#include "inv_inc_invoke" +#include "inv_inc_blast" +#include "prc_add_spell_dc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +//:: Updates the Invocation DC for Ability Focus feats. +int InvokerAbilityFocus(object oPC, int nEssence, int nEssence2 = -1) +{ + int nBonus = 0; + + // Check for the shape + switch(nEssence) + { + case INVOKE_ELDRITCH_BLAST: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_CHAIN: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_CHAIN, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_CONE: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_CONE, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_DOOM: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_DOOM, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_GLAIVE: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_GLAIVE, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_LINE: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_LINE, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_SPEAR: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_SPEAR, oPC)) nBonus += 2; + break; + case INVOKE_BRIMSTONE_BLAST: + if (GetHasFeat(FEAT_ABFOC_BRIMSTONE_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_NOXIOUS_BLAST: + if (GetHasFeat(FEAT_ABFOC_NOXIOUS_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_FRIGHTFUL_BLAST: + if (GetHasFeat(FEAT_ABFOC_FRIGHTFUL_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_SICKENING_BLAST: + if (GetHasFeat(FEAT_ABFOC_SICKENING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_HELLRIME_BLAST: + if (GetHasFeat(FEAT_ABFOC_HELLRIME_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_BEWITCHING_BLAST: + if (GetHasFeat(FEAT_ABFOC_BEWITCHING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_BINDING_BLAST: + if (GetHasFeat(FEAT_ABFOC_BINDING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_HINDERING_BLAST: + if (GetHasFeat(FEAT_ABFOC_HINDERING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_PENETRATING_BLAST: + if (GetHasFeat(FEAT_ABFOC_PENETRATING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_UTTERDARK_BLAST: + if (GetHasFeat(FEAT_ABFOC_UTTERDARK_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_INCARNUM_BLAST: + if (GetHasFeat(FEAT_ABFOC_INCARNUM_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_HAMMER_BLAST: + if (GetHasFeat(FEAT_ABFOC_HAMMER_BLAST, oPC)) nBonus += 2; + break; + // case INVOKE_VITRIOLIC_BLAST: + // if (GetHasFeat(FEAT_ABFOC_VITRIOLIC_BLAST, oPC)) nBonus += 2; + // break; + case INVOKE_BANEFUL_BLAST_ABERRATION: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ABERRATION, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_BEAST: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_BEAST, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_CONSTRUCT: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_CONSTRUCT, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_DRAGON: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_DRAGON, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_DWARF: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_DWARF, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_ELEMENTAL: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ELEMENTAL, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_ELF: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ELF, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_FEY: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_FEY, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_GIANT: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_GIANT, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_GOBLINOID: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_GOBLINOID, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_GNOME: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_GNOME, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_HALFLING: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_HALFLING, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_HUMAN: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_HUMAN, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_MONSTROUS: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_MONSTROUS, oPC)) nBonus += 2; + break; + // case INVOKE_BANEFUL_BLAST_OOZE: + // if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_OOZE, oPC)) nBonus += 2; + // break; + case INVOKE_BANEFUL_BLAST_ORC: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ORC, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_OUTSIDER: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_OUTSIDER, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_PLANT: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_PLANT, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_REPTILIAN: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_REPTILIAN, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_SHAPECHANGER: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_SHAPECHANGER, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_UNDEAD: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_UNDEAD, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_VERMIN: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_VERMIN, oPC)) nBonus += 2; + break; + } + + // Check for the secondary shape or essence component + switch(nEssence2) + { + case INVOKE_ELDRITCH_BLAST: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_CHAIN: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_CHAIN, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_CONE: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_CONE, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_DOOM: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_DOOM, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_GLAIVE: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_GLAIVE, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_LINE: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_LINE, oPC)) nBonus += 2; + break; + case INVOKE_ELDRITCH_SPEAR: + if (GetHasFeat(FEAT_ABFOC_ELDRITCH_SPEAR, oPC)) nBonus += 2; + break; + case INVOKE_BRIMSTONE_BLAST: + if (GetHasFeat(FEAT_ABFOC_BRIMSTONE_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_NOXIOUS_BLAST: + if (GetHasFeat(FEAT_ABFOC_NOXIOUS_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_FRIGHTFUL_BLAST: + if (GetHasFeat(FEAT_ABFOC_FRIGHTFUL_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_SICKENING_BLAST: + if (GetHasFeat(FEAT_ABFOC_SICKENING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_HELLRIME_BLAST: + if (GetHasFeat(FEAT_ABFOC_HELLRIME_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_BEWITCHING_BLAST: + if (GetHasFeat(FEAT_ABFOC_BEWITCHING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_BINDING_BLAST: + if (GetHasFeat(FEAT_ABFOC_BINDING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_HINDERING_BLAST: + if (GetHasFeat(FEAT_ABFOC_HINDERING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_PENETRATING_BLAST: + if (GetHasFeat(FEAT_ABFOC_PENETRATING_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_UTTERDARK_BLAST: + if (GetHasFeat(FEAT_ABFOC_UTTERDARK_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_INCARNUM_BLAST: + if (GetHasFeat(FEAT_ABFOC_INCARNUM_BLAST, oPC)) nBonus += 2; + break; + case INVOKE_HAMMER_BLAST: + if (GetHasFeat(FEAT_ABFOC_HAMMER_BLAST, oPC)) nBonus += 2; + break; + // case INVOKE_VITRIOLIC_BLAST: + // if (GetHasFeat(FEAT_ABFOC_VITRIOLIC_BLAST, oPC)) nBonus += 2; + // break; + case INVOKE_BANEFUL_BLAST_ABERRATION: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ABERRATION, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_BEAST: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_BEAST, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_CONSTRUCT: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_CONSTRUCT, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_DRAGON: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_DRAGON, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_DWARF: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_DWARF, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_ELEMENTAL: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ELEMENTAL, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_ELF: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ELF, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_FEY: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_FEY, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_GIANT: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_GIANT, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_GOBLINOID: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_GOBLINOID, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_GNOME: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_GNOME, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_HALFLING: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_HALFLING, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_HUMAN: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_HUMAN, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_MONSTROUS: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_MONSTROUS, oPC)) nBonus += 2; + break; + // case INVOKE_BANEFUL_BLAST_OOZE: + // if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_OOZE, oPC)) nBonus += 2; + // break; + case INVOKE_BANEFUL_BLAST_ORC: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_ORC, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_OUTSIDER: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_OUTSIDER, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_PLANT: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_PLANT, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_REPTILIAN: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_REPTILIAN, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_SHAPECHANGER: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_SHAPECHANGER, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_UNDEAD: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_UNDEAD, oPC)) nBonus += 2; + break; + case INVOKE_BANEFUL_BLAST_VERMIN: + if (GetHasFeat(FEAT_ABFOC_BANEFUL_BLAST_VERMIN, oPC)) nBonus += 2; + break; + } + + return nBonus; +} + +int GetInvokingClass(object oInvoker = OBJECT_SELF) +{ + return GetLocalInt(oInvoker, PRC_INVOKING_CLASS) - 1; +} + +/*int PracticedInvoker(object oInvoker, int iInvokingClass, int iInvokingLevels) +{ + int nFeat; + int iAdjustment = GetHitDice(oInvoker) - iInvokingLevels; + if(iAdjustment > 4) iAdjustment = 4; + if(iAdjustment < 0) iAdjustment = 0; + + switch(iInvokingClass) + { + case CLASS_TYPE_DRAGONFIRE_ADEPT: nFeat = FEAT_PRACTICED_INVOKER_DRAGONFIRE_ADEPT; break; + case CLASS_TYPE_WARLOCK: nFeat = FEAT_PRACTICED_INVOKER_WARLOCK; break; + default: return 0; + } + + if(GetHasFeat(nFeat, oInvoker)) + return iAdjustment; + + return 0; +}*/ + +int GetInvokerLevel(object oInvoker = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID, int bPracticedInvoker = TRUE) +{ + int nAdjust = GetLocalInt(oInvoker, PRC_CASTERLEVEL_ADJUSTMENT); + int nLevel = GetLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE); + + // For when you want to assign the caster level. + if(nLevel) + { + if(DEBUG) SendMessageToPC(oInvoker, "Forced-level Invoking at level " + IntToString(GetCasterLevel(oInvoker))); + //DelayCommand(1.0, DeleteLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE)); + return nLevel + nAdjust; + } + + if(nSpecificClass == CLASS_TYPE_INVALID) + nSpecificClass = GetInvokingClass(oInvoker); + + if(nSpecificClass != -1) + { + if(!GetIsInvocationClass(nSpecificClass)) + return 0; + + if(nSpecificClass == CLASS_TYPE_DRAGON_SHAMAN) + nLevel = PRCMax(GetLevelByClass(nSpecificClass, oInvoker) - 4, 1); // Can't go below 1 + else + nLevel = GetLevelByClass(nSpecificClass, oInvoker); + if(DEBUG) DoDebug("Invoker Class Level is: " + IntToString(nLevel)); + if(GetPrimaryInvocationClass(oInvoker) == nSpecificClass) + { + //Invoker level is class level + any arcane spellcasting or invoking levels in any PRCs + nLevel += GetInvocationPRCLevels(oInvoker); + } + /*if(bPracticedInvoker) + nLevel += PracticedInvoker(oInvoker, nSpecificClass, nLevel);*/ + } + else + nLevel = GetLevelByClass(GetPrimaryInvocationClass(oInvoker), oInvoker); + + nLevel += nAdjust; + SetLocalInt(oInvoker, "InvokerLevel", nLevel); + return nLevel; +} + +int GetIsInvocationUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCreature) || + GetLevelByClass(CLASS_TYPE_WARLOCK, oCreature) || + GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oCreature) + ); +} + +int GetHighestInvokerLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetInvokerLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestInvokerLevel(object oCreature) +{ + return PRCMax(PRCMax(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetInvokerLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetInvokerLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetInvokerLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsInvocationClass(int nClass) +{ + int bTest = nClass == CLASS_TYPE_DRAGONFIRE_ADEPT + || nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGON_SHAMAN; + return bTest; +} + +int GetInvocationLevel(object oInvoker) +{ + return GetLocalInt(oInvoker, PRC_INVOCATION_LEVEL); +} + +string GetInvocationName(int nSpellId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))); +} + +int GetInvocationPRCLevels(object oCaster) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_HELLFIRE_WARLOCK, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster) + + GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + +//:: Some Arcane PrCs boost invocations +/* if(GetLocalInt(oCaster, "INV_Caster") == 2) + nLevel += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2 + + (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2 + + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster) + + GetLevelByClass(CLASS_TYPE_MAESTER, oCaster) + + (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; */ + + if(GetLocalInt(oCaster, "INV_Caster") == 2) + { + //:: Abjurant Champion Invoking + if(GetHasFeat(FEAT_ABCHAMP_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ABCHAMP_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ABCHAMP_INVOKING_DRAGON_SHAMAN, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + //:: Acolyte of the Skin Invoking + if(GetHasFeat(FEAT_AOTS_INVOKING_WARLOCK, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_AOTS_INVOKING_DFA, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2; + + //:: Anima Mage Invoking + if(GetHasFeat(FEAT_ANIMA_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ANIMA_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + //:: Arcane Trickster Invoking + if(GetHasFeat(FEAT_ARCTRICK_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + //:: Disciple of Asmodeus Invoking + if(GetHasFeat(FEAT_ASMODEUS_INVOKING_WARLOCK, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ASMODEUS_INVOKING_DFA, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ASMODEUS_INVOKING_DRAGON_SHAMAN, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + //:: Blood Magus Invoking + if(GetHasFeat(FEAT_BLDMAGUS_INVOKING_WARLOCK, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BLDMAGUS_INVOKING_DFA, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster) + 1) / 2; + + //:: Enlightened Fist Invoking + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_INVOKING_DRAGON_SHAMAN, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + //:: Maester Invoking + if(GetHasFeat(FEAT_MAESTER_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAESTER_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + //:: Talon of Tiamat Invoking + if(GetHasFeat(FEAT_TIAMAT_INVOKING_WARLOCK, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_INVOKING_DFA, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_INVOKING_DRAGON_SHAMAN, oCaster)) + nLevel += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + //:: Unseen Seer Invoking + if(GetHasFeat(FEAT_UNSEEN_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_INVOKING_DRAGON_SHAMAN, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + //:: Virtuoso Invoking + if(GetHasFeat(FEAT_VIRTUOSO_INVOKING_WARLOCK, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_INVOKING_DFA, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_INVOKING_DRAGON_SHAMAN, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + //:: Wild Mage Invoking + if(GetHasFeat(FEAT_WILDMAGE_INVOKING_WARLOCK, oCaster)) + { + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) + nLevel += nClass - 3 + d6(); + } + if(GetHasFeat(FEAT_WILDMAGE_INVOKING_DFA, oCaster)) + { + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) + nLevel += nClass - 3 + d6(); + } + if(GetHasFeat(FEAT_WILDMAGE_INVOKING_DRAGON_SHAMAN, oCaster)) + { + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) + nLevel += nClass - 3 + d6(); + } + } + + return nLevel; +} + +int GetPrimaryInvocationClass(object oCreature = OBJECT_SELF) +{ + int nClass; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nInvocationPos = GetFirstInvocationClassPosition(oCreature); + if (!nInvocationPos) return CLASS_TYPE_INVALID; // no invoking class + + nClass = GetClassByPosition(nInvocationPos, oCreature); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsInvocationClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsInvocationClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsInvocationClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsInvocationClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsInvocationClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsInvocationClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsInvocationClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsInvocationClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstInvocationClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsInvocationClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsInvocationClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsInvocationClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsInvocationClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsInvocationClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsInvocationClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsInvocationClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsInvocationClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int GetInvocationSaveDC(object oTarget, object oCaster, int nSpellID = -1) +{ + int nDC; + // For when you want to assign the caster DC + //this does not take feat/race/class into account, it is an absolute override + if (GetLocalInt(oCaster, PRC_DC_TOTAL_OVERRIDE) != 0) + { + nDC = GetLocalInt(oCaster, PRC_DC_TOTAL_OVERRIDE); + DoDebug("Forced-DC PRC_DC_TOTAL_OVERRIDE casting at DC " + IntToString(nDC)); + return nDC; + } + // For when you want to assign the caster DC + //this does take feat/race/class into account, it only overrides the baseDC + if(GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE) > 0) + { + nDC = GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE); + if(DEBUG) DoDebug("Forced Base-DC casting at DC " + IntToString(nDC)); + } + else + { + if(nSpellID == -1) nSpellID = PRCGetSpellId(); + //10+spelllevel+stat(cha default) + nDC = 10; + nDC += StringToInt(Get2DACache("Spells", "Innate", nSpellID)); + nDC += GetAbilityModifier(ABILITY_CHARISMA, oCaster); + } + nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, 0); + + return nDC; +} + +void ClearInvocationLocalVars(object oPC) +{ + //Invocations + if (DEBUG) DoDebug("Clearing invocation flags"); + DeleteLocalObject(oPC, "ChillingFog"); + //Endure Exposure wearing off + array_delete(oPC, "BreathProtected"); + DeleteLocalInt(oPC, "DragonWard"); + + //cleaning targets of Endure exposure cast by resting caster + if (array_exists(oPC, "BreathProtectTargets")) + { + if(DEBUG) DoDebug("Checking for casts of Endure Exposure"); + int nBPTIndex = 0; + int bCasterDone = FALSE; + int bTargetDone = FALSE; + object oBreathTarget; + while(!bCasterDone) + { + oBreathTarget = array_get_object(oPC, "BreathProtectTargets", nBPTIndex); + if(DEBUG) DoDebug("Possible target: " + GetName(oBreathTarget) + " - " + ObjectToString(oBreathTarget)); + if(oBreathTarget != OBJECT_INVALID) + { + //replace caster with target... always immune to own breath, so good way to erase caster from array without deleting whole array + int nBPIndex = 0; + + while(!bTargetDone) + { + if(DEBUG) DoDebug("Checking " + GetName(oBreathTarget)); + //if it matches, remove and end + if(array_get_object(oBreathTarget, "BreathProtected", nBPIndex) == oPC) + { + array_set_object(oBreathTarget, "BreathProtected", nBPIndex, oBreathTarget); + bTargetDone = TRUE; + if(DEBUG) DoDebug("Found caster, clearing."); + } + //if it is not end of array, keep going + else if(array_get_object(oBreathTarget, "BreathProtected", nBPTIndex) != OBJECT_INVALID) + { + nBPIndex++; + } + else + bTargetDone = TRUE; + + } + + nBPTIndex++; + bTargetDone = FALSE; + + } + else + { + array_delete(oPC, "BreathProtectTargets"); + bCasterDone = TRUE; + } + } + } +} + +// Test main +// void main(){} diff --git a/src/include/inv_inc_invknown.nss b/src/include/inv_inc_invknown.nss new file mode 100644 index 0000000..dbd7acb --- /dev/null +++ b/src/include/inv_inc_invknown.nss @@ -0,0 +1,529 @@ +//:://///////////////////////////////////////////// +//:: Invocation include: Invocations Known +//:: inv_inc_invknown +//:://///////////////////////////////////////////// +/** @file + Defines functions for adding & removing + Invocations known. + + Data stored: + + - For each Class list + -- Total number of Invocations known + -- A modifier value to maximum Invocations known on this list to account for feats and classes that add Invocations + -- An array related to Invocations the knowledge of which is not dependent on character level + --- Each array entry specifies the spells.2da row of the known Invocations's class-specific entry + -- For each character level on which Invocations have been gained from this list + --- An array of Invocations gained on this level + ---- Each array entry specifies the spells.2da row of the known Invocations's class-specific entry + + @author Fox + @date Created - 2008.01.25 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +// Included here to provide the values for the constants below +#include "prc_class_const" + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int INVOCATION_LIST_DRAGONFIRE_ADEPT = CLASS_TYPE_DRAGONFIRE_ADEPT; +const int INVOCATION_LIST_WARLOCK = CLASS_TYPE_WARLOCK; +const int INVOCATION_LIST_DRAGON_SHAMAN = CLASS_TYPE_DRAGON_SHAMAN; + +/// Special Maneuver list. Maneuvers gained via Extra Invocation or other sources. +const int INVOCATION_LIST_EXTRA = CLASS_TYPE_INVALID;//-1; +const int INVOCATION_LIST_EXTRA_EPIC = /*CLASS_TYPE_INVALID - 1;*/-2; //needs a constant in there to compile properly + +const string _INVOCATION_LIST_NAME_BASE = "PRC_InvocationList_"; +const string _INVOCATION_LIST_TOTAL_KNOWN = "_TotalKnown"; +const string _INVOCATION_LIST_MODIFIER = "_KnownModifier"; +const string _INVOCATION_LIST_EXTRA_ARRAY = "_InvocationsKnownExtraArray"; +const string _INVOCATION_LIST_EXTRA_EPIC_ARRAY = "_InvocationsKnownExtraEpicArray"; +const string _INVOCATION_LIST_LEVEL_ARRAY = "_InvocationsKnownLevelArray_"; +const string _INVOCATION_LIST_GENERAL_ARRAY = "_InvocationsKnownGeneralArray"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gives the creature the control feats for the given Invocation and marks the Invocation + * in a Invocations known array. + * If the Invocation's data is already stored in one of the Invocations known arrays for + * the list or adding the Invocation's data to the array fails, the function aborts. + * + * @param oCreature The creature to gain the Invocation + * @param nList The list the Invocation comes from. One of INVOCATION_LIST_* + * @param n2daRow The 2da row in the lists's 2da file that specifies the Invocation. + * @param bLevelDependent If this is TRUE, the Invocation is tied to a certain level and can + * be lost via level loss. If FALSE, the Invocation is not dependent + * of a level and cannot be lost via level loss. + * @param nLevelToTieTo If bLevelDependent is TRUE, this specifies the level the Invocation + * is gained on. Otherwise, it's ignored. + * The default value (-1) means that the current level of oCreature + * will be used. + * + * @return TRUE if the Invocation was successfully stored and control feats added. + * FALSE otherwise. + */ +int AddInvocationKnown(object oCreature, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1); + +/** + * Removes all Invocations gained from each list on the given level. + * + * @param oCreature The creature whose Invocations to remove + * @param nLevel The level to clear + */ +void RemoveInvocationsKnownOnLevel(object oCreature, int nLevel); + +/** + * Gets the value of the Invocations known modifier, which is a value that is added + * to the 2da-specified maximum Invocations known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to get + * @param nList The list the maximum Invocations known from which the modifier + * modifies. One of INVOCATION_LIST_* + */ +int GetKnownInvocationsModifier(object oCreature, int nList); + +/** + * Sets the value of the Invocations known modifier, which is a value that is added + * to the 2da-specified maximum Invocations known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to set + * @param nList The list the maximum Invocations known from which the modifier + * modifies. One of INVOCATION_LIST_* + */ +void SetKnownInvocationsModifier(object oCreature, int nList, int nNewValue); + +/** + * Gets the number of Invocations a character character possesses from a + * specific list and lexicon + * + * @param oCreature The creature whose Invocations to check + * @param nList The list to check. One of INVOCATION_LIST_* + * @return The number of Invocations known oCreature has from nList + */ +int GetInvocationCount(object oCreature, int nList); + +/** + * Gets the maximum number of Invocations a character may posses from a given list + * at this time. Calculated based on class levels, feats and a misceallenous + * modifier. There are three Types of Invocations, so it checks each seperately. + * + * @param oCreature Character to determine maximum Invocations for + * @param nList INVOCATION_LIST_* of the list to determine maximum Invocations for + * @return Maximum number of Invocations that oCreature may know from the given list. + */ +int GetMaxInvocationCount(object oCreature, int nList); + +/** + * Determines whether a character has a given Invocation, gained via some Invocation list. + * + * @param nInvocation INVOKE_* of the Invocation to test + * @param oCreature Character to test for the possession of the Invocation + * @return TRUE if the character has the Invocation, FALSE otherwise + */ +int GetHasInvocation(int nInvocation, object oCreature = OBJECT_SELF); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_item_props" +#include "prc_x2_itemprop" +#include "inc_lookups" +#include "prc_inc_nwscript" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _InvocationRecurseRemoveArray(object oCreature, string sArrayName, string sInvocFile, int nArraySize, int nCurIndex) +{ + if(DEBUG) DoDebug("_InvocationRecurseRemoveArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "sArrayName = '" + sArrayName + "'\n" + + "sInvocFile = '" + sInvocFile + "'\n" + + "nArraySize = " + IntToString(nArraySize) + "\n" + + "nCurIndex = " + IntToString(nCurIndex) + "\n" + ); + + // Determine whether we've already parsed the whole array or not + if(nCurIndex >= nArraySize) + { + if(DEBUG) DoDebug("_InvocationRecurseRemoveArray(): Running itemproperty removal loop."); + // Loop over itemproperties on the skin and remove each match + object oSkin = GetPCSkin(oCreature); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + GetLocalInt(oCreature, "PRC_InvocFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))) + ) + { + if(DEBUG) DoDebug("_InvocationRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + } + + ipTest = GetNextItemProperty(oSkin); + } + } + // Still parsing the array + else + { + // Set the marker + string sName = "PRC_InvocFeatRemovalMarker_" + Get2DACache(sInvocFile, "IPFeatID", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArrayName, nCurIndex)) + ); + if(DEBUG) DoDebug("_InvocationRecurseRemoveArray(): Recursing through array, marker set:\n" + sName); + + SetLocalInt(oCreature, sName, TRUE); + // Recurse to next array index + _InvocationRecurseRemoveArray(oCreature, sArrayName, sInvocFile, nArraySize, nCurIndex + 1); + // After returning, delete the local + DeleteLocalInt(oCreature, sName); + } +} + +void _RemoveInvocationArray(object oCreature, int nList, int nLevel) +{ + if(DEBUG) DoDebug("_RemoveInvocationArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + ); + + string sBase = _INVOCATION_LIST_NAME_BASE + IntToString(nList); + string sArray = sBase + _INVOCATION_LIST_LEVEL_ARRAY + IntToString(nLevel); + int nSize = persistant_array_get_size(oCreature, sArray); + + // Reduce the total by the array size + SetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN) - nSize + ); + + // Remove each Invocation in the array + _InvocationRecurseRemoveArray(oCreature, sArray, GetAMSDefinitionFileName(nList), nSize, 0); + + // Remove the array itself + persistant_array_delete(oCreature, sArray); +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int AddInvocationKnown(object oCreature, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1) +{ + string sBase = _INVOCATION_LIST_NAME_BASE + IntToString(nList); + string sArray = sBase; + string sPowerFile = GetAMSDefinitionFileName(/*PowerListToClassType(*/nList/*)*/); + if(nList == -2 || nList == CLASS_TYPE_INVALID) + { + sPowerFile = GetAMSDefinitionFileName(GetPrimaryInvocationClass(oCreature)); + } + string sTestArray; + int i, j, nSize, bReturn; + + // Get the spells.2da row corresponding to the cls_psipw_*.2da row + int nSpells2daRow = StringToInt(Get2DACache(sPowerFile, "SpellID", n2daRow)); + + // Determine the array name. + if(bLevelDependent) + { + // If no level is specified, default to the creature's current level + if(nLevelToTieTo == -1) + nLevelToTieTo = GetHitDice(oCreature); + + sArray += _INVOCATION_LIST_LEVEL_ARRAY + IntToString(nLevelToTieTo); + } + else + { + sArray += _INVOCATION_LIST_GENERAL_ARRAY; + } + + // Make sure the power isn't already in an array. If it is, abort and return FALSE + // Loop over each level array and check that it isn't there. + if(DEBUG) DoDebug("inv_inc_invknown: Checking first array set for duplicates."); + for(i = 1; i <= GetHitDice(oCreature); i++) + { + sTestArray = sBase + _INVOCATION_LIST_LEVEL_ARRAY + IntToString(i); + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + } + // Check the non-level-dependent array + if(DEBUG) DoDebug("inv_inc_invknown: Checking second array set for duplicates."); + sTestArray = sBase + _INVOCATION_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + + // All checks are made, now start adding the new power + // Create the array if it doesn't exist yet + if(!persistant_array_exists(oCreature, sArray)) + persistant_array_create(oCreature, sArray); + + // Store the power in the array + if(DEBUG) DoDebug("inv_inc_invknown: Adding to invocation array."); + if(persistant_array_set_int(oCreature, sArray, persistant_array_get_size(oCreature, sArray), nSpells2daRow) != SDL_SUCCESS) + { + if(DEBUG) DoDebug("inv_inc_invknown: AddPowerKnown(): ERROR: Unable to add power to known array\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + + "n2daRow = " + IntToString(n2daRow) + "\n" + + "bLevelDependent = " + DebugBool2String(bLevelDependent) + "\n" + + "nLevelToTieTo = " + IntToString(nLevelToTieTo) + "\n" + + "nSpells2daRow = " + IntToString(nSpells2daRow) + "\n" + ); + return FALSE; + } + + // Increment Invocations known total + SetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN) + 1 + ); + + // Give the power's control feats + object oSkin = GetPCSkin(oCreature); + string sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID", n2daRow); + itemproperty ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + // Second power feat, if any + sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID2", n2daRow); + if(sPowerFeatIP != "") + { + ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + + return TRUE; +} + +void RemoveInvocationsKnownOnLevel(object oCreature, int nLevel) +{ + if(DEBUG) DoDebug("inv_inc_invknown: RemoveInvocationKnownOnLevel():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + + string sPostFix = _INVOCATION_LIST_LEVEL_ARRAY + IntToString(nLevel); + // For each Invocation list, determine if an array exists for this level. + if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_DRAGONFIRE_ADEPT) + sPostFix)) + // If one does exist, clear it + _RemoveInvocationArray(oCreature, INVOCATION_LIST_DRAGONFIRE_ADEPT, nLevel); + + if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_WARLOCK) + sPostFix)) + _RemoveInvocationArray(oCreature, INVOCATION_LIST_WARLOCK, nLevel); + + if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_DRAGON_SHAMAN) + sPostFix)) + _RemoveInvocationArray(oCreature, INVOCATION_LIST_DRAGON_SHAMAN, nLevel); + + if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_EXTRA) + sPostFix)) + _RemoveInvocationArray(oCreature, INVOCATION_LIST_EXTRA, nLevel); + + if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_EXTRA_EPIC) + sPostFix)) + _RemoveInvocationArray(oCreature, INVOCATION_LIST_EXTRA_EPIC, nLevel); +} + +int GetKnownInvocationsModifier(object oCreature, int nList) +{ + return GetPersistantLocalInt(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(nList) + _INVOCATION_LIST_MODIFIER); +} + +void SetKnownInvocationsModifier(object oCreature, int nList, int nNewValue) +{ + SetPersistantLocalInt(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(nList) + _INVOCATION_LIST_MODIFIER, nNewValue); +} + +int GetInvocationCount(object oCreature, int nList) +{ + return GetPersistantLocalInt(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(nList) + _INVOCATION_LIST_TOTAL_KNOWN); +} + +int GetMaxInvocationCount(object oCreature, int nList) +{ + int nMaxInvocations = 0; + + switch(nList) + { + case INVOCATION_LIST_DRAGONFIRE_ADEPT:{ + // Determine base Invocations known + int nLevel = GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCreature); + nLevel += GetPrimaryInvocationClass(oCreature) == CLASS_TYPE_DRAGONFIRE_ADEPT ? GetInvocationPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxInvocations = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_DRAGONFIRE_ADEPT), "InvocationKnown", nLevel - 1)); + + // Calculate feats + + // Add in the custom modifier + nMaxInvocations += GetKnownInvocationsModifier(oCreature, nList); + break; + } + + case INVOCATION_LIST_WARLOCK:{ + // Determine base Invocations known + int nLevel = GetLevelByClass(CLASS_TYPE_WARLOCK, oCreature); + nLevel += GetPrimaryInvocationClass(oCreature) == CLASS_TYPE_WARLOCK ? GetInvocationPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxInvocations = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_WARLOCK), "InvocationKnown", nLevel - 1)); + + // Calculate feats + + // Add in the custom modifier + nMaxInvocations += GetKnownInvocationsModifier(oCreature, nList); + break; + } + + case INVOCATION_LIST_DRAGON_SHAMAN:{ + // Determine base Invocations known + int nLevel = GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oCreature); + nLevel += GetPrimaryInvocationClass(oCreature) == CLASS_TYPE_DRAGON_SHAMAN ? GetInvocationPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxInvocations = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_DRAGON_SHAMAN), "InvocationKnown", nLevel - 1)); + + // Calculate feats + + // Add in the custom modifier + nMaxInvocations += GetKnownInvocationsModifier(oCreature, nList); + break; + } + + case INVOCATION_LIST_EXTRA: + nMaxInvocations = GetHasFeat(FEAT_EXTRA_INVOCATION_I, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_II, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_III, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_IV, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_V, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_VI, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_VII, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_VIII, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_IX, oCreature) + + GetHasFeat(FEAT_EXTRA_INVOCATION_X, oCreature); + break; + + case INVOCATION_LIST_EXTRA_EPIC: + nMaxInvocations = GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_I, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_II, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_III, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_IV, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_V, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_VI, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_VII, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_VIII, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_IX, oCreature) + + GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_X, oCreature); + break; + + default:{ + string sErr = "GetMaxInvocationCount(): ERROR: Unknown power list value: " + IntToString(nList); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return nMaxInvocations; +} + +int GetHasInvocation(int nInvocation, object oCreature = OBJECT_SELF) +{ + if((GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCreature) + && GetHasFeat(GetClassFeatFromPower(nInvocation, CLASS_TYPE_DRAGONFIRE_ADEPT), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_WARLOCK, oCreature) + && GetHasFeat(GetClassFeatFromPower(nInvocation, CLASS_TYPE_WARLOCK), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oCreature) + && GetHasFeat(GetClassFeatFromPower(nInvocation, CLASS_TYPE_DRAGON_SHAMAN), oCreature) + ) + // add new Invocation classes here + ) + return TRUE; + return FALSE; +} + +string DebugListKnownInvocations(object oCreature) +{ + string sReturn = "Invocations known by " + DebugObject2Str(oCreature) + ":\n"; + int i, j, k, numPowerLists = 6; + int nPowerList, nSize; + string sTemp, sArray, sArrayBase, sPowerFile; + // Loop over all power lists + for(i = 1; i <= numPowerLists; i++) + { + // Some padding + sReturn += " "; + // Get the power list for this loop + switch(i) + { + case 1: nPowerList = INVOCATION_LIST_DRAGONFIRE_ADEPT; sReturn += "Dragonfire Adept"; break; + + case 2: nPowerList = INVOCATION_LIST_WARLOCK; sReturn += "Warlock"; break; + case 3: nPowerList = INVOCATION_LIST_DRAGON_SHAMAN; sReturn += "Dragon Shaman"; break; + + // This should always be last + case 5: nPowerList = INVOCATION_LIST_EXTRA; sReturn += "Extra"; break; + case 6: nPowerList = INVOCATION_LIST_EXTRA_EPIC; sReturn += "Epic Extra"; break; + } + sReturn += " Invocations known:\n"; + + // Determine if the character has any Invocations from this list + sPowerFile = GetAMSDefinitionFileName(nPowerList); + sArrayBase = _INVOCATION_LIST_NAME_BASE + IntToString(nPowerList); + + // Loop over levels + for(j = 1; j <= GetHitDice(oCreature); j++) + { + sArray = sArrayBase + _INVOCATION_LIST_LEVEL_ARRAY + IntToString(j); + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Gained on level " + IntToString(j) + ":\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + // Non-leveldependent Invocations + sArray = sArrayBase + _INVOCATION_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Non-leveldependent:\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + return sReturn; +} +// Test main +//void main(){} diff --git a/src/include/inv_inc_invoke.nss b/src/include/inv_inc_invoke.nss new file mode 100644 index 0000000..0abd1e1 --- /dev/null +++ b/src/include/inv_inc_invoke.nss @@ -0,0 +1,577 @@ +//:://///////////////////////////////////////////// +//:: Invocation include: Casting +//:: inv_inc_invoke +//:://///////////////////////////////////////////// +/** @file + Defines structures and functions for handling + initiating a invocation + + @author Fox + @date Created - 2008.1.26 + @thanks to Ornedan for his work on Psionics upon which this is based. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string PRC_INVOKING_CLASS = "PRC_CurrentInvocation_InitiatingClass"; +const string PRC_INVOCATION_LEVEL = "PRC_CurrentInvocation_Level"; +const string INV_DEBUG_IGNORE_CONSTRAINTS = "INV_DEBUG_IGNORE_CONSTRAINTS"; + +/** + * The variable in which the invocation token is stored. If no token exists, + * the variable is set to point at the invoker itself. That way OBJECT_INVALID + * means the variable is unitialised. + */ +//const string PRC_INVOCATION_TOKEN_VAR = "PRC_InvocationToken"; +//const string PRC_INVOCATION_TOKEN_NAME = "PRC_INVOKETOKEN"; +//const float PRC_INVOCATION_HB_DELAY = 0.5f; + + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure that contains common data used during invocation. + */ +struct invocation{ + /* Generic stuff */ + /// The creature Truespeaking the Invocation + object oInvoker; + /// Whether the invocation is successful or not + int bCanInvoke; + /// The creature's invoker level in regards to this invocation + int nInvokerLevel; + /// The invocation's spell ID + int nInvocationId; +}; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines if the invocation that is currently being attempted to be TrueSpoken + * can in fact be truespoken. Determines metainvocations used. + * + * @param oInvoker A creature attempting to truespeak a invocation at this moment. + * @param oTarget The target of the invocation, if any. For pure Area of Effect. + * invocations, this should be OBJECT_INVALID. Otherwise the main + * target of the invocation as returned by PRCGetSpellTargetObject(). + * + * @return A invocation structure that contains the data about whether + * the invocation was successfully initiated and some other + * commonly used data, like the PC's invoker level for this invocation. + */ +struct invocation EvaluateInvocation(object oInvoker, object oTarget); + +/** + * Causes OBJECT_SELF to use the given invocation. + * + * @param nInvocation The index of the invocation to use in spells.2da or an UTTER_* + * @param nClass The index of the class to use the invocation as in classes.2da or a CLASS_TYPE_* + * @param nLevelOverride An optional override to normal invoker level. + * @param bInstant If true invocation will be used without casting animations (eldritch sculptor) + * Default: 0, which means the parameter is ignored. + */ +void UseInvocation(int nInvocation, int nClass, int nLevelOverride = 0, int bInstant = FALSE); + +/** + * A debugging function. Takes a invocation structure and + * makes a string describing the contents. + * + * @param move A set of invocation data + * @return A string describing the contents of move + */ +string DebugInvocation2Str(struct invocation invoked); + +/** + * Stores a invocation structure as a set of local variables. If + * a structure was already stored with the same name on the same object, + * it is overwritten. + * + * @param oObject The object on which to store the structure + * @param sName The name under which to store the structure + * @param move The invocation structure to store + */ +void SetLocalInvocation(object oObject, string sName, struct invocation invoked); + +/** + * Retrieves a previously stored invocation structure. If no structure is stored + * by the given name, the structure returned is empty. + * + * @param oObject The object from which to retrieve the structure + * @param sName The name under which the structure is stored + * @return The structure built from local variables stored on oObject under sName + */ +struct invocation GetLocalInvocation(object oObject, string sName); + +/** + * Deletes a stored invocation structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalInvocation(object oObject, string sName); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "inv_inc_invfunc" //Access in parent +#include "prc_spellf_inc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Handles Spellfire absorption when a utterance is used on a friendly spellfire + * user. + */ +struct invocation _DoInvocationSpellfireFriendlyAbsorption(struct invocation invoked, object oTarget) +{ + if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") && + GetIsFriend(oTarget, invoked.oInvoker) + ) + { + if(CheckSpellfire(invoked.oInvoker, oTarget, TRUE)) + { + PRCShowSpellResist(invoked.oInvoker, oTarget, SPELL_RESIST_MANTLE); + invoked.bCanInvoke = FALSE; + } + } + + return invoked; +} + +/** Internal function. + * Sets invocation-related local variables. + * + * @param oInvoker The creature currently casting invocation + * @param nClass Invocation casting class constant + * @param nLevel Invocation level + */ +void _SetInvocationVariables(object oInvoker, int nClass, int nLevel) +{ + if (DEBUG) FloatingTextStringOnCreature(GetName(oInvoker)+" is a "+IntToString(nClass)+" at "+IntToString(nLevel)+" invocation level", oInvoker); + SetLocalInt(oInvoker, PRC_INVOKING_CLASS, nClass + 1); + SetLocalInt(oInvoker, PRC_INVOCATION_LEVEL, nLevel); +} + + +/** Internal function. + * Deletes invocation-related local variables. + * + * @param oInvoker The creature currently initiating a invocation + */ +void _CleanInvocationVariables(object oInvoker) +{ + DeleteLocalInt(oInvoker, PRC_INVOKING_CLASS); + DeleteLocalInt(oInvoker, PRC_INVOCATION_LEVEL); +} + +/** Internal function. + * Determines whether a invocation token exists. If one does, returns it. + * + * @param oInvoker A creature whose invocation token to get + * @return The invocation token if it exists, OBJECT_INVALID otherwise. + */ +/*object _GetInvocationToken(object oInvoker) +{ + object oInvokeToken = GetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR); + + // If the token object is no longer valid, set the variable to point at invoker + if(!GetIsObjectValid(oInvokeToken)) + { + oInvokeToken = oInvoker; + SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvokeToken); + } + + + // Check if there is no token + if(oInvokeToken == oInvoker) + oInvokeToken = OBJECT_INVALID; + + return oInvokeToken; +}*/ + +/** Internal function. + * Destroys the given invocation token and sets the creature's invocation token variable + * to point at itself. + * + * @param oInvoker The invoker whose token to destroy + * @param oInvokeToken The token to destroy + */ +/*void _DestroyInvocationToken(object oInvoker, object oInvokeToken) +{ + DestroyObject(oInvokeToken); + SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvoker); +}*/ + +/** Internal function. + * Destroys the previous invocation token, if any, and creates a new one. + * + * @param oInvoker A creature for whom to create a invocation token + * @return The newly created token + */ +/*object _CreateInvocationToken(object oInvoker) +{ + object oInvokeToken = _GetInvocationToken(oInvoker); + object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oInvoker); + + // Delete any previous tokens + if(GetIsObjectValid(oInvokeToken)) + _DestroyInvocationToken(oInvoker, oInvokeToken); + + // Create new token and store a reference to it + oInvokeToken = CreateItemOnObject(PRC_INVOCATION_TOKEN_NAME, oStore); + SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvokeToken); + + Assert(GetIsObjectValid(oInvokeToken), "GetIsObjectValid(oInvokeToken)", "ERROR: Unable to create invocation token! Store object: " + DebugObject2Str(oStore), "inv_inc_invoke", "_CreateInvocationToken()"); + + return oInvokeToken; +}*/ + +/** Internal function. + * Determines whether the given invoker is doing something that would + * interrupt initiating a invocation or affected by an effect that would do + * the same. + * + * @param oInvoker A creature on which _InvocationHB() is running + * @return TRUE if the creature can continue initiating, + * FALSE otherwise + */ +/*int _InvocationStateCheck(object oInvoker) +{ + int nAction = GetCurrentAction(oInvoker); + // If the current action is not among those that could either be used to truespeak the invocation or movement, the invocation fails + if(!(nAction || ACTION_CASTSPELL || nAction == ACTION_INVALID || + nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT || + nAction || ACTION_USEOBJECT || nAction == ACTION_WAIT + ) ) + return FALSE; + + // Affected by something that prevents one from initiating + effect eTest = GetFirstEffect(oInvoker); + int nEType; + while(GetIsEffectValid(eTest)) + { + nEType = GetEffectType(eTest); + if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE || + nEType == EFFECT_TYPE_DAZED || + nEType == EFFECT_TYPE_PARALYZE || + nEType == EFFECT_TYPE_PETRIFY || + nEType == EFFECT_TYPE_SLEEP || + nEType == EFFECT_TYPE_STUNNED + ) + return FALSE; + + // Get next effect + eTest = GetNextEffect(oInvoker); + } + + return TRUE; +}*/ + +/** Internal function. + * Runs while the given creature is initiating. If they move, take other actions + * that would cause them to interrupt initiating the invocation or are affected by an + * effect that would cause such interruption, deletes the invocation token. + * Stops if such condition occurs or something else destroys the token. + * + * @param oInvoker A creature initiating a invocation + * @param lInvoker The location where the invoker was when starting the invocation + * @param oInvokeToken The invocation token that controls the ongoing invocation + */ +/*void _InvocationHB(object oInvoker, location lInvoker, object oInvokeToken) +{ + if(DEBUG) DoDebug("_InvocationHB() running:\n" + + "oInvoker = " + DebugObject2Str(oInvoker) + "\n" + + "lInvoker = " + DebugLocation2Str(lInvoker) + "\n" + + "oInvokeToken = " + DebugObject2Str(oInvokeToken) + "\n" + + "Distance between invocation start location and current location: " + FloatToString(GetDistanceBetweenLocations(lInvoker, GetLocation(oInvoker))) + "\n" + ); + if(GetIsObjectValid(oInvokeToken)) + { + // Continuance check + if(GetDistanceBetweenLocations(lInvoker, GetLocation(oInvoker)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting + !_InvocationStateCheck(oInvoker) // Action and effect check + ) + { + if(DEBUG) DoDebug("_InvocationHB(): invoker moved or lost concentration, destroying token"); + _DestroyInvocationToken(oInvoker, oInvokeToken); + + // Inform invoker + FloatingTextStrRefOnCreature(16832980, oInvoker, FALSE); // "You have lost concentration on the invocation you were attempting to cast!" + } + // Schedule next HB + else + DelayCommand(PRC_INVOCATION_HB_DELAY, _InvocationHB(oInvoker, lInvoker, oInvokeToken)); + } +}*/ + +/** Internal function. + * Checks if the invoker is in range to use the invocation they are trying to use. + * If not, queues commands to make the invoker to run into range. + * + * @param oInvoker A creature initiating a invocation + * @param nInvocation SpellID of the invocation being initiated + * @param lTarget The target location or the location of the target object + */ +/*void _InvocationRangeCheck(object oInvoker, int nInvocation, location lTarget) +{ + float fDistance = GetDistanceBetweenLocations(GetLocation(oInvoker), lTarget); + float fRangeLimit; + string sRange = Get2DACache("spells", "Range", nInvocation); + + // Personal range invocations are always in range + if(sRange == "P") + return; + // Ranges according to the CCG spells.2da page + else if(sRange == "T") + fRangeLimit = 2.25f; + else if(sRange == "S") + fRangeLimit = 8.0f; + else if(sRange == "M") + fRangeLimit = 20.0f; + else if(sRange == "L") + fRangeLimit = 40.0f; + + // See if we are out of range + if(fDistance > fRangeLimit) + { + // Create waypoint for the movement + object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget); + + // Move into range, with a bit of fudge-factor + //ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f); + + // CleanUp + ActionDoCommand(DestroyObject(oWP)); + + // CleanUp, paranoia + AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f))); + } +}*/ + +/** Internal function. + * Assigns the fakecast command that is used to display the conjuration VFX when using an invocation. + * Separated from UseInvocation() due to a bug with ActionFakeCastSpellAtObject(), which requires + * use of ClearAllActions() to work around. + * The problem is that if the target is an item on the ground, if the actor is out of spell + * range when doing the fakecast, they will run on top of the item instead of to the edge of + * the spell range. This only happens if there was a "real action" in the actor's action queue + * immediately prior to the fakecast. + */ +/*void _AssignUseInvocationFakeCastCommands(object oInvoker, object oTarget, location lTarget, int nSpellID) +{ + // Nuke actions to prevent the fakecast action from bugging + ClearAllActions(); + + if(GetIsObjectValid(oTarget)) + ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT); + else + ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT); +}*/ + + +/** Internal function. + * Places the cheatcasting of the real invocation into the invoker's action queue. + */ +/*void _UseInvocationAux(object oInvoker, object oInvokeToken, int nSpellId, + object oTarget, location lTarget, + int nInvocation, int nClass, int nLevelOverride) +{ + if(DEBUG) DoDebug("_UseInvocationAux() running:\n" + + "oInvoker = " + DebugObject2Str(oInvoker) + "\n" + + "oInvokeToken = " + DebugObject2Str(oInvokeToken) + "\n" + + "nSpellId = " + IntToString(nSpellId) + "\n" + + "oTarget = " + DebugObject2Str(oTarget) + "\n" + + "lTarget = " + DebugLocation2Str(lTarget) + "\n" + + "nInvocation = " + IntToString(nInvocation) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + ); + + // Make sure nothing has interrupted this invocation + if(GetIsObjectValid(oInvokeToken)) + { + if(DEBUG) DoDebug("_UseInvocationAux(): Token was valid, queueing actual invocation"); + // Set the class to cast as + SetLocalInt(oInvoker, PRC_INVOKING_CLASS, nClass + 1); + + // Set the invocation's level + SetLocalInt(oInvoker, PRC_INVOCATION_LEVEL, StringToInt(lookup_spell_innate(nSpellId))); + + if(nLevelOverride != 0) + AssignCommand(oInvoker, ActionDoCommand(SetLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride))); + if(GetIsObjectValid(oTarget)) + AssignCommand(oInvoker, ActionCastSpellAtObject(nInvocation, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + else + AssignCommand(oInvoker, ActionCastSpellAtLocation(nInvocation, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + if(nLevelOverride != 0) + AssignCommand(oInvoker, ActionDoCommand(DeleteLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE))); + + // Destroy the invocation token for this invocation + _DestroyInvocationToken(oInvoker, oInvokeToken); + } +}*/ + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct invocation EvaluateInvocation(object oInvoker, object oTarget) +{ + /* Get some data */ + int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // invoker-related stuff + int nInvokerLevel = GetInvokerLevel(oInvoker); + int nInvocationLevel = GetInvocationLevel(oInvoker); + int nClass = GetInvokingClass(oInvoker); + + /* Initialise the invocation structure */ + struct invocation invoked; + invoked.oInvoker = oInvoker; + invoked.bCanInvoke = TRUE; // Assume successfull invocation by default + invoked.nInvokerLevel = nInvokerLevel; + invoked.nInvocationId = PRCGetSpellId(); + + if (DEBUG) FloatingTextStringOnCreature(GetName(oInvoker)+" is a "+IntToString(nClass)+" casting invocation "+IntToString(invoked.nInvocationId)+", a "+IntToString(nInvocationLevel)+" level invocation, at "+IntToString(nInvokerLevel)+" invoker level", oInvoker); + + // Skip doing anything if something has prevented a successful invocation already by this point + //if(invoked.bCanInvoke) + //{ + invoked = _DoInvocationSpellfireFriendlyAbsorption(invoked, oTarget); + //}//end if + + if(DEBUG) DoDebug("EvaluateInvocation(): Final result:\n" + DebugInvocation2Str(invoked)); + + // Initiate invocation-related variable CleanUp + //DelayCommand(0.5f, _CleanInvocationVariables(oInvoker)); + + return invoked; +} + +void UseInvocation(int nInvocation, int nClass, int nLevelOverride = 0, int bInstant = FALSE) +{ + if(nClass < 0) + nClass = CLASS_TYPE_WARLOCK; + object oInvoker = OBJECT_SELF; +// object oSkin = GetPCSkin(oInvoker); +// object oTarget = PRCGetSpellTargetObject(); +// object oInvokeToken; +// location lTarget = PRCGetSpellTargetLocation(); +// int nSpellID = PRCGetSpellId(); + //int nInvocationDur = StringToInt(Get2DACache("spells", "ConjTime", nInvocation)) + StringToInt(Get2DACache("spells", "CastTime", nInvocation)); + // This is a test case to speed up the impact of the melee attacks, as PerformAttackRound takes the full 6 second. +// int nInvocationDur = 0; + + + // Setup invocation-related variables + ActionDoCommand(_SetInvocationVariables(oInvoker, nClass, StringToInt(lookup_spell_innate(nInvocation)))); + + // Cast the actual invocation + ActionCastSpell(nInvocation, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstant); + + // Initiate invocation-related variable CleanUp + ActionDoCommand(_CleanInvocationVariables(oInvoker)); + // Normally swift action invocations check + /*if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nInvocation, nClass)) == "SWIFT_ACTION" && // The invocation is swift action to use + TakeSwiftAction(oInvoker) // And the invoker can take a swift action now + ) + { + nInvocationDur = 0; + }*/ + + /*if(DEBUG) DoDebug("UseInvocation(): invoker is " + DebugObject2Str(oInvoker) + "\n" + + "nInvocation = " + IntToString(nInvocation) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + + "invocation duration = " + IntToString(nInvocationDur) + "ms \n" + //+ "Token exists = " + DebugBool2String(GetIsObjectValid(oInvokeToken)) + );*/ + + // Create the invocation token. Deletes any old tokens and cancels corresponding invocations as a side effect + //oInvokeToken = _CreateInvocationToken(oInvoker); + + /// @todo Hook to the invoker's OnDamaged event for the concentration checks to avoid losing the invocation + + // Nuke action queue to prevent cheating with creative invocation stacking. + // Probably not necessary anymore - Ornedan + //if(DEBUG) SendMessageToPC(oInvoker, "Clearing all actions in preparation for second stage of the invocation."); + //ClearAllActions(); + + // If out of range, move to range + //_InvocationRangeCheck(oInvoker, nInvocation, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget); + + // Start the invocation monitor HB + //DelayCommand(IntToFloat(nInvocationDur), ActionDoCommand(_InvocationHB(oInvoker, GetLocation(oInvoker), oInvokeToken))); + + // Assuming the spell isn't used as a swift action, fakecast for visuals + /*if(nInvocationDur > 0) + { + // Hack. Workaround of a bug with the fakecast actions. See function comment for details + ActionDoCommand(_AssignUseInvocationFakeCastCommands(oInvoker, oTarget, lTarget, nSpellID)); + }*/ + + // Action queue the function that will cheatcast the actual invocation + //DelayCommand(IntToFloat(nInvocationDur), AssignCommand(oInvoker, ActionDoCommand(_UseInvocationAux(oInvoker, oInvokeToken, nSpellID, oTarget, lTarget, nInvocation, nClass, nLevelOverride)))); +} + +string DebugInvocation2Str(struct invocation invoked) +{ + string sRet; + + sRet += "oInvoker = " + DebugObject2Str(invoked.oInvoker) + "\n"; + sRet += "bCanInvoke = " + DebugBool2String(invoked.bCanInvoke) + "\n"; + sRet += "nInvokerLevel = " + IntToString(invoked.nInvokerLevel); + + return sRet; +} + +void SetLocalInvocation(object oObject, string sName, struct invocation invoked) +{ + //SetLocal (oObject, sName + "_", ); + SetLocalObject(oObject, sName + "_oInvoker", invoked.oInvoker); + + SetLocalInt(oObject, sName + "_bCanInvoke", invoked.bCanInvoke); + SetLocalInt(oObject, sName + "_nInvokerLevel", invoked.nInvokerLevel); + SetLocalInt(oObject, sName + "_nSpellID", invoked.nInvocationId); +} + +struct invocation GetLocalInvocation(object oObject, string sName) +{ + struct invocation invoked; + invoked.oInvoker = GetLocalObject(oObject, sName + "_oInvoker"); + + invoked.bCanInvoke = GetLocalInt(oObject, sName + "_bCanInvoke"); + invoked.nInvokerLevel = GetLocalInt(oObject, sName + "_nInvokerLevel"); + invoked.nInvocationId = GetLocalInt(oObject, sName + "_nSpellID"); + + return invoked; +} + +void DeleteLocalInvocation(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oInvoker"); + + DeleteLocalInt(oObject, sName + "_bCanInvoke"); + DeleteLocalInt(oObject, sName + "_nInvokerLevel"); + DeleteLocalInt(oObject, sName + "_nSpellID"); +} + +void InvocationDebugIgnoreConstraints(object oInvoker) +{ + SetLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS, TRUE); + DelayCommand(0.0f, DeleteLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS)); +} + +// Test main +//void main(){} diff --git a/src/include/inv_invoc_const.nss b/src/include/inv_invoc_const.nss new file mode 100644 index 0000000..539a31c --- /dev/null +++ b/src/include/inv_invoc_const.nss @@ -0,0 +1,258 @@ +// Real Invocation SpellId Constants +/* + +*/ + +// Least Draconic Invocations +const int INVOKE_BEGUILING_INFLUENCE = 18001; +const int INVOKE_BREATH_OF_THE_NIGHT = 18002; +const int INVOKE_DARKNESS = 18003; +const int INVOKE_DEAFENING_ROAR = 18004; +const int INVOKE_DRACONIC_KNOWLEDGE = 18005; +const int INVOKE_ENDURE_EXPOSURE = 18006; +const int INVOKE_MAGIC_INSIGHT = 18007; +const int INVOKE_SCALDING_GUST = 18008; +const int INVOKE_SEE_THE_UNSEEN = 18009; + +// Lesser Draconic Invocations +const int INVOKE_CHARM = 18010; +const int INVOKE_ENERGY_RESISTANCE = 18011; +const int INVOKE_ENERGY_RESISTANCE_ACID = 18012; +const int INVOKE_ENERGY_RESISTANCE_COLD = 18013; +const int INVOKE_ENERGY_RESISTANCE_ELEC = 18014; +const int INVOKE_ENERGY_RESISTANCE_FIRE = 18015; +const int INVOKE_ENERGY_RESISTANCE_SONIC = 18016; +const int INVOKE_FRIGHTFUL_PRESENCE = 18017; +const int INVOKE_HUMANOID_SHAPE = 18018; +const int INVOKE_HUMANOID_SHAPE_LEARN = 18019; +const int INVOKE_HUMANOID_SHAPE_OPTION = 18020; +const int INVOKE_HUMANOID_SHAPE_TRUE = 18021; +const int INVOKE_HUMANOID_SHAPE_QS1 = 18022; +const int INVOKE_HUMANOID_SHAPE_QS2 = 18023; +const int INVOKE_VOIDSENSE = 18024; +const int INVOKE_VORACIOUS_DISPELLING = 18025; +const int INVOKE_WALK_UNSEEN = 18026; + +// Greater Draconic Invocations +const int INVOKE_AURA_OF_FLAME = 18027; +const int INVOKE_CHILLING_FOG = 18028; +const int INVOKE_DEVOUR_MAGIC = 18029; +const int INVOKE_DRACONIC_TOUGHNESS = 18030; +const int INVOKE_TERRIFYING_ROAR = 18031; + +// Dark Draconic Invocations +const int INVOKE_ENERGY_IMMUNITY = 18032; +const int INVOKE_ENERGY_IMMUNITY_ACID = 18033; +const int INVOKE_ENERGY_IMMUNITY_COLD = 18034; +const int INVOKE_ENERGY_IMMUNITY_ELEC = 18035; +const int INVOKE_ENERGY_IMMUNITY_FIRE = 18036; +const int INVOKE_ENERGY_IMMUNITY_SONIC = 18037; +const int INVOKE_INSTILL_VULNERABILITY = 18038; +const int INVOKE_INSTILL_VULNERABIL_ACID = 18039; +const int INVOKE_INSTILL_VULNERABIL_COLD = 18040; +const int INVOKE_INSTILL_VULNERABIL_ELEC = 18041; +const int INVOKE_INSTILL_VULNERABIL_FIRE = 18042; +const int INVOKE_INSTILL_VULNERABIL_SON = 18043; + +//Least Warlock Invocations +const int INVOKE_ALL_SEEING_EYES = 18045; +const int INVOKE_BALEFUL_UTTERANCE = 18046; +const int INVOKE_CALL_OF_THE_BEAST = 18047; +const int INVOKE_COCOON_OF_REFUSE = 18048; +const int INVOKE_DARK_ONES_OWN_LUCK = 18049; +const int INVOKE_DARK_ONES_OWN_LUCK_FORT = 18050; +const int INVOKE_DARK_ONES_OWN_LUCK_REFLEX = 18051; +const int INVOKE_DARK_ONES_OWN_LUCK_WILL = 18052; +const int INVOKE_DEVILS_SIGHT = 18053; +const int INVOKE_DRAIN_INCARNUM = -1; +const int INVOKE_EARTHEN_GRASP = 18055; +const int INVOKE_ELDRITCH_GLAIVE = 18056; +const int INVOKE_ELDRITCH_SPEAR = 18058; +const int INVOKE_ENTROPIC_WARDING = 18059; +const int INVOKE_FRIGHTFUL_BLAST = 18060; +const int INVOKE_HAMMER_BLAST = 18061; +const int INVOKE_HIDEOUS_BLOW = 18062; +const int INVOKE_LEAPS_AND_BOUNDS = 18063; +const int INVOKE_MIASMIC_CLOUD = 18064; +const int INVOKE_OTHERWORLDLY_WHISPERS = 18065; +const int INVOKE_SERPENTS_TONGUE = 18066; +const int INVOKE_SICKENING_BLAST = 18067; +const int INVOKE_SOULREAVING_AURA = 18068; +const int INVOKE_SUMMON_SWARM = 18069; +const int INVOKE_SUMMON_SWARM_RAT = 18070; +const int INVOKE_SUMMON_SWARM_BAT = 18071; +const int INVOKE_SWIMMING_THE_STYX = 18072; + +//Lesser Warlock Invocations +const int INVOKE_BANEFUL_BLAST_ABERRATION = 18073; +const int INVOKE_BANEFUL_BLAST_BEAST = 18074; +const int INVOKE_BANEFUL_BLAST_CONSTRUCT = 18075; +const int INVOKE_BANEFUL_BLAST_DRAGON = 18076; +const int INVOKE_BANEFUL_BLAST_DWARF = 18077; +const int INVOKE_BANEFUL_BLAST_ELEMENTAL = 18078; +const int INVOKE_BANEFUL_BLAST_ELF = 18079; +const int INVOKE_BANEFUL_BLAST_FEY = 18080; +const int INVOKE_BANEFUL_BLAST_GIANT = 18081; +const int INVOKE_BANEFUL_BLAST_GOBLINOID = 18082; +const int INVOKE_BANEFUL_BLAST_GNOME = 18083; +const int INVOKE_BANEFUL_BLAST_HALFLING = 18084; +const int INVOKE_BANEFUL_BLAST_HUMAN = 18085; +const int INVOKE_BANEFUL_BLAST_MONSTROUS = 18086; +const int INVOKE_BANEFUL_BLAST_ORC = 18087; +const int INVOKE_BANEFUL_BLAST_OUTSIDER = 18088; +const int INVOKE_BANEFUL_BLAST_PLANT = 18089; +const int INVOKE_BANEFUL_BLAST_REPTILIAN = 18090; +const int INVOKE_BANEFUL_BLAST_SHAPECHANGER = 18091; +const int INVOKE_BANEFUL_BLAST_UNDEAD = 18092; +const int INVOKE_BANEFUL_BLAST_VERMIN = 18093; +const int INVOKE_BESHADOWED_BLAST = 18094; +const int INVOKE_BRIMSTONE_BLAST = 18095; +const int INVOKE_COLD_COMFORT = 18096; +const int INVOKE_CURSE_OF_DESPAIR = 18097; +const int INVOKE_DREAD_SEIZURE = 18098; +const int INVOKE_ELDRITCH_CHAIN = 18099; +const int INVOKE_FLEE_THE_SCENE = 18100; +const int INVOKE_FLEE_THE_SCENE_SELECT = 18101; +const int INVOKE_FLEE_THE_SCENE_DIRDIST = 18102; +const int INVOKE_HELLRIME_BLAST = 18103; +const int INVOKE_HUNGRY_DARKNESS = 18104; +const int INVOKE_IGNORE_THE_PYRE = 18105; +const int INVOKE_IGNORE_THE_PYRE_ACID = 18106; +const int INVOKE_IGNORE_THE_PYRE_COLD = 18107; +const int INVOKE_IGNORE_THE_PYRE_ELEC = 18108; +const int INVOKE_IGNORE_THE_PYRE_FIRE = 18109; +const int INVOKE_IGNORE_THE_PYRE_SONIC = 18110; +const int INVOKE_MASK_OF_FLESH = 18111; +const int INVOKE_MASK_OF_FLESH_FRIENDLY = 18112; +const int INVOKE_MASK_OF_FLESH_HOSTILE = 18113; +const int INVOKE_RELENTLESS_DISPELLING = 18114; +const int INVOKE_SPIDER_SHAPE = 18115; +const int INVOKE_STEAL_INCARNUM = -1; +const int INVOKE_STONY_GRASP = 18117; +const int INVOKE_THE_DEAD_WALK = 18118; +const int INVOKE_WALL_OF_GLOOM = 18119; +const int INVOKE_WITCHWOOD_STEP = 18120; + +//Greater Warlock Invocations +const int INVOKE_BEWITCHING_BLAST = 18121; +const int INVOKE_CAUSTIC_MIRE = 18122; +const int INVOKE_CHILLING_TENTACLES = 18123; +const int INVOKE_DRAGON_WARD = 18124; +const int INVOKE_ELDRITCH_CONE = 18125; +const int INVOKE_ELDRITCH_LINE = 18126; +const int INVOKE_ENERVATING_SHADOW = 18127; +const int INVOKE_HELLSPAWNED_GRACE = 18128; +const int INVOKE_HINDERING_BLAST = 18129; +const int INVOKE_INCARNUM_BLAST = -1; +const int INVOKE_NIGHTMARES_MADE_REAL = 18131; +const int INVOKE_NOXIOUS_BLAST = 18132; +const int INVOKE_PAINFUL_SLUMBER_OF_AGES = 18133; +const int INVOKE_PENETRATING_BLAST = 18134; +const int INVOKE_TENACIOUS_PLAGUE = 18135; +const int INVOKE_VITRIOLIC_BLAST = 18136; +const int INVOKE_WALL_OF_PERILOUS_FLAME = 18137; + +//Dark Warlock Invocations +const int INVOKE_BINDING_BLAST = 18138; +const int INVOKE_CASTERS_LAMENT = 18139; +const int INVOKE_DARK_DISCORPORATION = 18140; +const int INVOKE_DARK_FORESIGHT = 18141; +const int INVOKE_ELDRITCH_DOOM = 18142; +const int INVOKE_INCARNUM_SHROUD = -1; +const int INVOKE_PATH_OF_SHADOW = 18144; +const int INVOKE_PATH_OF_SHADOW_SELF = 18145; +const int INVOKE_PATH_OF_SHADOW_PARTY = 18146; +const int INVOKE_RETRIBUTIVE_INVISIBILITY = 18147; +const int INVOKE_STEAL_SUMMONING = 18148; +const int INVOKE_UTTERDARK_BLAST = 18149; +const int INVOKE_WORD_OF_CHANGING = 18150; + +//AOEs +const int INVOKE_AOE_CHILLFOG = 224; +const int INVOKE_AOE_BREATH_OF_NIGHT = 142; +const int INVOKE_AOE_COLD_COMFORT = 226; +const int INVOKE_VFX_CHILLING_TENTACLES = 227; +const int INVOKE_VFX_PER_WALLPERILFIRE = 228; +const int INVOKE_AOE_MIASMIC_CLOUD = 229; +const int INVOKE_AOE_CAUSTIC_MIRE = 230; +const int INVOKE_AOE_ENERVATING_SHADOW = 231; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_1 = 232; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_4 = 233; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_7 = 234; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_11 = 235; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_15 = 236; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_21 = 237; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_25 = 238; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_31 = 239; +const int INVOKE_VFX_NIGHTMARE_TERRAIN_37 = 240; +const int INVOKE_AOE_SWARMDMG = 241; +const int INVOKE_VFX_HUNGRY_DARKNESS = 242; +const int INVOKE_AOE_SWARMDMG_2 = 243; +const int INVOKE_AOE_SWARMDMG_3 = 244; +const int INVOKE_AOE_SWARMDMG_4 = 245; +const int INVOKE_AOE_SWARMDMG_5 = 246; +const int INVOKE_AOE_SWARMDMG_6 = 247; +const int INVOKE_VFX_DARK_DISCORPORATION = 248; +const int INVOKE_VFX_PER_WALL_OF_GLOOM = 249; +const int INVOKE_AOE_EARTHEN_GRASP_GRAPPLE = 250; + +//class abilities +const int INVOKE_ELDRITCH_BLAST = 2083; +const int INVOKE_IMBUE_ITEM = 2082; +const int INVOKE_FIENDISH_RESILIENCE = 2084; + +//epic warlock feats +const int INVOKE_LORD_OF_ALL_ESSENCES = 3758; +const int INVOKE_MASTER_OF_ELEMENTS_AIR = 3753; +const int INVOKE_MASTER_OF_ELEMENTS_EARTH = 3754; +const int INVOKE_MASTER_OF_ELEMENTS_FIRE = 3755; +const int INVOKE_MASTER_OF_ELEMENTS_WATER = 3756; +const int INVOKE_MASTER_OF_ELEMENTS_DOMINATE = 3757; +const int INVOKE_MORPHEME_SAVANT_WORD_KILL = 3750; +const int INVOKE_MORPHEME_SAVANT_WORD_STUN = 3751; +const int INVOKE_SHADOWMASTER_CONCEALMENT = 3743; +const int INVOKE_SHADOWMASTER_SUMMON_SHADOW = 3745; +const int INVOKE_SHADOWMASTER_CONE_OF_COLD = 3746; +const int INVOKE_SHADOWMASTER_FIREBALL = 3747; +const int INVOKE_SHADOWMASTER_STONESKIN = 3748; +const int INVOKE_SHADOWMASTER_WALL_OF_FIRE = 3749; + +//other invocation-related abilities +const int INVOKE_ELDRITCH_GLAIVE_ONHIT = 205; + +//Hellfire Warlock +const int INVOKE_HELLFIRE_BLAST = 17282; +const int INVOKE_HELLFIRE_SPEAR = 17283; +const int INVOKE_HELLFIRE_GLAIVE = 17284; +const int INVOKE_HELLFIRE_BLOW = 17285; +const int INVOKE_HELLFIRE_CHAIN = 17286; +const int INVOKE_HELLFIRE_CONE = 17287; +const int INVOKE_HELLFIRE_LINE = 17288; +const int INVOKE_HELLFIRE_DOOM = 17289; +const int INVOKE_HF_INFUSION_EXTEND = 17291; +const int INVOKE_HF_INFUSION_EMPOWER = 17292; +const int INVOKE_HF_INFUSION_WIDEN = 17293; +const int INVOKE_HF_INFUSION_MAXIMIZE = 17294; +const int INVOKE_HELLFIRE_SHIELD = 17295; + +//Eldritch Disciple +const int INVOKE_CORRUPTING_BLAST = 17270; +const int INVOKE_DAMAGE_REDUCTION = 17271; +const int INVOKE_FEARFUL_GLARE = 17272; +const int INVOKE_FIENDISH_RESISTANCE = 17273; +const int INVOKE_HEALING_BLAST = 17274; +const int INVOKE_PROTECTIVE_AURA = 17275; +const int INVOKE_STRENGTH_OF_WILL = 17276; +const int INVOKE_WILD_FRENZY = 17277; + +//Eldritch Theurge +const int INVOKE_GR_SPELL_SELECT_CONVO = 17259; +const int INVOKE_GR_SPELL_SELECT_QUICK1 = 17260; +const int INVOKE_GR_SPELL_SELECT_QUICK2 = 17261; +const int INVOKE_GR_SPELL_SELECT_QUICK3 = 17262; +const int INVOKE_GR_SPELL_SELECT_QUICK4 = 17263; +const int INVOKE_SB_SPELL_SELECT_CONVO = 17265; +const int INVOKE_SB_SPELL_SELECT_QUICK1 = 17266; +const int INVOKE_SB_SPELL_SELECT_QUICK2 = 17267; +const int INVOKE_SB_SPELL_SELECT_QUICK3 = 17268; +const int INVOKE_SB_SPELL_SELECT_QUICK4 = 17269; diff --git a/src/include/inv_invokehook.nss b/src/include/inv_invokehook.nss new file mode 100644 index 0000000..92b0f1f --- /dev/null +++ b/src/include/inv_invokehook.nss @@ -0,0 +1,170 @@ +//:://///////////////////////////////////////////// +//:: Invocation Hook File. +//:: inv_invokehook.nss +//::////////////////////////////////////////////// +/* + + This file acts as a hub for all code that + is hooked into the invocation scripts + +*/ +//::////////////////////////////////////////////// +//:: Created By: Fox +//:: Created On: 25-1-2008 +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "inv_inc_invfunc" +#include "x2_inc_spellhook" + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int PreInvocationCastCode(); + +// All invocations have somatic component so we will roll ASF check here +int InvocationASFCheck(object oInvoker, int nClass) +{ + int nASF = GetArcaneSpellFailure(oInvoker); + + // Warlocks ignore ASF chance while casting in light armor + if(nClass == CLASS_TYPE_WARLOCK) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oInvoker); + int nAC = GetBaseAC(oArmor); + + //armors + switch(nAC) + { + case 1: nASF -= 5; break;//light + case 2: nASF -= 10; break;//light + case 3: nASF -= 20; break;//light + case 4: nASF -= GetHasFeat(FEAT_BATTLE_CASTER, oInvoker) ? 20 : 0; break;//medium; + case 5: nASF -= GetHasFeat(FEAT_BATTLE_CASTER, oInvoker) ? 30 : 0; break;//medium + default: break; + } + } + else if(nClass == CLASS_TYPE_DRAGON_SHAMAN) + { + //no ASF chance + return TRUE; + } + + if(Random(100) < nASF) + { + //52946 = Spell failed due to arcane spell failure! + FloatingTextStrRefOnCreature(52946, oInvoker, FALSE); + return FALSE; + } + + return TRUE; +} + +//------------------------------------------------------------------------------ +// if FALSE is returned by this function, the spell will not be cast +// the order in which the functions are called here DOES MATTER, changing it +// WILL break the crafting subsystems +//------------------------------------------------------------------------------ +int PreInvocationCastCode() +{ + object oInvoker = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + object oCastItem = GetSpellCastItem(); + int nInvokeId = PRCGetSpellId(); + int nInvokingClass = GetInvokingClass(oInvoker); + int nInvokeLevel = GetInvocationLevel(oInvoker); + int bInvokeIsHostile = Get2DACache("spells", "HostileSetting", nInvokeId) == "1"; + + int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oInvoker); + + //--------------------------------------------------------------------------- + // Break any spell require maintaining concentration + //--------------------------------------------------------------------------- + X2BreakConcentrationSpells(); + + //--------------------------------------------------------------------------- + // No invoking while using expertise + //--------------------------------------------------------------------------- + if(nContinue) + if (GetActionMode(oInvoker, ACTION_MODE_EXPERTISE) || GetActionMode(oInvoker, ACTION_MODE_IMPROVED_EXPERTISE)) + nContinue = FALSE; + + //--------------------------------------------------------------------------- + // Check for PRC spell effects + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PRCSpellEffects(oInvoker, oTarget, nInvokeId, nInvokeLevel, nInvokingClass, bInvokeIsHostile, -1); + + //--------------------------------------------------------------------------- + // Run Grappling Concentration Check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = GrappleConc(oInvoker, nInvokeLevel); + + //--------------------------------------------------------------------------- + // Run ASF Check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = InvocationASFCheck(oInvoker, nInvokingClass); + + //--------------------------------------------------------------------------- + // This stuff is only interesting for player characters we assume that use + // magic device always works and NPCs don't use the crafting feats or + // sequencers anyway. Thus, any NON PC spellcaster always exits this script + // with TRUE (unless they are DM possessed or in the Wild Magic Area in + // Chapter 2 of Hordes of the Underdark. + //--------------------------------------------------------------------------- + if(!GetIsPC(oInvoker) + && !GetPRCSwitch(PRC_NPC_HAS_PC_SPELLCASTING)) + { + if(!GetIsDMPossessed(oInvoker) && !GetLocalInt(GetArea(oInvoker), "X2_L_WILD_MAGIC")) + { + return TRUE; + } + } + + //--------------------------------------------------------------------------- + // Run use magic device skill check + //--------------------------------------------------------------------------- + if(nContinue) + { + nContinue = X2UseMagicDeviceCheck(oInvoker); + } + + //----------------------------------------------------------------------- + // run any user defined spellscript here + //----------------------------------------------------------------------- + if (nContinue) + { + nContinue = X2RunUserDefinedSpellScript(); + } + + //--------------------------------------------------------------------------- + // Check for the new restricted itemproperties + //--------------------------------------------------------------------------- + if(nContinue + && GetIsObjectValid(oCastItem) + && !CheckPRCLimitations(oCastItem, oInvoker)) + { + SendMessageToPC(oInvoker, "You cannot use "+GetName(oCastItem)); + nContinue = FALSE; + } + + //Cleaning spell variables used for holding the charge + if(!GetLocalInt(oInvoker, "PRC_SPELL_EVENT")) + { + DeleteLocalInt(oInvoker, "PRC_SPELL_CHARGE_COUNT"); + DeleteLocalInt(oInvoker, "PRC_SPELL_CHARGE_SPELLID"); + DeleteLocalObject(oInvoker, "PRC_SPELL_CONC_TARGET"); + DeleteLocalInt(oInvoker, "PRC_SPELL_METAMAGIC"); + DeleteLocalManifestation(oInvoker, "PRC_POWER_HOLD_MANIFESTATION"); + DeleteLocalMystery(oInvoker, "MYST_HOLD_MYST"); + } + else if(GetLocalInt(oInvoker, "PRC_SPELL_CHARGE_SPELLID") != nInvokeId) + { //Sanity check, in case something goes wrong with the action queue + DeleteLocalInt(oInvoker, "PRC_SPELL_EVENT"); + } + + return nContinue; +} + diff --git a/src/include/lookup_2da_spell.nss b/src/include/lookup_2da_spell.nss new file mode 100644 index 0000000..20561ab --- /dev/null +++ b/src/include/lookup_2da_spell.nss @@ -0,0 +1,123 @@ +/* +** Spell lookup&caching code by DarkGod +** Because the engine sucks so badly ... +*/ +#include "inc_2dacache" + +//const string PRC_CACHE_SUB_STRING = "NULL"; + +string +lookup_and_cache_spell_field(int spell_id, string tag_base, string column, object oModule = OBJECT_INVALID) +{ +//modifed by Primogenitor to a more general 2da caching system +/* + // Verify the module + if (!GetIsObjectValid(oModule)) + oModule = GetModule(); + + // Create the tag string + string tag = tag_base + IntToString(spell_id); + + // lookup the tag in cache + string val = GetLocalString(oModule, tag); + + // lookup and fill the cache if required + if (val == "") { + val = Get2DACache("spells", column, spell_id); + + // Get2DAString() will return "" for invalid fields + // In order to make our per field cache work, we need to + // perform a substitution so that non-existant and invalid + // values are different. Verify that this constant is indeed + // unique within the 2da file if this code is reused + if (val == "") + val = PRC_CACHE_SUB_STRING; + + SetLocalString(oModule, tag, val); + } + + // Undo the substitution, see above comments for details + if (val == PRC_CACHE_SUB_STRING) + val = ""; + + return val; +*/ + return Get2DACache("spells", column, spell_id); +} + +string +lookup_spell_name(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_NAME_", "Name", oModule); +} + +string +lookup_spell_level(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_LEVEL_", "Wiz_Sorc", oModule); +} + +string +lookup_spell_innate(int spell_id, object oModule = OBJECT_INVALID) +{ + string sTemp = lookup_and_cache_spell_field(spell_id, "PRC_PACK_SPELL_INNATE_LEVEL_", "Innate", oModule); + if(sTemp == "") + { + string sMaster = Get2DACache("spells", "Master", spell_id); + if(sMaster != "") + { + sTemp = Get2DACache("spells", "Innate", StringToInt(sMaster)); + } + } + return sTemp; +} + +string +lookup_spell_druid_level(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_DRUID_LEVEL_", "Druid", oModule); +} + +string +lookup_spell_cleric_level(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_CLERIC_LEVEL_", "Cleric", oModule); +} + +string +lookup_spell_type(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_TYPE_", "ImmunityType", oModule); +} + +string +lookup_spell_vs(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_VS_", "VS", oModule); +} + +string +lookup_spell_school(int spell_id, object oModule = OBJECT_INVALID) +{ + return lookup_and_cache_spell_field(spell_id, + "PRC_PACK_SPELL_SCHOOL_", "School", oModule); +} + +void +lookup_spell(int spell_id) +{ + object module = GetModule(); + lookup_spell_level(spell_id, module); + lookup_spell_cleric_level(spell_id, module); + lookup_spell_innate(spell_id, module); + lookup_spell_cleric_level(spell_id, module); + lookup_spell_type(spell_id, module); + lookup_spell_vs(spell_id, module); + lookup_spell_school(spell_id, module); +} diff --git a/src/include/moi_inc_moifunc.nss b/src/include/moi_inc_moifunc.nss new file mode 100644 index 0000000..d48a441 --- /dev/null +++ b/src/include/moi_inc_moifunc.nss @@ -0,0 +1,1490 @@ +//:://///////////////////////////////////////////// +//:: Meldshaping/Incarnum main include: Miscellaneous +//:: moi_inc_moifunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Meldshaping. + + Also acts as inclusion nexus for the general + meldshaping includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2019.12.28 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the given creature's Meldshaper level. + * + * @param oMeldshaper The creature whose Meldshaper level to determine + * @param nSpecificClass The class to determine the creature's Meldshaper + * level in. + * @param nMeld The meld to test, since Incarnum does level by meld + * + * @return The Meldshaper level + */ +int GetMeldshaperLevel(object oMeldshaper, int nSpecificClass, int nMeld); + +/** + * Determines the given creature's highest unmodified Meldshaper level among its + * Meldshaping classes. + * + * @param oMeldshaper Creature whose highest Meldshaper level to determine + * @return The highest unmodified Meldshaper level the creature can have + */ +int GetHighestMeldshaperLevel(object oMeldshaper); + +/** + * Determines whether a given class is a Incarnum-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a Incarnum-related class, FALSE otherwise + */ +int GetIsIncarnumClass(int nClass); + +/** + * Calculates how many Meldshaper levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oMeldshaper Creature to calculate added Meldshaper levels for + * @return The number of Meldshaper levels gained + */ +int GetIncarnumPRCLevels(object oMeldshaper); + +/** + * Determines which of the character's classes is their highest or first + * Meldshaping class, if any. This is the one which gains Meldshaper + * level raise benefits from prestige classes. + * + * @param oMeldshaper Creature whose classes to test + * @return CLASS_TYPE_* of the first Meldshaping class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryIncarnumClass(object oMeldshaper = OBJECT_SELF); + +/** + * Determines the position of a creature's first Meldshaping class, if any. + * + * @param oMeldshaper Creature whose classes to test + * @return The position of the first Meldshaping class {1, 2, 3} or 0 if + * the creature possesses no levels in Meldshaping classes. + */ +int GetFirstIncarnumClassPosition(object oMeldshaper = OBJECT_SELF); + +/** + * Checks every second to see if temporary essentia has been lost + * + * @param oMeldshaper The meldshaper + */ +void SpawnTempEssentiaChecker(object oMeldshaper); + +/** + * Returns total value of temporary essentia for the meldshaper + * + * @param oMeldshaper The meldshaper + * + * @return Total value of temporary essentia + */ +int GetTemporaryEssentia(object oMeldshaper); + +/** + * Essentia put into feats is locked away for 24 hours/until next rest + * + * @param oMeldshaper The meldshaper + * + * @return Total value of locked essentia + */ +int GetFeatLockedEssentia(object oMeldshaper); + +/** + * Total essentia a character has access to + * + * @param oMeldshaper The meldshaper + * + * @return Total value of essentia available + */ +int GetTotalEssentia(object oMeldshaper); + +/** + * Total essentia a character has access to, minus feat locked essentia + * + * @param oMeldshaper The meldshaper + * + * @return Total value of essentia available, minus feat locked essentia + */ +int GetTotalUsableEssentia(object oMeldshaper); + +/** + * Returns the slot associated to a given chakra + * + * @param nChakra Chakra constant + * + * @return Slot constant + */ +int ChakraToSlot(int nChakra); + +/** + * Returns the total binds the character can have for that class and level + * + * @param oMeldshaper The meldshaper + * @param nClass The class to check + * + * @return Slot constant + */ +int GetMaxBindCount(object oMeldshaper, int nClass); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_natweap" +#include "prc_inc_function" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetMeldshaperLevel(object oMeldshaper, int nSpecificClass, int nMeld) +{ + int nLevel; + + //if (DEBUG) DoDebug("GetMeldshaperLevel: "+GetName(oMeldshaper)+" is a "+IntToString(nSpecificClass)); + + if(GetIsIncarnumClass(nSpecificClass)) + { + // Meldshaper level is class level + prestige + nLevel = GetLevelByClass(nSpecificClass, oMeldshaper); + if(nLevel) + { + // Prevents people double-dipping prestige levels + if (nSpecificClass == GetPrimaryIncarnumClass(oMeldshaper)) + { + nLevel += GetIncarnumPRCLevels(oMeldshaper); + nLevel += GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper); // Necrocarnate is here because it doesn't add to anything other than meldshaper level + } + if (nSpecificClass == CLASS_TYPE_SOULBORN) nLevel /= 2; + } + } + + //if(DEBUG) DoDebug("Meldshaper Level: " + IntToString(nLevel)); + // Being bound to the Totem Chakra increases the level by one. + if (GetIsMeldBound(oMeldshaper, nMeld) == CHAKRA_TOTEM) nLevel++; + return nLevel; +} + +int GetIncarnumLevelForClass(int nSpecificClass, object oMeldshaper) +{ + int nLevel; + + //if (DEBUG) DoDebug("GetMeldshaperLevel: "+GetName(oMeldshaper)+" is a "+IntToString(nSpecificClass)); + + if(GetIsIncarnumClass(nSpecificClass)) + { + // Meldshaper level is class level + prestige + nLevel = GetLevelByClass(nSpecificClass, oMeldshaper); + if(nLevel) + { + // Prevents people double-dipping prestige levels + if (nSpecificClass == GetPrimaryIncarnumClass(oMeldshaper)) nLevel += GetIncarnumPRCLevels(oMeldshaper); + } + } + if(nSpecificClass == CLASS_TYPE_UMBRAL_DISCIPLE || nSpecificClass == CLASS_TYPE_INCANDESCENT_CHAMPION || nSpecificClass == CLASS_TYPE_NECROCARNATE) + nLevel = GetLevelByClass(nSpecificClass, oMeldshaper); + + //if(DEBUG) DoDebug("GetIncarnumLevelForClass: " + IntToString(nLevel)); + return nLevel; +} + +int GetHighestMeldshaperLevel(object oMeldshaper) +{ + /**return PRCMax(PRCMax(GetClassByPosition(1, oMeldshaper) != CLASS_TYPE_INVALID ? GetMeldshaperLevel(oMeldshaper, GetClassByPosition(1, oMeldshaper), -1) : 0, + GetClassByPosition(2, oMeldshaper) != CLASS_TYPE_INVALID ? GetMeldshaperLevel(oMeldshaper, GetClassByPosition(2, oMeldshaper), -1) : 0 + ), + GetClassByPosition(3, oMeldshaper) != CLASS_TYPE_INVALID ? GetMeldshaperLevel(oMeldshaper, GetClassByPosition(3, oMeldshaper), -1) : 0 + );**/ + + int nMax; + int i; + for(i = 1; i <= 8; i++) + { + int nClass = GetClassByPosition(i, oMeldshaper); + int nTest = GetMeldshaperLevel(oMeldshaper, GetClassByPosition(i, oMeldshaper), -1); + if (nTest > nMax) + nMax = nTest; + } + return nMax; +} + +int GetIsIncarnumClass(int nClass) +{ + return nClass == CLASS_TYPE_INCARNATE + || nClass == CLASS_TYPE_SOULBORN + || nClass == CLASS_TYPE_TOTEMIST + || nClass == CLASS_TYPE_SPINEMELD_WARRIOR; +} + +int GetIncarnumPRCLevels(object oMeldshaper) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oMeldshaper); + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper); + + // These two don't add at 1st level + if (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper)) + nLevel += GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) - 1; + // Totem Rager + if (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >= 6) + nLevel += (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) -2; + else if (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) + nLevel += (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) -1; + //This is an odd one + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper)) + { + nLevel += (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper)+1)/2; + + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper) >= 10) nLevel += 1; + } + + /*if (GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oMeldshaper)) + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oMeldshaper) - 1; +*/ + return nLevel; +} + +int GetPrimaryIncarnumClass(object oMeldshaper = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nIncarnumPos = GetFirstIncarnumClassPosition(oMeldshaper); + if (!nIncarnumPos) return CLASS_TYPE_INVALID; // no Blade Magic Meldshaping class + + nClass = GetClassByPosition(nIncarnumPos, oMeldshaper); + } + else + { + int nClassTest, nClassLvlTest, nMax, i; + for(i = 1; i <= 8; i++) + { + int nClassTest = GetClassByPosition(i, oMeldshaper); + + if(GetIsIncarnumClass(nClassTest)) + nClassLvlTest = GetLevelByClass(nClassTest, oMeldshaper); + else + nClassLvlTest = 0; // Reset to 0 each iteration that isn't incarnum + + if (nClassLvlTest > nMax) + { + nMax = nClassLvlTest; + nClass = nClassTest; + } + } + + if(nMax == 0) //No Incarnum classes + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstIncarnumClassPosition(object oMeldshaper = OBJECT_SELF) +{ + int i; + for(i = 1; i <= 8; i++) + { + if (GetIsIncarnumClass(GetClassByPosition(i, oMeldshaper))) + return i; + } + + return 0; +} + +string GetMeldFile(int nClass = -1) +{ + //string sFile; + //if (nClass == CLASS_TYPE_INCARNATE) sFile = "cls_meld_incarn"; + + return "soulmelds"; +} + +string GetMeldshapingClassFile(int nClass) +{ + string sFile; + if (nClass == CLASS_TYPE_INCARNATE) sFile = "cls_mlkn_incarn"; + else if (nClass == CLASS_TYPE_SOULBORN) sFile = "cls_mlkn_soulbn"; + else if (nClass == CLASS_TYPE_TOTEMIST) sFile = "cls_mlkn_totem"; + else if (nClass == CLASS_TYPE_SPINEMELD_WARRIOR) sFile = "cls_mlkn_spnmld"; + else if (nClass == CLASS_TYPE_UMBRAL_DISCIPLE) sFile = "cls_mlkn_umbral"; + else if (nClass == CLASS_TYPE_INCANDESCENT_CHAMPION) sFile = "cls_mlkn_incand"; + else if (nClass == CLASS_TYPE_NECROCARNATE) sFile = "cls_mlkn_necrnm"; + + return sFile; +} + +int GetMeldshapingClass(object oMeldshaper) +{ + int nClass = -1; + // If there's levels in the class and haven't already done it + if (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) && !GetLocalInt(oMeldshaper, "FirstMeldDone")) nClass = CLASS_TYPE_INCARNATE; + else if (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) && !GetLocalInt(oMeldshaper, "SecondMeldDone")) nClass = CLASS_TYPE_SOULBORN; + else if (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) && !GetLocalInt(oMeldshaper, "ThirdMeldDone")) nClass = CLASS_TYPE_TOTEMIST; + else if (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper) && !GetLocalInt(oMeldshaper, "FourthMeldDone")) nClass = CLASS_TYPE_SPINEMELD_WARRIOR; + //if (DEBUG) DoDebug("GetMeldshapingClass is "+IntToString(nClass)); + return nClass; +} + +int GetMaxShapeSoulmeldCount(object oMeldshaper, int nClass) +{ + int nMax = StringToInt(Get2DACache(GetMeldshapingClassFile(nClass), "Soulmelds", GetIncarnumLevelForClass(nClass, oMeldshaper)-1)); + if (nClass == GetPrimaryIncarnumClass(oMeldshaper)) + { + nMax += StringToInt(Get2DACache(GetMeldshapingClassFile(CLASS_TYPE_NECROCARNATE), "Soulmelds", GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper))); + int i; + for(i = FEAT_BONUS_SOULMELD_1; i <= FEAT_BONUS_SOULMELD_10; i++) + if(GetHasFeat(i, oMeldshaper)) nMax++; + } + + int nCon = GetAbilityScore(oMeldshaper, ABILITY_CONSTITUTION, TRUE)-10; + if (GetHasFeat(FEAT_UNDEAD_MELDSHAPER, oMeldshaper)) nCon = GetAbilityScore(oMeldshaper, ABILITY_WISDOM, TRUE)-10; + //Limited to Con score - 10 or class limit, whichever is less + nMax = PRCMin(nMax, nCon); + + //if (DEBUG) DoDebug("GetMaxShapeSoulmeldCount is "+IntToString(nMax)); + return nMax; +} + +int GetTotalSoulmeldCount(object oMeldshaper) +{ + int nMax = GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_INCARNATE); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_SOULBORN); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_TOTEMIST); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_SPINEMELD_WARRIOR); + nMax += GetMaxShapeSoulmeldCount(oMeldshaper, CLASS_TYPE_NECROCARNATE); + + //if (DEBUG) DoDebug("GetTotalSoulmeldCount is "+IntToString(nMax)); + return nMax; +} + +int GetMaxBindCount(object oMeldshaper, int nClass) +{ + int nMax = StringToInt(Get2DACache(GetMeldshapingClassFile(nClass), "ChakraBinds", GetIncarnumLevelForClass(nClass, oMeldshaper)-1)); + if (nClass == GetPrimaryIncarnumClass(oMeldshaper)) + { + nMax += StringToInt(Get2DACache(GetMeldshapingClassFile(CLASS_TYPE_NECROCARNATE), "ChakraBinds", GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper))); + int i; + for(i = FEAT_EXTRA_CHAKRA_BIND_1; i <= FEAT_EXTRA_CHAKRA_BIND_10; i++) + if(GetHasFeat(i, oMeldshaper)) nMax += 1; + } + //if (DEBUG) DoDebug("GetMaxBindCount is "+IntToString(nMax)); + return nMax; +} + +void ShapeSoulmeld(object oMeldshaper, int nMeld) +{ + PRCRemoveSpellEffects(nMeld, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(nMeld, oMeldshaper, FALSE); + ActionCastSpellOnSelf(nMeld); + //if (DEBUG) DoDebug("Shaping Soulmeld "+IntToString(nMeld)+" on "+GetName(oMeldshaper)); +} + +void MarkMeldShaped(object oMeldshaper, int nMeld, int nClass) +{ + //if (DEBUG) DoDebug("MarkMeldShaped nMeld is "+IntToString(nMeld)); + int nCont = TRUE; + int nTest, i; + while (nCont) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i)); + //if (DEBUG) DoDebug("MarkMeldShaped nTest is "+IntToString(nTest)); + if (!nTest) // If it's blank + { + SetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i), nMeld); + //if (DEBUG) DoDebug("MarkMeldShaped SetLocal"); + nCont = FALSE; // Break the loop + } + else + i++; // Increment the counter to check + } +} + +void ClearMeldShapes(object oMeldshaper) +{ + object oSkin = GetPCSkin(oMeldshaper); + ScrubPCSkin(oMeldshaper, oSkin); + int i; + for (i = 0; i <= 22; i++) + { + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + DeleteLocalInt(oMeldshaper, "BoundMeld"+IntToString(i)); + int nTest = GetLocalInt(oMeldshaper, "SpellInvestCheck"+IntToString(i)); + if (nTest) + DeleteLocalInt(oMeldshaper, "SpellEssentia"+IntToString(nTest)); + DeleteLocalInt(oMeldshaper, "SpellInvestCheck"+IntToString(i)); + DeleteLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(i)); + DeleteLocalInt(oMeldshaper, "UsedBladeMeld"+IntToString(i)); + } + for (i = 18700; i < 18799; i++) + { + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i)); + } + for (i = 8869; i < 8889; i++) + { + DeleteLocalInt(oMeldshaper, "FeatEssentia"+IntToString(i)); + } + DeleteLocalInt(oMeldshaper, "ArcaneFocusBound"); + if (GetLocalInt(oMeldshaper, "DiademPurelight")) + { + SetLocalInt(oMeldshaper, "PRCInLight", GetLocalInt(oMeldshaper, "PRCInLight")-1); + DeleteLocalInt(oMeldshaper, "DiademPurelight"); + } + + if (GetLocalInt(oMeldshaper, "PlanarChasubleLimit") > 7) + DeleteLocalInt(oMeldshaper, "PlanarChasubleLimit"); + else + SetLocalInt(oMeldshaper, "PlanarChasubleLimit", GetLocalInt(oMeldshaper, "PlanarChasubleLimit")+1); + + DeleteLocalInt(oMeldshaper, "GorgonMaskLimit"); + DeleteLocalInt(oMeldshaper, "IncarnateAvatarSpeed"); + DeleteLocalInt(oMeldshaper, "LamiaBeltSpeed"); + DeleteLocalInt(oMeldshaper, "LifebondVestmentsTimer"); + DeleteLocalInt(oMeldshaper, "MidnightAugPower"); + DeleteLocalInt(oMeldshaper, "MeldEssentia18973"); // MELD_DUSKLING_SPEED + DeleteLocalInt(oMeldshaper, "MeldEssentia18691"); // MELD_SPINE_ENHANCEMENT + DeleteLocalInt(oMeldshaper, "MeldEssentia18687"); // MELD_IRONSOUL_SHIELD + DeleteLocalInt(oMeldshaper, "MeldEssentia18685"); // MELD_IRONSOUL_ARMOR + DeleteLocalInt(oMeldshaper, "MeldEssentia18683"); // MELD_IRONSOUL_WEAPON + DeleteLocalInt(oMeldshaper, "MeldEssentia18681"); // MELD_UMBRAL_STEP + DeleteLocalInt(oMeldshaper, "MeldEssentia18679"); // MELD_UMBRAL_SHADOW + DeleteLocalInt(oMeldshaper, "MeldEssentia18677"); // MELD_UMBRAL_SIGHT + DeleteLocalInt(oMeldshaper, "MeldEssentia18675"); // MELD_UMBRAL_SOUL + DeleteLocalInt(oMeldshaper, "MeldEssentia18673"); // MELD_UMBRAL_KISS + DeleteLocalInt(oMeldshaper, "MeldEssentia18670"); // MELD_INCANDESCENT_STRIKE + DeleteLocalInt(oMeldshaper, "MeldEssentia18668"); // MELD_INCANDESCENT_HEAL + DeleteLocalInt(oMeldshaper, "MeldEssentia18666"); // MELD_INCANDESCENT_COUNTENANCE + DeleteLocalInt(oMeldshaper, "MeldEssentia18663"); // MELD_INCANDESCENT_RAY + DeleteLocalInt(oMeldshaper, "MeldEssentia18659"); // MELD_INCANDESCENT_AURA + DeleteLocalInt(oMeldshaper, "MeldEssentia18634"); // MELD_WITCH_MELDSHIELD + DeleteLocalInt(oMeldshaper, "MeldEssentia18636"); // MELD_WITCH_DISPEL + DeleteLocalInt(oMeldshaper, "MeldEssentia18638"); // MELD_WITCH_SHACKLES + DeleteLocalInt(oMeldshaper, "MeldEssentia18640"); // MELD_WITCH_ABROGATION + DeleteLocalInt(oMeldshaper, "MeldEssentia18642"); // MELD_WITCH_SPIRITFLAY + DeleteLocalInt(oMeldshaper, "MeldEssentia18644"); // MELD_WITCH_INTEGUMENT + DeleteLocalInt(oMeldshaper, "NecrocarnumCircletPen"); + DeleteLocalInt(oMeldshaper, "AstralVambraces"); + DeleteLocalInt(oMeldshaper, "TemporaryEssentia"); + DestroyObject(GetItemPossessedBy(oMeldshaper, "moi_incarnatewpn")); // Remove any weapons created by Incarnate Weapon + + // Clean up any the natural weapons that are lying around + ClearNaturalWeapons(oMeldshaper); + // Nuke the creature weapons. If the normal form is supposed to have natural weapons, they'll get re-constructed + if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oMeldshaper))) MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oMeldshaper)); + if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oMeldshaper))) MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oMeldshaper)); + if(GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oMeldshaper))) MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oMeldshaper)); + + if (GetLocalInt(oMeldshaper, "ClearEventTotem")) ClearEventScriptList(oMeldshaper, EVENT_ITEM_ONHIT, TRUE, TRUE); +} + +int GetShapedMeldsCount(object oMeldshaper) +{ + int i, nCount, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + } + //if (DEBUG) DoDebug("GetTotalShapedMelds is "+IntToString(nCount)); + return nCount; +} + +int GetTotalShapedMelds(object oMeldshaper, int nClass) +{ + int i, nCount, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + } + //if (DEBUG) DoDebug("GetTotalShapedMelds is "+IntToString(nCount)); + return nCount; +} + +int CheckSplitChakra(object oMeldshaper, int nSlot) +{ + if (nSlot == INVENTORY_SLOT_HEAD && GetHasFeat(FEAT_SPLIT_CHAKRA_CROWN , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_BOOTS && GetHasFeat(FEAT_SPLIT_CHAKRA_FEET , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_ARMS && GetHasFeat(FEAT_SPLIT_CHAKRA_HANDS , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_ARMS && GetHasFeat(FEAT_SPLIT_CHAKRA_ARMS , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_HEAD && GetHasFeat(FEAT_SPLIT_CHAKRA_BROW , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_CLOAK && GetHasFeat(FEAT_SPLIT_CHAKRA_SHOULDERS, oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_NECK && GetHasFeat(FEAT_SPLIT_CHAKRA_THROAT , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_BELT && GetHasFeat(FEAT_SPLIT_CHAKRA_WAIST , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_CHEST && GetHasFeat(FEAT_SPLIT_CHAKRA_HEART , oMeldshaper)) return TRUE; + if (nSlot == INVENTORY_SLOT_CHEST && GetHasFeat(FEAT_SPLIT_CHAKRA_SOUL , oMeldshaper)) return TRUE; + + return FALSE; +} + +void BindMeldToChakra(object oMeldshaper, int nMeld, int nChakra, int nClass) +{ + // Make sure it's not in use already, and that you have any binds to make + if (!GetIsChakraBound(oMeldshaper, nChakra) && GetMaxBindCount(oMeldshaper, nClass)) + { + //FloatingTextStringOnCreature("BindMeldToChakra: nMeld "+IntToString(nMeld)+" nChakra "+IntToString(nChakra), oMeldshaper); + SetLocalInt(oMeldshaper, "BoundMeld"+IntToString(nChakra), nMeld); + ShapeSoulmeld(oMeldshaper, nMeld); + int nSlot = ChakraToSlot(DoubleChakraToChakra(nChakra)); // Can't have an item in a bound slot, unless Split Chakra + if (!CheckSplitChakra(oMeldshaper, nSlot)) ForceUnequip(oMeldshaper, GetItemInSlot(nSlot, oMeldshaper), nSlot); + } +} + +int GetTotalBoundMelds(object oMeldshaper) +{ + int i, nCount, nTest; + for (i = 0; i <= 22; i++) + { + nTest = GetLocalInt(oMeldshaper, "BoundMeld"+IntToString(i)); + if (nTest) // If it's not blank + nCount++; + } + //if (DEBUG) DoDebug("GetTotalBoundMelds is "+IntToString(nCount)); + return nCount; +} + +int GetIsChakraUsed(object oMeldshaper, int nChakra, int nClass) +{ + int nTest = GetLocalInt(oMeldshaper, "UsedMeld"+IntToString(nClass)+IntToString(nChakra)); + + //if (DEBUG) DoDebug("GetIsChakraUsed is "+IntToString(nTest)); + return nTest; +} + +void SetChakraUsed(object oMeldshaper, int nMeld, int nChakra, int nClass) +{ + // This isn't the same as binding, but can only have one soulmeld in each chakra + // Each class has its own limit on this, as per p20 of MoI + if (!GetIsChakraUsed(oMeldshaper, nChakra, nClass)) + { + SetLocalInt(oMeldshaper, "UsedMeld"+IntToString(nClass)+IntToString(nChakra), nMeld); + } +} + +int ChakraToSlot(int nChakra) +{ + if (nChakra == CHAKRA_CROWN ) return INVENTORY_SLOT_HEAD; + if (nChakra == CHAKRA_FEET ) return INVENTORY_SLOT_BOOTS; + if (nChakra == CHAKRA_HANDS ) return INVENTORY_SLOT_ARMS; + if (nChakra == CHAKRA_ARMS ) return INVENTORY_SLOT_ARMS; + if (nChakra == CHAKRA_BROW ) return INVENTORY_SLOT_HEAD; + if (nChakra == CHAKRA_SHOULDERS) return INVENTORY_SLOT_CLOAK; + if (nChakra == CHAKRA_THROAT ) return INVENTORY_SLOT_NECK; + if (nChakra == CHAKRA_WAIST ) return INVENTORY_SLOT_BELT; + if (nChakra == CHAKRA_HEART ) return INVENTORY_SLOT_CHEST; + if (nChakra == CHAKRA_SOUL ) return INVENTORY_SLOT_CHEST; + if (nChakra == CHAKRA_TOTEM ) return -1; // no slot associated + + return -1; +} + +void ChakraBindUnequip(object oMeldshaper, object oItem) +{ + int nTest = FALSE; + if (GetItemInSlot(INVENTORY_SLOT_HEAD, oMeldshaper) == oItem && (GetIsChakraBound(oMeldshaper, CHAKRA_CROWN) || GetIsChakraBound(oMeldshaper, CHAKRA_BROW))) nTest = INVENTORY_SLOT_HEAD + 1; + else if (GetItemInSlot(INVENTORY_SLOT_BOOTS, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_FEET)) nTest = INVENTORY_SLOT_BOOTS + 1; + else if (GetItemInSlot(INVENTORY_SLOT_ARMS, oMeldshaper) == oItem && (GetIsChakraBound(oMeldshaper, CHAKRA_ARMS) || GetIsChakraBound(oMeldshaper, CHAKRA_HANDS))) nTest = INVENTORY_SLOT_ARMS + 1; + else if (GetItemInSlot(INVENTORY_SLOT_CLOAK, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_SHOULDERS)) nTest = INVENTORY_SLOT_CLOAK + 1; + else if (GetItemInSlot(INVENTORY_SLOT_NECK, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_THROAT)) nTest = INVENTORY_SLOT_NECK + 1; + else if (GetItemInSlot(INVENTORY_SLOT_BELT, oMeldshaper) == oItem && GetIsChakraBound(oMeldshaper, CHAKRA_WAIST)) nTest = INVENTORY_SLOT_BELT + 1; + else if (GetItemInSlot(INVENTORY_SLOT_CHEST, oMeldshaper) == oItem && (GetIsChakraBound(oMeldshaper, CHAKRA_SOUL) || GetIsChakraBound(oMeldshaper, CHAKRA_HEART))) nTest = INVENTORY_SLOT_CHEST + 1; + //if (DEBUG) DoDebug("ChakraBindUnequip is "+IntToString(nTest-1)); + if (nTest && !CheckSplitChakra(oMeldshaper, nTest-1) && GetIsItemPropertyValid(GetFirstItemProperty(oItem)) && oItem != GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oMeldshaper) && oItem != GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oMeldshaper) && + oItem != GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oMeldshaper) && oItem != GetItemInSlot(INVENTORY_SLOT_CARMOUR, oMeldshaper)) // If it's bound you can't equip in that slot + { + nTest = nTest - 1; + ForceUnequip(oMeldshaper, GetItemInSlot(nTest, oMeldshaper), nTest); + FloatingTextStringOnCreature("You cannot equip a magical item when you have bound a meld to the same chakra!", oMeldshaper, FALSE); + } +} + +string ChakraToString(int nChakra) +{ + string sReturn = ""; + if (nChakra == CHAKRA_CROWN || nChakra == CHAKRA_DOUBLE_CROWN ) sReturn = "Crown"; + if (nChakra == CHAKRA_FEET || nChakra == CHAKRA_DOUBLE_FEET ) sReturn = "Feet"; + if (nChakra == CHAKRA_HANDS || nChakra == CHAKRA_DOUBLE_HANDS ) sReturn = "Hands"; + if (nChakra == CHAKRA_ARMS || nChakra == CHAKRA_DOUBLE_ARMS ) sReturn = "Arms"; + if (nChakra == CHAKRA_BROW || nChakra == CHAKRA_DOUBLE_BROW ) sReturn = "Brow"; + if (nChakra == CHAKRA_SHOULDERS || nChakra == CHAKRA_DOUBLE_SHOULDERS) sReturn = "Shoulders"; + if (nChakra == CHAKRA_THROAT || nChakra == CHAKRA_DOUBLE_THROAT ) sReturn = "Throat"; + if (nChakra == CHAKRA_WAIST || nChakra == CHAKRA_DOUBLE_WAIST ) sReturn = "Waist"; + if (nChakra == CHAKRA_HEART || nChakra == CHAKRA_DOUBLE_HEART ) sReturn = "Heart"; + if (nChakra == CHAKRA_SOUL || nChakra == CHAKRA_DOUBLE_SOUL ) sReturn = "Soul"; + if (nChakra == CHAKRA_TOTEM || nChakra == CHAKRA_DOUBLE_TOTEM ) sReturn = "Totem"; + + //if (DEBUG) DoDebug("ChakraToString is "+IntToString(nChakra)+", Return is "+sReturn); + return sReturn; +} + +int GetCanBindChakra(object oMeldshaper, int nChakra) +{ + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 2)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 4)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 4)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 16)) return TRUE; + else if (nChakra == CHAKRA_SOUL && + (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 19)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 18)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) >= 18)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 5)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 5)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 5)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 9)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 14)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 17)) return TRUE; + else if (nChakra == CHAKRA_TOTEM && + (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 2)) return TRUE; + + if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper) >= 7)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_SOULCASTER, oMeldshaper) >= 8)) return TRUE; + + if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 4)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 6)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper) >= 10)) return TRUE; + + if (nChakra == CHAKRA_CROWN && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=4)) return TRUE; + else if (nChakra == CHAKRA_FEET && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=4)) return TRUE; + else if (nChakra == CHAKRA_HANDS && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=4)) return TRUE; + else if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=9)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=9)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >=9)) return TRUE; + + if (nChakra == CHAKRA_ARMS && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_BROW && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 3)) return TRUE; + else if (nChakra == CHAKRA_THROAT && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_WAIST && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 8)) return TRUE; + else if (nChakra == CHAKRA_HEART && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 11)) return TRUE; + else if (nChakra == CHAKRA_SOUL && + (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 13)) return TRUE; + + if (nChakra == CHAKRA_CROWN && GetHasFeat(FEAT_OPEN_LEAST_CHAKRA_CROWN, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_FEET && GetHasFeat(FEAT_OPEN_LEAST_CHAKRA_FEET , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_HANDS && GetHasFeat(FEAT_OPEN_LEAST_CHAKRA_HANDS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_ARMS && GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_ARMS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_BROW && GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_BROW , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_SHOULDERS && GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_SHOULDERS, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_THROAT && GetHasFeat(FEAT_OPEN_GREATER_CHAKRA_THROAT , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_WAIST && GetHasFeat(FEAT_OPEN_GREATER_CHAKRA_WAIST , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_HEART && GetHasFeat(FEAT_OPEN_HEART_CHAKRA , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_SOUL && GetHasFeat(FEAT_OPEN_SOUL_CHAKRA , oMeldshaper)) return TRUE; + + if (nChakra == CHAKRA_DOUBLE_CROWN && GetHasFeat(FEAT_DOUBLE_CHAKRA_CROWN, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_FEET && GetHasFeat(FEAT_DOUBLE_CHAKRA_FEET , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_HANDS && GetHasFeat(FEAT_DOUBLE_CHAKRA_HANDS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_ARMS && GetHasFeat(FEAT_DOUBLE_CHAKRA_ARMS , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_BROW && GetHasFeat(FEAT_DOUBLE_CHAKRA_BROW , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_SHOULDERS && GetHasFeat(FEAT_DOUBLE_CHAKRA_SHOULDERS, oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_THROAT && GetHasFeat(FEAT_DOUBLE_CHAKRA_THROAT , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_WAIST && GetHasFeat(FEAT_DOUBLE_CHAKRA_WAIST , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_HEART && GetHasFeat(FEAT_DOUBLE_CHAKRA_HEART , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_SOUL && GetHasFeat(FEAT_DOUBLE_CHAKRA_SOUL , oMeldshaper)) return TRUE; + else if (nChakra == CHAKRA_DOUBLE_TOTEM && GetHasFeat(FEAT_DOUBLE_CHAKRA_TOTEM , oMeldshaper)) return TRUE; + + return FALSE; +} + +int GetMaxEssentiaCount(object oMeldshaper, int nClass) +{ + int nMax = StringToInt(Get2DACache(GetMeldshapingClassFile(nClass), "Essentia", GetIncarnumLevelForClass(nClass, oMeldshaper)-1)); + //if (DEBUG) DoDebug("GetMaxEssentiaCount is "+IntToString(nMax)); + return nMax; +} + +void SpawnTempEssentiaChecker(object oMeldshaper) +{ + int nCur = GetTemporaryEssentia(oMeldshaper); + int nPrv = GetLocalInt(oMeldshaper, "TempEssTest"); + int nRed = nPrv - nCur; + // If we've lost some + if (nPrv > nCur) + { + int i, nCount, nTest; + for (i = 18700; i < 18799; i++) + { + nTest = GetLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(i)); + if (nTest) // If it's not blank + { + //if (DEBUG) DoDebug("Found "+IntToString(nTest)+" Temp Essentia in Meld "+IntToString(i)); + // There's still some temp essentia left in the meld + if (nTest > nRed) + { + int nChange = nTest-nRed; + SetLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(i), nChange); + SetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i), GetEssentiaInvested(oMeldshaper, i)-nChange); + ShapeSoulmeld(oMeldshaper, i); + //if (DEBUG) DoDebug("Reducing Essentia in Meld "+IntToString(i)+" by "+IntToString(nChange)); + } + else // Means the reduction is higher than the temp essentia ammount + { + // No more temp essentia here + DeleteLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(i)); + SetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i), GetEssentiaInvested(oMeldshaper, i)-nTest); + ShapeSoulmeld(oMeldshaper, i); + //if (DEBUG) DoDebug("Deleted Temp Essentia in Meld "+IntToString(i)+", total "+IntToString(nTest)); + } + + // Reduce the remaining amount of reduction by the amount we just used up + nRed = nRed - nTest; + //if (DEBUG) DoDebug("Remaining Temp Essentia to reduce "+IntToString(nRed)); + if (0 >= nRed) break; // End the loop if we're done + } + } + } + // If we still have some left + if (nCur > 0) + { + SetLocalInt(oMeldshaper, "TempEssTest", nCur); + DelayCommand(1.0, SpawnTempEssentiaChecker(oMeldshaper)); + } + else + DeleteLocalInt(oMeldshaper, "TempEssTest"); +} + +void InvestEssentia(object oMeldshaper, int nMeld, int nEssentia) +{ + // Use up expanded soulmeld capacity + if (nEssentia > 1000) + { + nEssentia -= 1000; + SetIsSoulmeldCapacityUsed(oMeldshaper, nMeld); + } + int nClass = GetMeldShapedClass(oMeldshaper, nMeld); + // Special capacity of 1/2 class level + if (nMeld == MELD_SPINE_ENHANCEMENT) nEssentia = PRCMin(nEssentia, (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)/2)); + else if (nEssentia > GetMaxEssentiaCapacity(oMeldshaper, nClass, nMeld)) nEssentia = GetMaxEssentiaCapacity(oMeldshaper, nClass, nMeld); + // Can't invest more than you have + if (nEssentia > GetTotalUsableEssentia(oMeldshaper)) nEssentia = GetTotalUsableEssentia(oMeldshaper); + + // All of this garbage to handle temporary essentia + if (GetLocalInt(oMeldshaper, "InvestingTempEssentia")) + { + SetLocalInt(oMeldshaper, "TempEssentiaAmount"+IntToString(nMeld), nEssentia); + SpawnTempEssentiaChecker(oMeldshaper); + DeleteLocalInt(oMeldshaper, "InvestingTempEssentia"); + } + + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nMeld))), oMeldshaper, FALSE); + SetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(nMeld), nEssentia); + SetLocalInt(oMeldshaper, "EssentiaRound"+IntToString(nMeld), TRUE); // This is used by Melds that only trigger on the round that essentia is invested into it + DelayCommand(6.0, DeleteLocalInt(oMeldshaper, "EssentiaRound"+IntToString(nMeld))); +} + +int GetTotalEssentiaInvested(object oMeldshaper) +{ + int i, nCount, nTest; + for (i = 18700; i < 18799; i++) + { + nTest = GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i)); + if (nTest) // If it's not blank + nCount += nTest; + } + nCount += GetFeatLockedEssentia(oMeldshaper); + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_SPINE_ENHANCEMENT)); // MELD_SPINE_ENHANCEMENT + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_SHIELD)); // MELD_IRONSOUL_SHIELD + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_ARMOR)); // MELD_IRONSOUL_ARMOR + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_WEAPON)); // MELD_IRONSOUL_WEAPON + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_STEP )); // MELD_UMBRAL_STEP + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SHADOW)); // MELD_UMBRAL_SHADOW + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SIGHT )); // MELD_UMBRAL_SIGHT + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SOUL )); // MELD_UMBRAL_SOUL + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_KISS )); // MELD_UMBRAL_KISS + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_DUSKLING_SPEED)); // MELD_DUSKLING_SPEED + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_STRIKE)); // MELD_INCANDESCENT_STRIKE + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_HEAL )); // MELD_INCANDESCENT_HEAL + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_COUNTENANCE)); // MELD_INCANDESCENT_COUNTENANCE + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_RAY )); // MELD_INCANDESCENT_RAY + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_AURA )); // MELD_INCANDESCENT_AURA + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_MELDSHIELD)); // MELD_WITCH_MELDSHIELD + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_DISPEL )); // MELD_WITCH_DISPEL + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SHACKLES )); // MELD_WITCH_SHACKLES + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_ABROGATION)); // MELD_WITCH_ABROGATION + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SPIRITFLAY)); // MELD_WITCH_SPIRITFLAY + nCount += GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_INTEGUMENT)); // MELD_WITCH_INTEGUMENT + //if (DEBUG) DoDebug("GetTotalEssentiaInvested is "+IntToString(nCount)); + return nCount; +} + +int EssentiaIDToRealID(int nEssentiaID) +{ + int i, nReal, nTest; + for (i = 1; i < 100; i++) + { + nTest = StringToInt(Get2DACache("soulmelds", "EssentiaID", i)); + if (nTest == nEssentiaID) // If it's been found + { + nReal = StringToInt(Get2DACache("soulmelds", "SpellID", i)); + break; + } + } + + if (nEssentiaID == 18690) nReal = MELD_SPINE_ENHANCEMENT; + else if (nEssentiaID == 18686) nReal = MELD_IRONSOUL_SHIELD; + else if (nEssentiaID == 18684) nReal = MELD_IRONSOUL_ARMOR; + else if (nEssentiaID == 18682) nReal = MELD_IRONSOUL_WEAPON; + else if (nEssentiaID == 18680) nReal = MELD_UMBRAL_STEP; + else if (nEssentiaID == 18678) nReal = MELD_UMBRAL_SHADOW; + else if (nEssentiaID == 18676) nReal = MELD_UMBRAL_SIGHT; + else if (nEssentiaID == 18674) nReal = MELD_UMBRAL_SOUL; + else if (nEssentiaID == 18672) nReal = MELD_UMBRAL_KISS; + else if (nEssentiaID == 18669) nReal = MELD_INCANDESCENT_STRIKE; + else if (nEssentiaID == 18667) nReal = MELD_INCANDESCENT_HEAL ; + else if (nEssentiaID == 18665) nReal = MELD_INCANDESCENT_COUNTENANCE; + else if (nEssentiaID == 18662) nReal = MELD_INCANDESCENT_RAY ; + else if (nEssentiaID == 18661) nReal = MELD_INCANDESCENT_AURA ; + else if (nEssentiaID == 18633) nReal = MELD_WITCH_MELDSHIELD; + else if (nEssentiaID == 18635) nReal = MELD_WITCH_DISPEL; + else if (nEssentiaID == 18637) nReal = MELD_WITCH_SHACKLES; + else if (nEssentiaID == 18639) nReal = MELD_WITCH_ABROGATION; + else if (nEssentiaID == 18641) nReal = MELD_WITCH_SPIRITFLAY; + else if (nEssentiaID == 18643) nReal = MELD_WITCH_INTEGUMENT; + + //if (DEBUG) DoDebug("EssentiaToRealID: nEssentiaID "+IntToString(nEssentiaID)+" nReal "+IntToString(nReal)); + return nReal; // Return the real spellID +} + +void DrainEssentia(object oMeldshaper) +{ + //FloatingTextStringOnCreature("DrainEssentia", oMeldshaper); + int i; + for (i = 18700; i < 18799; i++) + { + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(i)); + } + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_DUSKLING_SPEED)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_SPINE_ENHANCEMENT)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_SHIELD)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_ARMOR)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_IRONSOUL_WEAPON)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_STEP )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SHADOW)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SIGHT )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_SOUL )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_UMBRAL_KISS )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_STRIKE)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_HEAL )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_COUNTENANCE)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_RAY )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_INCANDESCENT_AURA )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_MELDSHIELD)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_DISPEL )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SHACKLES )); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_ABROGATION)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_SPIRITFLAY)); + DeleteLocalInt(oMeldshaper, "MeldEssentia"+IntToString(MELD_WITCH_INTEGUMENT)); +} + +void WipeMelds(object oMeldshaper) +{ + //FloatingTextStringOnCreature("WipeMelds", oMeldshaper); + int i; + for (i = 18700; i < 18799; i++) + { + PRCRemoveSpellEffects(i, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(i, oMeldshaper, FALSE); + } + PRCRemoveSpellEffects(MELD_DUSKLING_SPEED, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_DUSKLING_SPEED, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_SPINE_ENHANCEMENT, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_SPINE_ENHANCEMENT, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_IRONSOUL_SHIELD, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_IRONSOUL_SHIELD, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_IRONSOUL_ARMOR, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_IRONSOUL_ARMOR, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_IRONSOUL_WEAPON, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_IRONSOUL_WEAPON, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_STEP, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_STEP, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_SHADOW, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_SHADOW, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_SIGHT, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_SIGHT, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_SOUL, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_SOUL, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_UMBRAL_KISS, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_UMBRAL_KISS, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_INCANDESCENT_STRIKE, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_INCANDESCENT_STRIKE, oMeldshaper, FALSE); + PRCRemoveSpellEffects(MELD_WITCH_MELDSHIELD, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_WITCH_MELDSHIELD, oMeldshaper, FALSE); + for (i = 18647; i < 18657; i++) + { + PRCRemoveSpellEffects(i, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(i, oMeldshaper, FALSE); + } +} + +void ReshapeMelds(object oMeldshaper) +{ + // Check each class and run the loop + if (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper)) + { + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_INCARNATE)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_INCARNATE nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + if (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper)) + { + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SOULBORN)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_SOULBORN nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + if (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper)) + { + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_TOTEMIST)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_TOTEMIST nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + if (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)) + { + ActionCastSpellOnSelf(MELD_SPINE_ENHANCEMENT); + int i, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(CLASS_TYPE_SPINEMELD_WARRIOR)+IntToString(i)); + if (nTest) // If it's not blank, recast it + { + //FloatingTextStringOnCreature("ReshapeMelds: CLASS_TYPE_TOTEMIST nTest "+IntToString(nTest), oMeldshaper); + ActionCastSpellOnSelf(nTest); + } + } + } + int nIron = GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oMeldshaper); + if (nIron) + { + ActionCastSpellOnSelf(MELD_IRONSOUL_SHIELD); + if (nIron >= 5) ActionCastSpellOnSelf(MELD_IRONSOUL_ARMOR); + if (nIron >= 9) ActionCastSpellOnSelf(MELD_IRONSOUL_WEAPON); + } + int nUmbral = GetLevelByClass(CLASS_TYPE_UMBRAL_DISCIPLE, oMeldshaper); + if (nUmbral) + { + ActionCastSpellOnSelf(MELD_UMBRAL_STEP); + if (nUmbral >= 3) ActionCastSpellOnSelf(MELD_UMBRAL_SHADOW); + if (nUmbral >= 7) ActionCastSpellOnSelf(MELD_UMBRAL_SIGHT); + if (nUmbral >= 9) ActionCastSpellOnSelf(MELD_UMBRAL_SOUL); + if (nUmbral >= 10) ActionCastSpellOnSelf(MELD_UMBRAL_KISS); + } + int nIncand = GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oMeldshaper); + if (nIncand) + { + ActionCastSpellOnSelf(MELD_INCANDESCENT_STRIKE); + } + if(GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper)) ActionCastSpellOnSelf(MELD_WITCH_MELDSHIELD); + if (GetRacialType(oMeldshaper) == RACIAL_TYPE_DUSKLING) ActionCastSpellOnSelf(MELD_DUSKLING_SPEED); +} + +int EssentiaToD4(int nEssentia) +{ + if (nEssentia == 1 ) return IP_CONST_DAMAGEBONUS_1d4; + else if (nEssentia == 2) return IP_CONST_DAMAGEBONUS_2d4; + else if (nEssentia == 3) return IP_CONST_DAMAGEBONUS_3d4; + else if (nEssentia == 4) return IP_CONST_DAMAGEBONUS_4d4; + else if (nEssentia == 5) return IP_CONST_DAMAGEBONUS_5d4; + else if (nEssentia == 6) return IP_CONST_DAMAGEBONUS_6d4; + else if (nEssentia == 7) return IP_CONST_DAMAGEBONUS_7d4; + else if (nEssentia == 8) return IP_CONST_DAMAGEBONUS_8d4; + else if (nEssentia == 9) return IP_CONST_DAMAGEBONUS_9d4; + else if (nEssentia >= 10) return IP_CONST_DAMAGEBONUS_10d4; + + return -1; +} + +int IncarnateAlignment(object oMeldshaper) +{ + int nReturn = FALSE; + + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_LAWFUL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_NEUTRAL) + { + nReturn = TRUE; + } + else if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_CHAOTIC && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_NEUTRAL) + { + nReturn = TRUE; + } + else if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_NEUTRAL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_GOOD) + { + nReturn = TRUE; + } + else if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_NEUTRAL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_EVIL) + { + nReturn = TRUE; + } + + return nReturn; +} + +int GetOpposition(object oMeldshaper, object oTarget) +{ + int nReturn = FALSE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_LAWFUL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_GOOD && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL || GetAlignmentLawChaos(oTarget) == ALIGNMENT_CHAOTIC)) + nReturn = TRUE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_LAWFUL && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_EVIL && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD || GetAlignmentLawChaos(oTarget) == ALIGNMENT_CHAOTIC)) + nReturn = TRUE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_CHAOTIC && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_GOOD && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL || GetAlignmentLawChaos(oTarget) == ALIGNMENT_LAWFUL)) + nReturn = TRUE; + if (GetAlignmentLawChaos(oMeldshaper) == ALIGNMENT_CHAOTIC && GetAlignmentGoodEvil(oMeldshaper) == ALIGNMENT_EVIL && (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD || GetAlignmentLawChaos(oTarget) == ALIGNMENT_LAWFUL)) + nReturn = TRUE; + + return nReturn; +} + +void SetTemporaryEssentia(object oMeldshaper, int nEssentia) +{ + //if (DEBUG) DoDebug("Set Temporary Essentia from "+IntToString(GetLocalInt(oMeldshaper, "TemporaryEssentia"))+" to "+IntToString(GetLocalInt(oMeldshaper, "TemporaryEssentia")+nEssentia)); + SetLocalInt(oMeldshaper, "TemporaryEssentia", GetLocalInt(oMeldshaper, "TemporaryEssentia")+nEssentia); +} + +int GetTemporaryEssentia(object oMeldshaper) +{ + return GetLocalInt(oMeldshaper, "TemporaryEssentia"); +} + +int GetMaxEssentiaCapacityFeat(object oMeldshaper) +{ + int nMax = 1; // Always can invest one + int nHD = GetHitDice(oMeldshaper); + if (nHD >= 31) nMax = 5; + else if (nHD >= 18) nMax = 4; + else if (nHD >= 12) nMax = 3; + else if (nHD >= 6) nMax = 2; + + if (GetLocalInt(oMeldshaper, "DivineSoultouch")) nMax += 1; + if (GetLocalInt(oMeldshaper, "IncandescentOverload")) nMax += PRCMax(GetAbilityModifier(ABILITY_CHARISMA, oMeldshaper), 1); + if (GetHasFeat(FEAT_IMPROVED_ESSENTIA_CAPACITY, oMeldshaper)) nMax += 1; + + // Don't allow more than they have + if (nMax > GetTotalUsableEssentia(oMeldshaper)) nMax = GetTotalUsableEssentia(oMeldshaper); + + //if (DEBUG) DoDebug("GetMaxEssentiaCapacityFeat: nHD "+IntToString(nHD)+" nMax "+IntToString(nMax)); + return nMax; +} + +void SapphireSmiteUses(object oMeldshaper, int nEssentia) +{ + int i; + + for (i = 1; i <= nEssentia; i++) + { + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_GOOD_ALIGN); // Fist of Raziel + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_EVIL); // Paladin + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_GOOD); // Blackguard + IncrementRemainingFeatUses(oMeldshaper, FEAT_KIAI_SMITE); // CW Samurai + IncrementRemainingFeatUses(oMeldshaper, FEAT_CRUSADER_SMITE); // Crusader + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_UNDEAD); // Soldier of Light + IncrementRemainingFeatUses(oMeldshaper, FEAT_SHADOWBANE_SMITE); // Shadowbane + IncrementRemainingFeatUses(oMeldshaper, FEAT_KILLOREN_ASPECT_D); // Killoren + IncrementRemainingFeatUses(oMeldshaper, FEAT_SMITE_OPPOSITION); // Soulborn + IncrementRemainingFeatUses(oMeldshaper, FEAT_CULTIST_SMITE_MAGE); // Cultist of the Shattered Peak + } +} + +void AzureEnmity(object oMeldshaper, int nEssentia) +{ + effect eLink = EffectLinkEffects(EffectSkillIncrease(SKILL_BLUFF, nEssentia), EffectSkillIncrease(SKILL_LISTEN, nEssentia)); + eLink = EffectLinkEffects(eLink, EffectSkillIncrease(SKILL_SENSE_MOTIVE, nEssentia)); + eLink = EffectLinkEffects(eLink, EffectSkillIncrease(SKILL_SPOT, nEssentia)); + eLink = EffectLinkEffects(eLink, EffectDamageIncrease(IPGetDamageBonusConstantFromNumber(nEssentia), DAMAGE_TYPE_BASE_WEAPON)); + + if(GetHasFeat(FEAT_FAVORED_ENEMY_ABERRATION, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ABERRATION)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_ANIMAL, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ANIMAL)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_BEAST, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_BEAST)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_CONSTRUCT, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_CONSTRUCT)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_DRAGON, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_DRAGON)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_DWARF, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_DWARF)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_ELEMENTAL, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ELEMENTAL)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_ELF, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_ELF)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_FEY, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_FEY)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_GIANT, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_GIANT)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_GNOME, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_GNOME)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_GOBLINOID, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_GOBLINOID)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HALFELF, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HALFELF)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HALFLING, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HALFLING)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HALFORC, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HALFORC)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_HUMAN, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMAN)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_MAGICAL_BEAST, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_MAGICAL_BEAST)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_MONSTROUS, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_MONSTROUS)), oMeldshaper, 9999.0); + if(GetHasFeat(RACIAL_TYPE_HUMANOID_ORC, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_ORC)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_OUTSIDER, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_OUTSIDER)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_REPTILIAN, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_HUMANOID_REPTILIAN)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_SHAPECHANGER, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_SHAPECHANGER)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_UNDEAD, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_UNDEAD)), oMeldshaper, 9999.0); + if(GetHasFeat(FEAT_FAVORED_ENEMY_VERMIN, oMeldshaper)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(VersusRacialTypeEffect(eLink, RACIAL_TYPE_VERMIN)), oMeldshaper, 9999.0); +} + +void DoFeatBonus(object oMeldshaper, int nFeat, int nEssentia) +{ + effect eLink; + if (nFeat == FEAT_CERULEAN_FORTITUDE) eLink = EffectSavingThrowIncrease(SAVING_THROW_FORT, nEssentia); + else if (nFeat == FEAT_CERULEAN_REFLEXES) eLink = EffectSavingThrowIncrease(SAVING_THROW_REFLEX, nEssentia); + else if (nFeat == FEAT_CERULEAN_WILL) eLink = EffectSavingThrowIncrease(SAVING_THROW_WILL, nEssentia); + else if (nFeat == FEAT_AZURE_TALENT) ExecuteScript("moi_ft_aztalent", oMeldshaper); + else if (nFeat == FEAT_AZURE_TOUGHNESS) eLink = EffectTemporaryHitpoints(3*nEssentia); + else if (nFeat == FEAT_HEALING_SOUL) FeatUsePerDay(oMeldshaper, FEAT_HEALING_SOUL, -1, 0, nEssentia); + else if (nFeat == FEAT_SAPPHIRE_SMITE) SapphireSmiteUses(oMeldshaper, nEssentia); + else if (nFeat == FEAT_AZURE_ENMITY) AzureEnmity(oMeldshaper, nEssentia); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(eLink), oMeldshaper, 9999.0); +} + +void InvestEssentiaFeat(object oMeldshaper, int nFeat, int nEssentia) +{ + // Jump out if there's no feat + if (!GetHasFeat(nFeat, oMeldshaper)) return; + + int nMax = GetMaxEssentiaCapacityFeat(oMeldshaper); + + // Bonuses to specific feat caps + if (nFeat == FEAT_COBALT_RAGE && GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper)) nMax++; + + // No breaking the rules + if (nEssentia > nMax) nEssentia = nMax; + // Can't invest more than you have + if (nEssentia > GetTotalUsableEssentia(oMeldshaper)) nEssentia = GetTotalUsableEssentia(oMeldshaper); + + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))), oMeldshaper); + DoFeatBonus(oMeldshaper, nFeat, nEssentia); + SetLocalInt(oMeldshaper, "FeatEssentia"+IntToString(nFeat), nEssentia); +} + +void DoClassBonus(object oMeldshaper, int nFeat, int nEssentia) +{ + +} + +void InvestEssentiaClass(object oMeldshaper, int nFeat, int nEssentia) +{ + // Jump out if there's no feat + if (!GetHasFeat(nFeat, oMeldshaper)) return; + + // No breaking the rules + if (nEssentia > GetMaxEssentiaCapacityFeat(oMeldshaper)) nEssentia = GetMaxEssentiaCapacityFeat(oMeldshaper); + // Can't invest more than you have + if (nEssentia > GetTotalUsableEssentia(oMeldshaper)) nEssentia = GetTotalUsableEssentia(oMeldshaper); + + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))), oMeldshaper); + DoClassBonus(oMeldshaper, nFeat, nEssentia); + SetLocalInt(oMeldshaper, "ClassEssentia"+IntToString(nFeat), nEssentia); +} + +void InvestEssentiaSpell(object oMeldshaper, int nFeat, int nEssentia) +{ + FloatingTextStringOnCreature("Investing "+IntToString(nEssentia)+" essentia into "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nFeat))), oMeldshaper); + SetLocalInt(oMeldshaper, "SpellEssentia"+IntToString(nFeat), nEssentia); + if (!GetLocalInt(oMeldshaper, "SpellInvestCheck1")) SetLocalInt(oMeldshaper, "SpellInvestCheck1", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck2")) SetLocalInt(oMeldshaper, "SpellInvestCheck2", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck3")) SetLocalInt(oMeldshaper, "SpellInvestCheck3", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck4")) SetLocalInt(oMeldshaper, "SpellInvestCheck4", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck5")) SetLocalInt(oMeldshaper, "SpellInvestCheck5", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck6")) SetLocalInt(oMeldshaper, "SpellInvestCheck6", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck7")) SetLocalInt(oMeldshaper, "SpellInvestCheck7", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck8")) SetLocalInt(oMeldshaper, "SpellInvestCheck8", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck9")) SetLocalInt(oMeldshaper, "SpellInvestCheck9", nFeat); + else if (!GetLocalInt(oMeldshaper, "SpellInvestCheck10")) SetLocalInt(oMeldshaper, "SpellInvestCheck10", nFeat); +} + +int GetFeatLockedEssentia(object oMeldshaper) +{ + int nTotal, i, nTest; + for (i = 8869; i < 8889; i++) + { + nTest = GetLocalInt(oMeldshaper, "FeatEssentia"+IntToString(i)); + if (nTest) // If it's not blank + nTotal += nTest; + } + for (i = 0; i < 11; i++) + { + nTest = GetLocalInt(oMeldshaper, "SpellInvestCheck"+IntToString(i)); + if (nTest) // If it's not blank + nTotal += GetLocalInt(oMeldshaper, "SpellEssentia"+IntToString(nTest)); + } + //if (DEBUG) DoDebug("GetFeatLockedEssentia return value "+IntToString(nTotal)); + return nTotal; +} + +int IncarnumFeats(object oMeldshaper) +{ + int nEssentia; + + if (GetHasFeat(FEAT_AZURE_ENMITY, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_CERULEAN_FORTITUDE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_CERULEAN_REFLEXES, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_CERULEAN_WILL, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TALENT, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TOUCH, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TOUGHNESS, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_TURNING, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_AZURE_WILD_SHAPE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_CHARGE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_EXPERTISE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_POWER, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_COBALT_RAGE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_HEALING_SOUL, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_INCARNUM_SPELLSHAPING, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_INDIGO_STRIKE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_MIDNIGHT_AUGMENTATION, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_MIDNIGHT_DODGE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_MIDNIGHT_METAMAGIC, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_PSYCARNUM_BLADE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_SAPPHIRE_SMITE, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_SOULTOUCHED_SPELLCASTING, oMeldshaper)) nEssentia += 1; + if (GetHasFeat(FEAT_BONUS_ESSENTIA, oMeldshaper)) + { + nEssentia += 1; + if(GetIsIncarnumUser(oMeldshaper)) + nEssentia += 1; // Yes, this is correct + } + if (GetRacialType(oMeldshaper) == RACIAL_TYPE_DUSKLING) nEssentia += 1; + if (GetRacialType(oMeldshaper) == RACIAL_TYPE_AZURIN) nEssentia += 1; + + int i; + for(i = FEAT_EPIC_ESSENTIA_1; i <= FEAT_EPIC_ESSENTIA_6; i++) + if(GetHasFeat(i, oMeldshaper)) nEssentia += 3; + + //if (DEBUG) DoDebug("IncarnumFeats return value "+IntToString(nEssentia)); + return nEssentia; +} + +void AddNecrocarnumEssentia(object oMeldshaper, int nEssentia) +{ + int nNecrocarnum = GetLocalInt(oMeldshaper, "NecrocarnumEssentia"); + SetLocalInt(oMeldshaper, "NecrocarnumEssentia", nNecrocarnum+nEssentia); + // It lasts for 24 hours, then remove it back out + DelayCommand(HoursToSeconds(24), SetLocalInt(oMeldshaper, "NecrocarnumEssentia", GetLocalInt(oMeldshaper, "NecrocarnumEssentia")-nEssentia)); +} + +int GetTotalEssentia(object oMeldshaper) +{ + int nEssentia; + + if (GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_INCARNATE); + if (GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_SOULBORN); + if (GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_TOTEMIST); + if (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_SPINEMELD_WARRIOR); + if (GetLevelByClass(CLASS_TYPE_UMBRAL_DISCIPLE, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_UMBRAL_DISCIPLE); + if (GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper)) nEssentia += GetLocalInt(oMeldshaper, "NecrocarnumEssentia"); + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper) >= 2) nEssentia += 1; + if (GetLevelByClass(CLASS_TYPE_WITCHBORN_BINDER, oMeldshaper) >= 6) nEssentia += 1; + if (GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oMeldshaper)) nEssentia += GetMaxEssentiaCount(oMeldshaper, CLASS_TYPE_INCANDESCENT_CHAMPION); + + nEssentia += IncarnumFeats(oMeldshaper); + //if (DEBUG) DoDebug("GetTotalEssentia return value "+IntToString(nEssentia)); + return nEssentia; +} + +int GetTotalUsableEssentia(object oMeldshaper) +{ + return GetTotalEssentia(oMeldshaper) - GetFeatLockedEssentia(oMeldshaper); +} + +int GetIncarnumFeats(object oMeldshaper) +{ + int nTotal, i; + for (i = 8868; i < 8910; i++) + { + if (GetHasFeat(i, oMeldshaper)) + nTotal += 1; + } + //if (DEBUG) DoDebug("GetIncarnumFeats return value "+IntToString(nTotal)); + return nTotal; +} + +int GetIsBlademeldUsed(object oMeldshaper, int nChakra) +{ + int nTest = GetLocalInt(oMeldshaper, "UsedBladeMeld"+IntToString(nChakra)); + + //if (DEBUG) DoDebug("GetIsBlademeldUsed is "+IntToString(nTest)); + return nTest; +} + +void SetBlademeldUsed(object oMeldshaper, int nChakra) +{ + if (!GetIsBlademeldUsed(oMeldshaper, nChakra)) + { + SetLocalInt(oMeldshaper, "UsedBladeMeld"+IntToString(nChakra), nChakra); + } +} + +string GetBlademeldDesc(int nChakra) +{ + string sString; + if (nChakra == CHAKRA_CROWN ) sString = "You gain a +4 bonus on disarm checks and on Sense Motive"; + if (nChakra == CHAKRA_FEET ) sString = "You gain a +2 bonus on Initiative"; + if (nChakra == CHAKRA_HANDS ) sString = "You gain a +1 bonus on damage"; + if (nChakra == CHAKRA_ARMS ) sString = "You gain a +2 bonus on attack rolls"; + if (nChakra == CHAKRA_BROW ) sString = "You gain the Blind-Fight feat"; + if (nChakra == CHAKRA_SHOULDERS) sString = "You gain immunity to criticals but not sneak attacks"; + if (nChakra == CHAKRA_THROAT ) sString = "At will as a standard action, each enemy within 60 feet who can hear you shout must save or become shaken for 1 round (Will DC 10 + incarnum blade level + Con modifier)."; + if (nChakra == CHAKRA_WAIST ) sString = "You gain a +10 bonus on checks made to avoid being bull rushed, grappled, tripped, or overrun. You also gain Uncanny Dodge"; + if (nChakra == CHAKRA_HEART ) sString = "You gain temporary hit points equal to twice your character level (maximum +40)"; + if (nChakra == CHAKRA_SOUL ) sString = "You break DR up to +3, and deal 1d6 damage to creatures of one opposed alignment"; + + return sString; +} + +int ChakraToBlademeld(int nChakra) +{ + int nMeld; + if (nChakra == CHAKRA_CROWN ) nMeld = MELD_BLADEMELD_CROWN; + if (nChakra == CHAKRA_FEET ) nMeld = MELD_BLADEMELD_FEET; + if (nChakra == CHAKRA_HANDS ) nMeld = MELD_BLADEMELD_HANDS; + if (nChakra == CHAKRA_ARMS ) nMeld = MELD_BLADEMELD_ARMS; + if (nChakra == CHAKRA_BROW ) nMeld = MELD_BLADEMELD_BROW; + if (nChakra == CHAKRA_SHOULDERS) nMeld = MELD_BLADEMELD_SHOULDERS; + if (nChakra == CHAKRA_THROAT ) nMeld = MELD_BLADEMELD_THROAT; + if (nChakra == CHAKRA_WAIST ) nMeld = MELD_BLADEMELD_WAIST; + if (nChakra == CHAKRA_HEART ) nMeld = MELD_BLADEMELD_HEART; + if (nChakra == CHAKRA_SOUL ) nMeld = MELD_BLADEMELD_SOUL; + + return nMeld; +} \ No newline at end of file diff --git a/src/include/moi_meld_const.nss b/src/include/moi_meld_const.nss new file mode 100644 index 0000000..6f5c005 --- /dev/null +++ b/src/include/moi_meld_const.nss @@ -0,0 +1,338 @@ +// Chakra Constants +const int CHAKRA_CROWN = 1; +const int CHAKRA_FEET = 2; +const int CHAKRA_HANDS = 3; +const int CHAKRA_ARMS = 4; +const int CHAKRA_BROW = 5; +const int CHAKRA_SHOULDERS = 6; +const int CHAKRA_THROAT = 7; +const int CHAKRA_WAIST = 8; +const int CHAKRA_HEART = 9; +const int CHAKRA_SOUL = 10; +const int CHAKRA_TOTEM = 11; + +const int CHAKRA_DOUBLE_CROWN = 12; +const int CHAKRA_DOUBLE_FEET = 13; +const int CHAKRA_DOUBLE_HANDS = 14; +const int CHAKRA_DOUBLE_ARMS = 15; +const int CHAKRA_DOUBLE_BROW = 16; +const int CHAKRA_DOUBLE_SHOULDERS = 17; +const int CHAKRA_DOUBLE_THROAT = 18; +const int CHAKRA_DOUBLE_WAIST = 19; +const int CHAKRA_DOUBLE_HEART = 20; +const int CHAKRA_DOUBLE_SOUL = 21; +const int CHAKRA_DOUBLE_TOTEM = 22; + +// Meld SpellId Constants +const int MELD_ACROBAT_BOOTS = 18701; +const int MELD_ADAMANT_PAULDRONS = 18702; +const int MELD_CRYSTAL_HELM = 18703; +const int MELD_ANKHEG_BREASTPLATE = 18704; +const int MELD_APPARITION_RIBBON = 18705; +const int MELD_ARCANE_FOCUS = 18706; +const int MELD_ARMGUARDS_OF_DISRUPTION = 18707; +const int MELD_BASILISK_MASK = 18708; +const int MELD_BEAST_TAMER_CIRCLET = 18709; +const int MELD_BEHIR_GORGET = 18710; +const int MELD_BLINK_SHIRT = 18711; +const int MELD_BLOODTALONS = 18712; +const int MELD_BLOODWAR_GAUNTLETS = 18713; +const int MELD_BLUESTEEL_BRACERS = 18714; +const int MELD_BRASS_MANE = 18715; +const int MELD_CERULEAN_SANDALS = 18716; +const int MELD_DIADEM_OF_PURELIGHT = 18717; +const int MELD_DISENCHANTER_MASK = 18718; +const int MELD_DISPLACER_MANTLE = 18719; +const int MELD_DISSOLVING_SPITTLE = 18720; +const int MELD_DREAD_CARAPACE = 18721; +const int MELD_ENIGMA_HELM = 18722; +const int MELD_FEARSOME_MASK = 18723; +const int MELD_FELLMIST_ROBE = 18724; +const int MELD_FLAME_CINCTURE = 18725; +const int MELD_FROST_HELM = 18726; +const int MELD_GIRALLON_ARMS = 18727; +const int MELD_GLOVES_OF_THE_POISONED_SOUL = 18728; +const int MELD_GORGON_MASK = 18729; +const int MELD_GREAT_RAPTOR_MASK = 18730; +const int MELD_HEART_OF_FIRE = 18731; +const int MELD_HUNTERS_CIRCLET = 18732; +const int MELD_ILLUSION_VEIL = 18733; +const int MELD_IMPULSE_BOOTS = 18734; +const int MELD_INCARNATE_AVATAR = 18735; +const int MELD_INCARNATE_WEAPON = 18736; +const int MELD_KEENEYE_LENSES = 18737; +const int MELD_KRAKEN_MANTLE = 18738; +const int MELD_KRENSHAR_MASK = 18739; +const int MELD_KRUTHIK_CLAWS = 18740; +const int MELD_LAMIA_BELT = 18741; +const int MELD_LAMMASU_MANTLE = 18742; +const int MELD_LANDSHARK_BOOTS = 18743; +const int MELD_LIFEBOND_VESTMENTS = 18744; +const int MELD_LIGHTNING_GAUNTLETS = 18745; +const int MELD_LUCKY_DICE = 18746; +const int MELD_MAGES_SPECTACLES = 18747; +const int MELD_MANTICORE_BELT = 18748; +const int MELD_MANTLE_OF_FLAME = 18749; +const int MELD_MAULING_GAUNTLETS = 18750; +const int MELD_NECROCARNUM_CIRCLET = 18751; +const int MELD_NECROCARNUM_MANTLE = 18752; +const int MELD_NECROCARNUM_SHROUD = 18753; +const int MELD_NECROCARNUM_TOUCH = 18754; +const int MELD_NECROCARNUM_VESTMENTS = 18755; +const int MELD_NECROCARNUM_WEAPON = 18756; +const int MELD_PAULDRONS_OF_HEALTH = 18757; +const int MELD_PEGASUS_CLOAK = 18758; +const int MELD_PHASE_CLOAK = 18759; +const int MELD_PHOENIX_BELT = 18760; +const int MELD_PLANAR_CHASUBLE = 18761; +const int MELD_PLANAR_WARD = 18762; +const int MELD_RAGECLAWS = 18763; +const int MELD_RIDING_BRACERS = 18764; +const int MELD_SAILORS_BRACERS = 18765; +const int MELD_SHADOW_MANTLE = 18766; +const int MELD_SHEDU_CROWN = 18767; +const int MELD_SIGHTING_GLOVES = 18768; +const int MELD_SILVERTONGUE_MASK = 18769; +const int MELD_SOULSPARK_FAMILIAR = 18770; +const int MELD_SOULSPEAKER_CIRCLET = 18771; +const int MELD_SPELLWARD_SHIRT = 18772; +const int MELD_SPHINX_CLAWS = 18773; +const int MELD_STRONGHEART_VEST = 18774; +const int MELD_THEFT_GLOVES = 18775; +const int MELD_THERAPEUTIC_MANTLE = 18776; +const int MELD_THREEFOLD_MASK_OF_THE_CHIMERA = 18777; +const int MELD_THUNDERSTEP_BOOTS = 18778; +const int MELD_TOTEM_AVATAR = 18779; +const int MELD_TRUTHSEEKER_GOGGLES = 18780; +const int MELD_UNICORN_HORN = 18781; +const int MELD_URSKAN_GREAVES = 18782; +const int MELD_VITALITY_BELT = 18783; +const int MELD_WIND_CLOAK = 18784; +const int MELD_WINTER_MASK = 18785; +const int MELD_WORG_PELT = 18786; +const int MELD_WORMTAIL_BELT = 18787; +const int MELD_YRTHAK_MASK = 18788; +const int MELD_ASTRAL_VAMBRACES = 18789; +const int MELD_CHARMING_VEIL = 18790; +const int MELD_PSIONS_EYES = 18791; +const int MELD_PSIONKILLER_MASK = 18792; +const int MELD_PSYCHIC_FOCUS = 18793; +const int MELD_CLAW_OF_THE_WYRM = 18794; +const int MELD_DRAGON_MANTLE = 18795; +const int MELD_DRAGON_TAIL = 18796; +const int MELD_DRAGONFIRE_MASK = 18797; +const int MELD_ELDER_SPIRIT = 18798; + +// Meld IPFeat Constants +const int IP_CONST_MELD_ACROBAT_BOOTS = 13550; +const int IP_CONST_MELD_ADAMANT_PAULDRONS = 13551; +const int IP_CONST_MELD_CRYSTAL_HELM = 13552; +const int IP_CONST_MELD_ANKHEG_BREASTPLATE = 13553; +const int IP_CONST_MELD_APPARITION_RIBBON = 13554; +const int IP_CONST_MELD_ARCANE_FOCUS = 13555; +const int IP_CONST_MELD_ARMGUARDS_OF_DISRUPTION = 13556; +const int IP_CONST_MELD_BASILISK_MASK = 13557; +const int IP_CONST_MELD_BEAST_TAMER_CIRCLET = 13558; +const int IP_CONST_MELD_BEHIR_GORGET = 13559; +const int IP_CONST_MELD_BLINK_SHIRT = 13560; +const int IP_CONST_MELD_BLOODTALONS = 13561; +const int IP_CONST_MELD_BLOODWAR_GAUNTLETS = 13562; +const int IP_CONST_MELD_BLUESTEEL_BRACERS = 13563; +const int IP_CONST_MELD_BRASS_MANE = 13564; +const int IP_CONST_MELD_CERULEAN_SANDALS = 13565; +const int IP_CONST_MELD_DIADEM_OF_PURELIGHT = 13566; +const int IP_CONST_MELD_DISENCHANTER_MASK = 13567; +const int IP_CONST_MELD_DISPLACER_MANTLE = 13568; +const int IP_CONST_MELD_DISSOLVING_SPITTLE = 13569; +const int IP_CONST_MELD_DREAD_CARAPACE = 13570; +const int IP_CONST_MELD_ENIGMA_HELM = 13571; +const int IP_CONST_MELD_FEARSOME_MASK = 13572; +const int IP_CONST_MELD_FELLMIST_ROBE = 13573; +const int IP_CONST_MELD_FLAME_CINCTURE = 13574; +const int IP_CONST_MELD_FROST_HELM = 13575; +const int IP_CONST_MELD_GIRALLON_ARMS = 13576; +const int IP_CONST_MELD_GLOVES_OF_THE_POISONED_SOUL = 13577; +const int IP_CONST_MELD_GORGON_MASK = 13578; +const int IP_CONST_MELD_GREAT_RAPTOR_MASK = 13579; +const int IP_CONST_MELD_HEART_OF_FIRE = 13580; +const int IP_CONST_MELD_HUNTERS_CIRCLET = 13581; +const int IP_CONST_MELD_ILLUSION_VEIL = 13582; +const int IP_CONST_MELD_IMPULSE_BOOTS = 13583; +const int IP_CONST_MELD_INCARNATE_AVATAR = 13584; +const int IP_CONST_MELD_INCARNATE_WEAPON = 13585; +const int IP_CONST_MELD_KEENEYE_LENSES = 13586; +const int IP_CONST_MELD_KRAKEN_MANTLE = 13587; +const int IP_CONST_MELD_KRENSHAR_MASK = 13588; +const int IP_CONST_MELD_KRUTHIK_CLAWS = 13589; +const int IP_CONST_MELD_LAMIA_BELT = 13590; +const int IP_CONST_MELD_LAMMASU_MANTLE = 13591; +const int IP_CONST_MELD_LANDSHARK_BOOTS = 13592; +const int IP_CONST_MELD_LIFEBOND_VESTMENTS = 13593; +const int IP_CONST_MELD_LIGHTNING_GAUNTLETS = 13594; +const int IP_CONST_MELD_LUCKY_DICE = 13595; +const int IP_CONST_MELD_MAGES_SPECTACLES = 13596; +const int IP_CONST_MELD_MANTICORE_BELT = 13597; +const int IP_CONST_MELD_MANTLE_OF_FLAME = 13598; +const int IP_CONST_MELD_MAULING_GAUNTLETS = 13599; +const int IP_CONST_MELD_NECROCARNUM_CIRCLET = 13600; +const int IP_CONST_MELD_NECROCARNUM_MANTLE = 13601; +const int IP_CONST_MELD_NECROCARNUM_SHROUD = 13602; +const int IP_CONST_MELD_NECROCARNUM_TOUCH = 13603; +const int IP_CONST_MELD_NECROCARNUM_VESTMENTS = 13604; +const int IP_CONST_MELD_NECROCARNUM_WEAPON = 13605; +const int IP_CONST_MELD_PAULDRONS_OF_HEALTH = 13606; +const int IP_CONST_MELD_PEGASUS_CLOAK = 13607; +const int IP_CONST_MELD_PHASE_CLOAK = 13608; +const int IP_CONST_MELD_PHOENIX_BELT = 13609; +const int IP_CONST_MELD_PLANAR_CHASUBLE = 13610; +const int IP_CONST_MELD_PLANAR_WARD = 13611; +const int IP_CONST_MELD_RAGECLAWS = 13612; +const int IP_CONST_MELD_RIDING_BRACERS = 13613; +const int IP_CONST_MELD_SAILORS_BRACERS = 13614; +const int IP_CONST_MELD_SHADOW_MANTLE = 13615; +const int IP_CONST_MELD_SHEDU_CROWN = 13616; +const int IP_CONST_MELD_SIGHTING_GLOVES = 13617; +const int IP_CONST_MELD_SILVERTONGUE_MASK = 13618; +const int IP_CONST_MELD_SOULSPARK_FAMILIAR = 13619; +const int IP_CONST_MELD_SOULSPEAKER_CIRCLET = 13620; +const int IP_CONST_MELD_SPELLWARD_SHIRT = 13621; +const int IP_CONST_MELD_SPHINX_CLAWS = 13622; +const int IP_CONST_MELD_STRONGHEART_VEST = 13623; +const int IP_CONST_MELD_THEFT_GLOVES = 13624; +const int IP_CONST_MELD_THERAPEUTIC_MANTLE = 13625; +const int IP_CONST_MELD_THREEFOLD_MASK_OF_THE_CHIMERA = 13626; +const int IP_CONST_MELD_THUNDERSTEP_BOOTS = 13627; +const int IP_CONST_MELD_TOTEM_AVATAR = 13628; +const int IP_CONST_MELD_TRUTHSEEKER_GOGGLES = 13629; +const int IP_CONST_MELD_UNICORN_HORN = 13630; +const int IP_CONST_MELD_URSKAN_GREAVES = 13631; +const int IP_CONST_MELD_VITALITY_BELT = 13632; +const int IP_CONST_MELD_WIND_CLOAK = 13633; +const int IP_CONST_MELD_WINTER_MASK = 13634; +const int IP_CONST_MELD_WORG_PELT = 13635; +const int IP_CONST_MELD_WORMTAIL_BELT = 13636; +const int IP_CONST_MELD_YRTHAK_MASK = 13637; +const int IP_CONST_MELD_ASTRAL_VAMBRACES = 13638; +const int IP_CONST_MELD_CHARMING_VEIL = 13639; +const int IP_CONST_MELD_PSIONS_EYES = 13640; +const int IP_CONST_MELD_PSIONKILLER_MASK = 13641; +const int IP_CONST_MELD_PSYCHIC_FOCUS = 13642; +const int IP_CONST_MELD_CLAW_OF_THE_WYRM = 13643; +const int IP_CONST_MELD_DRAGON_MANTLE = 13644; +const int IP_CONST_MELD_DRAGON_TAIL = 13645; +const int IP_CONST_MELD_DRAGONFIRE_MASK = 13646; +const int IP_CONST_MELD_ELDER_SPIRIT = 13647; + +// Bind IPFeat Constants +const int IP_CONST_MELD_ANKHEG_BREASTPLATE_THROAT = 13650; +const int IP_CONST_MELD_APPARITION_RIBBON_THROAT = 13651; +const int IP_CONST_MELD_ARMGUARDS_OF_DISRUPTION_ARMS = 13652; +const int IP_CONST_MELD_BASILISK_MASK_TOTEM = 13653; +const int IP_CONST_MELD_BEAST_TAMER_CIRCLET_CROWN = 13654; +const int IP_CONST_MELD_BEAST_TAMER_CIRCLET_TOTEM = 13655; +const int IP_CONST_MELD_BEHIR_GORGET_THROAT = 13656; +const int IP_CONST_MELD_BLINK_SHIRT_DOOR_ST = 13657; +const int IP_CONST_MELD_BLINK_SHIRT_HEART = 13658; +const int IP_CONST_MELD_BLINK_SHIRT_DOOR_MV = 13659; +const int IP_CONST_MELD_BLOODWAR_GAUNTLETS_ARMS = 13660; +const int IP_CONST_MELD_BRASS_MANE_THROAT = 13661; +const int IP_CONST_MELD_CERULEAN_SANDALS_FEET = 13662; +const int IP_CONST_MELD_DISENCHANTER_MASK_DETECT = 13663; +const int IP_CONST_MELD_DISENCHANTER_MASK_TOTEM = 13664; +const int IP_CONST_MELD_DISPLACER_MANTLE_TOTEM = 13665; +const int IP_CONST_MELD_DISSOLVING_SPITTLE_SPIT = 13666; +const int IP_CONST_MELD_DREAD_CARAPACE_FEET = 13667; +const int IP_CONST_MELD_FLAME_CINCTURE_WAIST = 13668; +const int IP_CONST_MELD_FROST_HELM_CROWN = 13669; +const int IP_CONST_MELD_FROST_HELM_TOTEM = 13670; +const int IP_CONST_MELD_GLOVES_OF_THE_POISONED_SOUL_HANDS = 13671; +const int IP_CONST_MELD_GORGON_MASK_THROAT = 13672; +const int IP_CONST_MELD_GORGON_MASK_TOTEM = 13673; +const int IP_CONST_MELD_INCARNATE_WEAPON_ARMS = 13674; +const int IP_CONST_MELD_KRENSHAR_MASK_TOTEM = 13675; +const int IP_CONST_MELD_LAMMASU_MANTLE_TOTEM = 13676; +const int IP_CONST_MELD_LANDSHARK_BOOTS_FEET = 13677; +const int IP_CONST_MELD_LANDSHARK_BOOTS_TOTEM = 13678; +const int IP_CONST_MELD_LIFEBOND_VESTMENTS_HEAL = 13679; +const int IP_CONST_MELD_LIFEBOND_VESTMENTS_ARMS = 13680; +const int IP_CONST_MELD_LIGHTNING_GAUNTLETS_ZAP = 13681; +const int IP_CONST_MELD_LIGHTNING_GAUNTLETS_HANDS = 13682; +const int IP_CONST_MELD_LUCKY_DICE_LUCK = 13683; +const int IP_CONST_MELD_MAGES_SPECTACLES_BROW = 13684; +const int IP_CONST_MELD_MANTICORE_TOTEM = 13685; +const int IP_CONST_MELD_MANTLE_OF_FLAME_SHOULDERS = 13686; +const int IP_CONST_MELD_PHOENIX_BELT_TOTEM = 13687; +const int IP_CONST_MELD_PLANAR_CHASUBLE_SOUL = 13688; +const int IP_CONST_MELD_SHADOW_MANTLE_SHOULDERS = 13689; +const int IP_CONST_MELD_SHEDU_CROWN_HEART = 13690; +const int IP_CONST_MELD_SILVERTONGUE_MASK_THROAT = 13691; +const int IP_CONST_MELD_UNICORN_HORN_BROW = 13692; +const int IP_CONST_MELD_WINTER_MASK_TOUCH = 13693; +const int IP_CONST_MELD_WINTER_MASK_THROAT = 13694; +const int IP_CONST_MELD_WORMTAIL_BELT_TOTEM = 13695; +const int IP_CONST_MELD_YRTHAK_MASK_BROW = 13696; +const int IP_CONST_MELD_YRTHAK_MASK_TOTEM = 13697; +const int IP_CONST_MELD_NECRO_CIRCLET_BROW = 13698; +const int IP_CONST_MELD_NECROCARNUM_SHROUD_SOUL = 13699; +const int IP_CONST_MELD_NECROCARNUM_TOUCH_ESS = 13700; +const int IP_CONST_MELD_NECROCARNUM_TOUCH_ARMS = 13701; +const int IP_CONST_MELD_SOULSPARK_FAMILIAR_ESS = 13702; +const int IP_CONST_MELD_BLADEMELD_SHOUT = 13703; +const int IP_CONST_MELD_PSIONS_EYES_BROW = 13704; +const int IP_CONST_MELD_PSIONKILLER_MASK_DETECT = 13705; +const int IP_CONST_MELD_PSIONKILLER_MASK_TOTEM = 13706; +const int IP_CONST_MELD_DRAGON_TAIL_SLAM = 13707; +const int IP_CONST_MELD_DRAGON_TAIL_SWEEP = 13708; +const int IP_CONST_MELD_DRAGONFIRE_MASK_THROAT = 13709; +const int IP_CONST_MELD_DRAGONFIRE_MASK_TOTEM = 13710; + +// Radial SubSpell Constants +const int MELD_LUCKY_DICE_ATTACK = 18935; +const int MELD_LUCKY_DICE_SAVING_THROWS = 18936; +const int MELD_LUCKY_DICE_SKILLS = 18937; +const int MELD_SHADOW_MANTLE_SHOULDERS = 18943; + +// Class Spell Constants +const int MELD_INCARNUM_RADIANCE = 18698; +const int MELD_SPINE_ENHANCEMENT = 18691; +const int MELD_IRONSOUL_SHIELD = 18687; +const int MELD_IRONSOUL_ARMOR = 18685; +const int MELD_IRONSOUL_WEAPON = 18683; +const int MELD_UMBRAL_STEP = 18681; +const int MELD_UMBRAL_SHADOW = 18679; +const int MELD_UMBRAL_SIGHT = 18677; +const int MELD_UMBRAL_SOUL = 18675; +const int MELD_UMBRAL_KISS = 18673; +const int MELD_INCANDESCENT_STRIKE = 18670; +const int MELD_INCANDESCENT_HEAL = 18668; +const int MELD_INCANDESCENT_COUNTENANCE = 18666; +const int MELD_INCANDESCENT_RAY = 18663; +const int MELD_INCANDESCENT_AURA = 18659; +const int MELD_BLADEMELD_CROWN = 18647; +const int MELD_BLADEMELD_FEET = 18648; +const int MELD_BLADEMELD_HANDS = 18649; +const int MELD_BLADEMELD_ARMS = 18650; +const int MELD_BLADEMELD_BROW = 18651; +const int MELD_BLADEMELD_SHOULDERS = 18652; +const int MELD_BLADEMELD_THROAT = 18653; +const int MELD_BLADEMELD_WAIST = 18654; +const int MELD_BLADEMELD_HEART = 18655; +const int MELD_BLADEMELD_SOUL = 18656; +const int MELD_WITCH_MELDSHIELD = 18634; +const int MELD_WITCH_DISPEL = 18636; +const int MELD_WITCH_SHACKLES = 18638; +const int MELD_WITCH_ABROGATION = 18640; +const int MELD_WITCH_SPIRITFLAY = 18642; +const int MELD_WITCH_INTEGUMENT = 18644; + +// Race Spell Constants +const int MELD_DUSKLING_SPEED = 18973; + +// Feat Spell Constants +const int MELD_OPEN_SOUL_CHAKRA = 18632; + +// Bind spell Constants +const int MELD_DRAGON_TAIL_SWEEP = 18962; + +// Essentia Conv +const int IP_CONST_FEAT_INVEST_ESSENTIA_CONV = 13549; \ No newline at end of file diff --git a/src/include/nw_inc_nui.nss b/src/include/nw_inc_nui.nss new file mode 100644 index 0000000..96fc3da --- /dev/null +++ b/src/include/nw_inc_nui.nss @@ -0,0 +1,1193 @@ +const int NUI_DIRECTION_HORIZONTAL = 0; +const int NUI_DIRECTION_VERTICAL = 1; + +const int NUI_MOUSE_BUTTON_LEFT = 0; +const int NUI_MOUSE_BUTTON_MIDDLE = 1; +const int NUI_MOUSE_BUTTON_RIGHT = 2; + +const int NUI_SCROLLBARS_NONE = 0; +const int NUI_SCROLLBARS_X = 1; +const int NUI_SCROLLBARS_Y = 2; +const int NUI_SCROLLBARS_BOTH = 3; +const int NUI_SCROLLBARS_AUTO = 4; + +const int NUI_ASPECT_FIT = 0; +const int NUI_ASPECT_FILL = 1; +const int NUI_ASPECT_FIT100 = 2; +const int NUI_ASPECT_EXACT = 3; +const int NUI_ASPECT_EXACTSCALED = 4; +const int NUI_ASPECT_STRETCH = 5; + +const int NUI_HALIGN_CENTER = 0; +const int NUI_HALIGN_LEFT = 1; +const int NUI_HALIGN_RIGHT = 2; + +const int NUI_VALIGN_MIDDLE = 0; +const int NUI_VALIGN_TOP = 1; +const int NUI_VALIGN_BOTTOM = 2; + +// ----------------------- +// Style + +const float NUI_STYLE_PRIMARY_WIDTH = 150.0; +const float NUI_STYLE_PRIMARY_HEIGHT = 50.0; + +const float NUI_STYLE_SECONDARY_WIDTH = 150.0; +const float NUI_STYLE_SECONDARY_HEIGHT = 35.0; + +const float NUI_STYLE_TERTIARY_WIDTH = 100.0; +const float NUI_STYLE_TERTIARY_HEIGHT = 30.0; + +const float NUI_STYLE_ROW_HEIGHT = 25.0; + +// ----------------------- +// Window + +// Special cases: +// * Set the window title to JsonBool(FALSE), Collapse to JsonBool(FALSE) and bClosable to FALSE +// to hide the title bar. +// Note: You MUST provide a way to close the window some other way, or the user will be stuck with it. +json // Window +NuiWindow( + json jRoot, // Layout-ish (NuiRow, NuiCol, NuiGroup) + json jTitle, // Bind:String + json jGeometry, // Bind:Rect Set x and/or y to -1.0 to center the window on that axis + // Set x and/or y to -2.0 to position the window's top left at the mouse cursor's position of that axis + // Set x and/or y to -3.0 to center the window on the mouse cursor's position of that axis + json jResizable, // Bind:Bool Set to JsonBool(TRUE) or JsonNull() to let user resize without binding. + json jCollapsed, // Bind:Bool Set to a static value JsonBool(FALSE) to disable collapsing. + // Set to JsonNull() to let user collapse without binding. + // For better UX, leave collapsing on. + json jClosable, // Bind:Bool You must provide a way to close the window if you set this to FALSE. + // For better UX, handle the window "closed" event. + json jTransparent, // Bind:Bool Do not render background + json jBorder, // Bind:Bool Do not render border + json jAcceptsInput = // Bind:Bool Set JsonBool(FALSE) to disable all input. + JSON_TRUE // All hover, clicks and keypresses will fall through. +); + +// ----------------------- +// Values + +// Create a dynamic bind. Unlike static values, these can change at runtime: +// NuiBind("mybindlabel"); +// NuiSetBind(.., "mybindlabel", JsonString("hi")); +// To create static values, just use the json types directly: +// JsonString("hi"); +json // Bind +NuiBind( + string sId +); + +// Tag the given element with a id. +// Only tagged elements will send events to the server. +json // Element +NuiId( + json jElem, // Element + string sId // String +); + +// A shim/helper that can be used to render or bind a strref where otherwise +// a string value would go. +json +NuiStrRef( + int nStrRef // STRREF +); + +// ----------------------- +// Layout + +// A column will auto-space all elements inside of it and advise the parent +// about it's desired size. +json // Layout +NuiCol( + json jList // Layout[] or Element[] +); + +// A row will auto-space all elements inside of it and advise the parent +// about it's desired size. +json // Layout +NuiRow( + json jList // Layout[] or Element[] +); + +// A group, usually with a border and some padding, holding a single element. Can scroll. +// Will not advise parent of size, so you need to let it fill a span (col/row) as if it was +// a element. +json // Layout +NuiGroup( + json jChild, // Layout or Element + int bBorder = TRUE, + int nScroll = NUI_SCROLLBARS_AUTO +); + +// Modifiers/Attributes: These are all static and cannot be bound, since the UI system +// cannot easily reflow once the layout is set up. You need to swap the layout if you +// want to change element geometry. + +json // Element +NuiWidth( + json jElem, // Element + float fWidth // Float: Element width in pixels (strength=required). +); + +json // Element +NuiHeight( + json jElem, // Element + float fHeight // Float: Height in pixels (strength=required). +); + +json // Element +NuiAspect( + json jElem, // Element + float fAspect // Float: Ratio of x/y. +); + +// Set a margin on the widget. The margin is the spacing outside of the widget. +json // Element +NuiMargin( + json jElem, // Element + float fMargin // Float +); + +// Set padding on the widget. The margin is the spacing inside of the widget. +json // Element +NuiPadding( + json jElem, // Element + float fPadding // Float +); + +// Disabled elements are non-interactive and greyed out. +json // Element +NuiEnabled( + json jElem, // Element + json jEnabler // Bind:Bool +); + +// Invisible elements do not render at all, but still take up layout space. +json // Element +NuiVisible( + json jElem, // Element + json jVisible // Bind:Bool +); + +// Tooltips show on mouse hover. +json // Element +NuiTooltip( + json jElem, // Element + json jTooltip // Bind:String +); + +// Tooltips for disabled elements show on mouse hover. +json // Element +NuiDisabledTooltip( + json jElem, // Element + json jTooltip // Bind:String +); + +// Encouraged elements have a breathing animated glow inside of it. +json // Element +NuiEncouraged( + json jElem, // Element + json jEncouraged // Bind:Bool +); + +// ----------------------- +// Props & Style + +json // Vec2 +NuiVec(float x, float y); + +json // Rect +NuiRect(float x, float y, float w, float h); + +json // Color +NuiColor(int r, int g, int b, int a = 255); + +// Style the foreground color of the widget. This is dependent on the widget +// in question and only supports solid/full colors right now (no texture skinning). +// For example, labels would style their text color; progress bars would style the bar. +json // Element +NuiStyleForegroundColor( + json jElem, // Element + json jColor // Bind:Color +); + +// ----------------------- +// Widgets + +// A special widget that just takes up layout space. +// If you add multiple spacers to a span, they will try to size equally. +// e.g.: [ ] will try to center the button. +json // Element +NuiSpacer(); + +// Create a label field. Labels are single-line stylable non-editable text fields. +json // Element +NuiLabel( + json jValue, // Bind:String + json jHAlign, // Bind:Int:NUI_HALIGN_* + json jVAlign // Bind:Int:NUI_VALIGN_* +); + +// Create a non-editable text field. Note: This text field internally implies a NuiGroup wrapped +// around it, which is providing the optional border and scrollbars. +json // Element +NuiText( + json jValue, // Bind:String + int bBorder = TRUE, // Bool + int nScroll = NUI_SCROLLBARS_AUTO // Int:NUI_SCROLLBARS_* +); + +// A clickable button with text as the label. +// Sends "click" events on click. +json // Element +NuiButton( + json jLabel // Bind:String +); + +// A clickable button with an image as the label. +// Sends "click" events on click. +json // Element +NuiButtonImage( + json jResRef // Bind:ResRef +); + +// A clickable button with text as the label. +// Same as the normal button, but this one is a toggle. +// Sends "click" events on click. +json // Element +NuiButtonSelect( + json jLabel, // Bind:String + json jValue // Bind:Bool +); + +// A checkbox with a label to the right of it. +json // Element +NuiCheck( + json jLabel, // Bind:String + json jBool // Bind:Bool +); + +// A image, with no border or padding. +json // Element +NuiImage( + json jResRef, // Bind:ResRef + json jAspect, // Bind:Int:NUI_ASPECT_* + json jHAlign, // Bind:Int:NUI_HALIGN_* + json jVAlign // Bind:Int:NUI_VALIGN_* +); + +// Optionally render only subregion of jImage. +// jRegion is a NuiRect (x, y, w, h) to indicate the render region inside the image. +json // NuiImage +NuiImageRegion( + json jImage, // NuiImage + json jRegion // Bind:NuiRect +); + +// A combobox/dropdown. +json // Element +NuiCombo( + json jElements, // Bind:ComboEntry[] + json jSelected // Bind:Int (index into jElements) +); + +json // ComboEntry +NuiComboEntry( + string sLabel, + int nValue +); + +// A floating-point slider. A good step size for normal-sized sliders is 0.01. +json // Element +NuiSliderFloat( + json jValue, // Bind:Float + json jMin, // Bind:Float + json jMax, // Bind:Float + json jStepSize // Bind:Float +); + +// A integer/discrete slider. +json // Element +NuiSlider( + json jValue, // Bind:Int + json jMin, // Bind:Int + json jMax, // Bind:Int + json jStepSize // Bind:Int +); + +// A progress bar. Progress is always from 0.0 to 1.0. +json // Element +NuiProgress( + json jValue // Bind:Float (0.0->1.0) +); + +// A editable text field. +json // Element +NuiTextEdit( + json jPlaceholder, // Bind:String + json jValue, // Bind:String + int nMaxLength, // UInt >= 1, <= 65535 + int bMultiline, // Bool + int bWordWrap = TRUE // Bool +); + +// Creates a list view of elements. +// jTemplate needs to be an array of NuiListTemplateCell instances. +// All binds referenced in jTemplate should be arrays of rRowCount size; +// e.g. when rendering a NuiLabel(), the bound label String should be an array of strings. +// You can pass in one of the template jRowCount into jSize as a convenience. The array +// size will be uses as the Int bind. +// jRowHeight defines the height of the rendered rows. +json // Element +NuiList( + json jTemplate, // NuiListTemplateCell[] (max: 16) + json jRowCount, // Bind:Int + float fRowHeight = NUI_STYLE_ROW_HEIGHT, + int bBorder = TRUE, + int nScroll = NUI_SCROLLBARS_Y // Note: Cannot be AUTO. +); + +json // NuiListTemplateCell +NuiListTemplateCell( + json jElem, // Element + float fWidth, // Float:0 = auto, >1 = pixel width + int bVariable // Bool:Cell can grow if space is available; otherwise static +); + +// A simple color picker, with no border or spacing. +json // Element +NuiColorPicker( + json jColor // Bind:Color +); + +// A list of options (radio buttons). Only one can be selected +// at a time. jValue is updated every time a different element is +// selected. The special value -1 means "nothing". +json // Element +NuiOptions( + int nDirection, // NUI_DIRECTION_* + json jElements, // JsonArray of string labels + json jValue // Bind:Int +); + +// A group of buttons. Only one can be selected at a time. jValue +// is updated every time a different button is selected. The special +// value -1 means "nothing". +json // Element +NuiToggles( + int nDirection, // NUI_DIRECTION_* + json jElements, // JsonArray of string labels + json jValue // Bind:Int +); + +const int NUI_CHART_TYPE_LINES = 0; +const int NUI_CHART_TYPE_COLUMN = 1; + +json // NuiChartSlot +NuiChartSlot( + int nType, // Int:NUI_CHART_TYPE_* + json jLegend, // Bind:String + json jColor, // Bind:NuiColor + json jData // Bind:Float[] +); + +// Renders a chart. +// Currently, min and max values are determined automatically and +// cannot be influenced. +json // Element +NuiChart( + json jSlots // NuiChartSlot[] +); + +// ----------------------- +// Draw Lists + +// Draw lists are raw painting primitives on top of widgets. +// They are anchored to the widget x/y coordinates, and are always +// painted in order of definition, without culling. You cannot bind +// the draw_list itself, but most parameters on individual draw_list +// entries can be bound. + +const int NUI_DRAW_LIST_ITEM_TYPE_POLYLINE = 0; +const int NUI_DRAW_LIST_ITEM_TYPE_CURVE = 1; +const int NUI_DRAW_LIST_ITEM_TYPE_CIRCLE = 2; +const int NUI_DRAW_LIST_ITEM_TYPE_ARC = 3; +const int NUI_DRAW_LIST_ITEM_TYPE_TEXT = 4; +const int NUI_DRAW_LIST_ITEM_TYPE_IMAGE = 5; +const int NUI_DRAW_LIST_ITEM_TYPE_LINE = 6; + +// You can order draw list items to be painted either before, or after the +// builtin render of the widget in question. This enables you to paint "behind" +// a widget. + +const int NUI_DRAW_LIST_ITEM_ORDER_BEFORE = -1; +const int NUI_DRAW_LIST_ITEM_ORDER_AFTER = 1; + +// Always render draw list item (default). +const int NUI_DRAW_LIST_ITEM_RENDER_ALWAYS = 0; +// Only render when NOT hovering. +const int NUI_DRAW_LIST_ITEM_RENDER_MOUSE_OFF = 1; +// Only render when mouse is hovering. +const int NUI_DRAW_LIST_ITEM_RENDER_MOUSE_HOVER = 2; +// Only render while LMB is held down. +const int NUI_DRAW_LIST_ITEM_RENDER_MOUSE_LEFT = 3; +// Only render while RMB is held down. +const int NUI_DRAW_LIST_ITEM_RENDER_MOUSE_RIGHT = 4; +// Only render while MMB is held down. +const int NUI_DRAW_LIST_ITEM_RENDER_MOUSE_MIDDLE = 5; + +json // DrawListItem +NuiDrawListPolyLine( + json jEnabled, // Bind:Bool + json jColor, // Bind:Color + json jFill, // Bind:Bool + json jLineThickness, // Bind:Float + json jPoints, // Bind:Float[] Always provide points in pairs + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // DrawListItem +NuiDrawListCurve( + json jEnabled, // Bind:Bool + json jColor, // Bind:Color + json jLineThickness, // Bind:Float + json jA, // Bind:Vec2 + json jB, // Bind:Vec2 + json jCtrl0, // Bind:Vec2 + json jCtrl1, // Bind:Vec2 + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // DrawListItem +NuiDrawListCircle( + json jEnabled, // Bind:Bool + json jColor, // Bind:Color + json jFill, // Bind:Bool + json jLineThickness, // Bind:Float + json jRect, // Bind:Rect + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // DrawListItem +NuiDrawListArc( + json jEnabled, // Bind:Bool + json jColor, // Bind:Color + json jFill, // Bind:Bool + json jLineThickness, // Bind:Float + json jCenter, // Bind:Vec2 + json jRadius, // Bind:Float + json jAMin, // Bind:Float + json jAMax, // Bind:Float + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // DrawListItem +NuiDrawListText( + json jEnabled, // Bind:Bool + json jColor, // Bind:Color + json jRect, // Bind:Rect + json jText, // Bind:String + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // DrawListItem +NuiDrawListImage( + json jEnabled, // Bind:Bool + json jResRef, // Bind:ResRef + json jPos, // Bind:Rect + json jAspect, // Bind:Int:NUI_ASPECT_* + json jHAlign, // Bind:Int:NUI_HALIGN_* + json jVAlign, // Bind:Int:NUI_VALIGN_* + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // DrawListItemImage +NuiDrawListImageRegion( + json jDrawListImage, // DrawListItemImage + json jRegion // Bind:NuiRect +); + +json // DrawListItem +NuiDrawListLine( + json jEnabled, // Bind:Bool + json jColor, // Bind:Color + json jLineThickness, // Bind:Float + json jA, // Bind:Vec2 + json jB, // Bind:Vec2 + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, // Int:NUI_DRAW_LIST_ITEM_ORDER_* + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS // Int:NUI_DRAW_LIST_ITEM_RENDER_* +); + +json // Element +NuiDrawList( + json jElem, // Element + json jScissor, // Bind:Bool Constrain painted elements to widget bounds. + json jList // DrawListItem[] +); + +// ----------------------- +// Implementation + +json +NuiWindow( + json jRoot, + json jTitle, + json jGeometry, + json jResizable, + json jCollapsed, + json jClosable, + json jTransparent, + json jBorder, + json jAcceptsInput +) +{ + json ret = JsonObject(); + // Currently hardcoded and here to catch backwards-incompatible data in the future. + ret = JsonObjectSet(ret, "version", JsonInt(1)); + ret = JsonObjectSet(ret, "title", jTitle); + ret = JsonObjectSet(ret, "root", jRoot); + ret = JsonObjectSet(ret, "geometry", jGeometry); + ret = JsonObjectSet(ret, "resizable", jResizable); + ret = JsonObjectSet(ret, "collapsed", jCollapsed); + ret = JsonObjectSet(ret, "closable", jClosable); + ret = JsonObjectSet(ret, "transparent", jTransparent); + ret = JsonObjectSet(ret, "border", jBorder); + ret = JsonObjectSet(ret, "accepts_input", jAcceptsInput); + return ret; +} + +json +NuiElement( + string sType, + json jLabel, + json jValue +) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "type", JsonString(sType)); + ret = JsonObjectSet(ret, "label", jLabel); + ret = JsonObjectSet(ret, "value", jValue); + return ret; +} + +json +NuiBind( + string sId +) +{ + return JsonObjectSet(JsonObject(), "bind", JsonString(sId)); +} + +json +NuiId( + json jElem, + string sId +) +{ + return JsonObjectSet(jElem, "id", JsonString(sId)); +} + +json +NuiStrRef( + int nStrRef +) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "strref", JsonInt(nStrRef)); + return ret; +} + +json +NuiCol( + json jList +) +{ + return JsonObjectSet(NuiElement("col", JsonNull(), JsonNull()), "children", jList); +} + +json +NuiRow( + json jList +) +{ + return JsonObjectSet(NuiElement("row", JsonNull(), JsonNull()), "children", jList); +} + +json +NuiGroup( + json jChild, + int bBorder = TRUE, + int nScroll = NUI_SCROLLBARS_AUTO +) +{ + json ret = NuiElement("group", JsonNull(), JsonNull()); + ret = JsonObjectSet(ret, "children", JsonArrayInsert(JsonArray(), jChild)); + ret = JsonObjectSet(ret, "border", JsonBool(bBorder)); + ret = JsonObjectSet(ret, "scrollbars", JsonInt(nScroll)); + return ret; +} + +json +NuiWidth(json jElem, float fWidth) +{ + return JsonObjectSet(jElem, "width", JsonFloat(fWidth)); +} + +json +NuiHeight(json jElem, float fHeight) +{ + return JsonObjectSet(jElem, "height", JsonFloat(fHeight)); +} + +json +NuiAspect(json jElem, float fAspect) +{ + return JsonObjectSet(jElem, "aspect", JsonFloat(fAspect)); +} + +json +NuiMargin( + json jElem, + float fMargin +) +{ + return JsonObjectSet(jElem, "margin", JsonFloat(fMargin)); +} + +json +NuiPadding( + json jElem, + float fPadding +) +{ + return JsonObjectSet(jElem, "padding", JsonFloat(fPadding)); +} + +json +NuiEnabled( + json jElem, + json jEnabler +) +{ + return JsonObjectSet(jElem, "enabled", jEnabler); +} + +json +NuiVisible( + json jElem, + json jVisible +) +{ + return JsonObjectSet(jElem, "visible", jVisible); +} + +json +NuiTooltip( + json jElem, + json jTooltip +) +{ + return JsonObjectSet(jElem, "tooltip", jTooltip); +} + +json +NuiDisabledTooltip( + json jElem, + json jTooltip +) +{ + return JsonObjectSet(jElem, "disabled_tooltip", jTooltip); +} + +json +NuiEncouraged( + json jElem, + json jEncouraged +) +{ + return JsonObjectSet(jElem, "encouraged", jEncouraged); +} + +json +NuiVec(float x, float y) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "x", JsonFloat(x)); + ret = JsonObjectSet(ret, "y", JsonFloat(y)); + return ret; +} + +json +NuiRect(float x, float y, float w, float h) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "x", JsonFloat(x)); + ret = JsonObjectSet(ret, "y", JsonFloat(y)); + ret = JsonObjectSet(ret, "w", JsonFloat(w)); + ret = JsonObjectSet(ret, "h", JsonFloat(h)); + return ret; +} + +json +NuiColor(int r, int g, int b, int a = 255) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "r", JsonInt(r)); + ret = JsonObjectSet(ret, "g", JsonInt(g)); + ret = JsonObjectSet(ret, "b", JsonInt(b)); + ret = JsonObjectSet(ret, "a", JsonInt(a)); + return ret; +} + +json +NuiStyleForegroundColor( + json jElem, + json jColor +) +{ + return JsonObjectSet(jElem, "foreground_color", jColor); +} + +json +NuiSpacer() +{ + return NuiElement("spacer", JsonNull(), JsonNull()); +} + +json +NuiLabel( + json jValue, + json jHAlign, + json jVAlign +) +{ + json ret = NuiElement("label", JsonNull(), jValue); + ret = JsonObjectSet(ret, "text_halign", jHAlign); + ret = JsonObjectSet(ret, "text_valign", jVAlign); + return ret; +} + +json +NuiText( + json jValue, + int bBorder = TRUE, + int nScroll = NUI_SCROLLBARS_AUTO +) +{ + json ret = NuiElement("text", JsonNull(), jValue); + ret = JsonObjectSet(ret, "border", JsonBool(bBorder)); + ret = JsonObjectSet(ret, "scrollbars", JsonInt(nScroll)); + return ret; +} + +json +NuiButton( + json jLabel +) +{ + return NuiElement("button", jLabel, JsonNull()); +} + +json +NuiButtonImage( + json jResRef +) +{ + return NuiElement("button_image", jResRef, JsonNull()); +} + +json +NuiButtonSelect( + json jLabel, + json jValue +) +{ + return NuiElement("button_select", jLabel, jValue); +} + +json +NuiCheck( + json jLabel, + json jBool +) +{ + return NuiElement("check", jLabel, jBool); +} + +json +NuiImage( + json jResRef, + json jAspect, + json jHAlign, + json jVAlign +) +{ + json img = NuiElement("image", JsonNull(), jResRef); + img = JsonObjectSet(img, "image_aspect", jAspect); + img = JsonObjectSet(img, "image_halign", jHAlign); + img = JsonObjectSet(img, "image_valign", jVAlign); + return img; +} + +json +NuiImageRegion( + json jImage, + json jRegion +) +{ + return JsonObjectSet(jImage, "image_region", jRegion); +} + +json +NuiCombo( + json jElements, + json jSelected +) +{ + return JsonObjectSet(NuiElement("combo", JsonNull(), jSelected), "elements", jElements); +} + +json +NuiComboEntry( + string sLabel, + int nValue +) +{ + return JsonArrayInsert(JsonArrayInsert(JsonArray(), JsonString(sLabel)), JsonInt(nValue)); +} + +json +NuiSliderFloat( + json jValue, + json jMin, + json jMax, + json jStepSize +) +{ + json ret = NuiElement("sliderf", JsonNull(), jValue); + ret = JsonObjectSet(ret, "min", jMin); + ret = JsonObjectSet(ret, "max", jMax); + ret = JsonObjectSet(ret, "step", jStepSize); + return ret; +} + +json +NuiSlider( + json jValue, + json jMin, + json jMax, + json jStepSize +) +{ + json ret = NuiElement("slider", JsonNull(), jValue); + ret = JsonObjectSet(ret, "min", jMin); + ret = JsonObjectSet(ret, "max", jMax); + ret = JsonObjectSet(ret, "step", jStepSize); + return ret; +} + +json +NuiProgress( + json jValue +) +{ + return NuiElement("progress", JsonNull(), jValue); +} + +json +NuiTextEdit( + json jPlaceholder, + json jValue, + int nMaxLength, + int bMultiline, + int bWordWrap = TRUE +) +{ + json ret = NuiElement("textedit", jPlaceholder, jValue); + ret = JsonObjectSet(ret, "max", JsonInt(nMaxLength)); + ret = JsonObjectSet(ret, "multiline", JsonBool(bMultiline)); + ret = JsonObjectSet(ret, "wordwrap", JsonBool(bWordWrap)); + return ret; +} + +json +NuiList( + json jTemplate, + json jRowCount, + float fRowHeight = NUI_STYLE_ROW_HEIGHT, + int bBorder = TRUE, + int nScroll = NUI_SCROLLBARS_Y +) +{ + json ret = NuiElement("list", JsonNull(), JsonNull()); + ret = JsonObjectSet(ret, "row_template", jTemplate); + ret = JsonObjectSet(ret, "row_count", jRowCount); + ret = JsonObjectSet(ret, "row_height", JsonFloat(fRowHeight)); + ret = JsonObjectSet(ret, "border", JsonBool(bBorder)); + ret = JsonObjectSet(ret, "scrollbars", JsonInt(nScroll)); + return ret; +} + +json +NuiListTemplateCell( + json jElem, + float fWidth, + int bVariable +) +{ + json ret = JsonArray(); + ret = JsonArrayInsert(ret, jElem); + ret = JsonArrayInsert(ret, JsonFloat(fWidth)); + ret = JsonArrayInsert(ret, JsonBool(bVariable)); + return ret; +} + +json +NuiColorPicker( + json jColor +) +{ + json ret = NuiElement("color_picker", JsonNull(), jColor); + return ret; +} + +json +NuiOptions( + int nDirection, + json jElements, + json jValue +) +{ + json ret = NuiElement("options", JsonNull(), jValue); + ret = JsonObjectSet(ret, "direction", JsonInt(nDirection)); + ret = JsonObjectSet(ret, "elements", jElements); + return ret; +} + +json +NuiToggles( + int nDirection, + json jElements, + json jValue +) +{ + json ret = NuiElement("tabbar", JsonNull(), jValue); + ret = JsonObjectSet(ret, "direction", JsonInt(nDirection)); + ret = JsonObjectSet(ret, "elements", jElements); + return ret; +} + +json +NuiChartSlot( + int nType, + json jLegend, + json jColor, + json jData +) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "type", JsonInt(nType)); + ret = JsonObjectSet(ret, "legend", jLegend); + ret = JsonObjectSet(ret, "color", jColor); + ret = JsonObjectSet(ret, "data", jData); + return ret; +} + +json +NuiChart( + json jSlots +) +{ + json ret = NuiElement("chart", JsonNull(), jSlots); + return ret; +} + +json +NuiDrawListItem( + int nType, + json jEnabled, + json jColor, + json jFill, + json jLineThickness, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = JsonObject(); + ret = JsonObjectSet(ret, "type", JsonInt(nType)); + ret = JsonObjectSet(ret, "enabled", jEnabled); + ret = JsonObjectSet(ret, "color", jColor); + ret = JsonObjectSet(ret, "fill", jFill); + ret = JsonObjectSet(ret, "line_thickness", jLineThickness); + ret = JsonObjectSet(ret, "order", JsonInt(nOrder)); + ret = JsonObjectSet(ret, "render", JsonInt(nRender)); + return ret; +} + +json +NuiDrawListPolyLine( + json jEnabled, + json jColor, + json jFill, + json jLineThickness, + json jPoints, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_POLYLINE, jEnabled, jColor, jFill, jLineThickness, nOrder, nRender); + ret = JsonObjectSet(ret, "points", jPoints); + return ret; +} + +json +NuiDrawListCurve( + json jEnabled, + json jColor, + json jLineThickness, + json jA, + json jB, + json jCtrl0, + json jCtrl1, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_CURVE, jEnabled, jColor, JsonBool(0), jLineThickness, nOrder, nRender); + ret = JsonObjectSet(ret, "a", jA); + ret = JsonObjectSet(ret, "b", jB); + ret = JsonObjectSet(ret, "ctrl0", jCtrl0); + ret = JsonObjectSet(ret, "ctrl1", jCtrl1); + return ret; +} + +json +NuiDrawListCircle( + json jEnabled, + json jColor, + json jFill, + json jLineThickness, + json jRect, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_CIRCLE, jEnabled, jColor, jFill, jLineThickness, nOrder, nRender); + ret = JsonObjectSet(ret, "rect", jRect); + return ret; +} + +json +NuiDrawListArc( + json jEnabled, + json jColor, + json jFill, + json jLineThickness, + json jCenter, + json jRadius, + json jAMin, + json jAMax, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_ARC, jEnabled, jColor, jFill, jLineThickness, nOrder, nRender); + ret = JsonObjectSet(ret, "c", jCenter); + ret = JsonObjectSet(ret, "radius", jRadius); + ret = JsonObjectSet(ret, "amin", jAMin); + ret = JsonObjectSet(ret, "amax", jAMax); + return ret; +} + +json +NuiDrawListText( + json jEnabled, + json jColor, + json jRect, + json jText, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_TEXT, jEnabled, jColor, JsonNull(), JsonNull(), nOrder, nRender); + ret = JsonObjectSet(ret, "rect", jRect); + ret = JsonObjectSet(ret, "text", jText); + return ret; +} + +json +NuiDrawListImage( + json jEnabled, + json jResRef, + json jRect, + json jAspect, + json jHAlign, + json jVAlign, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_IMAGE, jEnabled, JsonNull(), JsonNull(), JsonNull(), nOrder, nRender); + ret = JsonObjectSet(ret, "image", jResRef); + ret = JsonObjectSet(ret, "rect", jRect); + ret = JsonObjectSet(ret, "image_aspect", jAspect); + ret = JsonObjectSet(ret, "image_halign", jHAlign); + ret = JsonObjectSet(ret, "image_valign", jVAlign); + return ret; +} + +json +NuiDrawListImageRegion( + json jDrawListImage, + json jRegion +) +{ + return JsonObjectSet(jDrawListImage, "image_region", jRegion); +} + +json +NuiDrawListLine( + json jEnabled, + json jColor, + json jLineThickness, + json jA, + json jB, + int nOrder = NUI_DRAW_LIST_ITEM_ORDER_AFTER, + int nRender = NUI_DRAW_LIST_ITEM_RENDER_ALWAYS +) +{ + json ret = NuiDrawListItem(NUI_DRAW_LIST_ITEM_TYPE_LINE, jEnabled, jColor, JsonNull(), jLineThickness, nOrder, nRender); + ret = JsonObjectSet(ret, "a", jA); + ret = JsonObjectSet(ret, "b", jB); + return ret; +} + +json +NuiDrawList( + json jElem, + json jScissor, + json jList +) +{ + json ret = JsonObjectSet(jElem, "draw_list", jList); + ret = JsonObjectSet(ret, "draw_list_scissor", jScissor); + return ret; +} + +// json +// NuiCanvas( +// json jList +// ) +// { +// json ret = NuiElement("canvas", JsonNull(), jList); +// return ret; +// } + diff --git a/src/include/nw_o2_coninclude.nss b/src/include/nw_o2_coninclude.nss new file mode 100644 index 0000000..4a250bd --- /dev/null +++ b/src/include/nw_o2_coninclude.nss @@ -0,0 +1,4791 @@ +//:://///////////////////////////////////////////// +//:: NW_O2_CONINCLUDE.nss +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + This include file handles the random treasure + distribution for treasure from creatures and containers + + [ ] Documented +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent, Andrew +//:: Created On: November - May +//:: +//:: Updated for .35 by Jaysyn 2023/03/10 +//::////////////////////////////////////////////// +// :: MODS +// April 23 2002: Removed animal parts. They were silly. +// May 6 2002: Added Undead to the EXCLUSION treasure list (they drop nothing now) +// - redistributed treasure (to lessen amoun t of armor and increase 'class specific treasure' +// - Rangers with heavy armor prof. will be treated as Fighters else as Barbarians +// - Gave wizards, druids and monk their own function +// MAY 29 2002: Removed the heal potion from treasure +// Moved nymph cloak +4 to treasure bracket 6 +// Added Monk Enhancement items to random treasure + +#include "prc_class_const" + +// * --------- +// * CONSTANTS +// * --------- + +// * tweaking constants + + // * SIX LEVEL RANGES + const int RANGE_1_MIN = 0; + const int RANGE_1_MAX = 5; + const int RANGE_2_MIN = 6; + const int RANGE_2_MAX = 8; + + const int RANGE_3_MIN = 9; + const int RANGE_3_MAX = 10; + + const int RANGE_4_MIN = 11; + const int RANGE_4_MAX = 13; + + const int RANGE_5_MIN = 14; + const int RANGE_5_MAX = 16; + + const int RANGE_6_MIN = 17; + const int RANGE_6_MAX = 100; + + // * NUMBER OF ITEMS APPEARING + const int NUMBER_LOW_ONE = 100; const int NUMBER_MED_ONE = 60; const int NUMBER_HIGH_ONE = 40; const int NUMBER_BOSS_ONE = 100; + const int NUMBER_LOW_TWO = 0; const int NUMBER_MED_TWO = 30; const int NUMBER_HIGH_TWO = 40; const int NUMBER_BOSS_TWO = 0; + const int NUMBER_LOW_THREE = 0; const int NUMBER_MED_THREE = 10; const int NUMBER_HIGH_THREE = 20; const int NUMBER_BOSS_THREE = 0; + + const int NUMBER_BOOK_ONE = 75; + const int NUMBER_BOOK_TWO = 20; + const int NUMBER_BOOK_THREE = 5; + + // * AMOUNT OF GOLD BY VALUE + const float LOW_MOD_GOLD = 0.5; const float MEDIUM_MOD_GOLD = 1.0; const float HIGH_MOD_GOLD = 3.0; + // * FREQUENCY OF ITEM TYPE APPEARING BY TREASURE TYPE + const int LOW_PROB_BOOK = 1; const int MEDIUM_PROB_BOOK = 1; const int HIGH_PROB_BOOK =1; + const int LOW_PROB_ANIMAL = 0; const int MEDIUM_PROB_ANIMAL = 0; const int HIGH_PROB_ANIMAL = 0; + const int LOW_PROB_JUNK = 2; const int MEDIUM_PROB_JUNK = 1; const int HIGH_PROB_JUNK = 1; + const int LOW_PROB_GOLD = 43; const int MEDIUM_PROB_GOLD = 38; const int HIGH_PROB_GOLD = 15; + const int LOW_PROB_GEM = 9; const int MEDIUM_PROB_GEM = 15; const int HIGH_PROB_GEM = 15; + const int LOW_PROB_JEWEL = 4; const int MEDIUM_PROB_JEWEL = 6; const int HIGH_PROB_JEWEL = 15; + const int LOW_PROB_ARCANE = 3; const int MEDIUM_PROB_ARCANE = 3; const int HIGH_PROB_ARCANE = 3; + const int LOW_PROB_DIVINE = 3; const int MEDIUM_PROB_DIVINE = 3; const int HIGH_PROB_DIVINE = 3; + const int LOW_PROB_AMMO = 10; const int MEDIUM_PROB_AMMO = 5; const int HIGH_PROB_AMMO = 3; + const int LOW_PROB_KIT = 5; const int MEDIUM_PROB_KIT = 5; const int HIGH_PROB_KIT = 5; + const int LOW_PROB_POTION =17; const int MEDIUM_PROB_POTION = 20; const int HIGH_PROB_POTION= 9; + const int LOW_PROB_TABLE2 = 3; const int MEDIUM_PROB_TABLE2 = 3; const int HIGH_PROB_TABLE2= 30; + + +// * readability constants + +const int TREASURE_LOW = 1; +const int TREASURE_MEDIUM = 2; +const int TREASURE_HIGH = 3; +const int TREASURE_BOSS = 4; +const int TREASURE_BOOK = 5; + + +// * JUMP_LEVEL is used in a Specific item function +// * in the case where a generic item is called for within that function +// * it will create a generic item by adding JUMP_LEVEL to the character's +// * hit die for the purposes of the treasure evaluation. +// * May 2002: Lowered JUMP_LEVEL from 3 to 2 + +const int JUMP_LEVEL = 2; + + +//* Declarations + void CreateGenericExotic(object oTarget, object oAdventurer, int nModifier = 0); + void CreateGenericMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateSpecificMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateGenericDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateSpecificDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateGenericWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0); + void CreateSpecificWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0); + int nDetermineClassToUse(object oCharacter); + + +// * +// * IMPLEMENTATION +// * + +// * Comment the speakstring in to debug treasure generation +void dbSpeak(string s) +{ +// SpeakString(s); +} + +//* made this function to help with debugging +void dbCreateItemOnObject(string sItemTemplate, object oTarget = OBJECT_SELF, int nStackSize = 1) +{ +/* + if (sItemTemplate == "") + { + PrintString("blank item passed into dbCreateItemOnObject. Please report as bug to Brent."); + } + dbSpeak(sItemTemplate); +*/ + + //sItemTemplate = GetStringLowerCase + + if (nStackSize == 1) + { + // * checks to see if this is a throwing item and if it is + // * it creates more + + string sRoot = GetSubString(sItemTemplate, 0, 6); + //dbSpeak("ROOT: " + sRoot); + if (GetStringLowerCase(sRoot) == "nw_wth") + { + nStackSize = Random(30) + 1; + } + } + object oItem = CreateItemOnObject(sItemTemplate, oTarget, nStackSize); +/* + if (GetIsObjectValid(oItem) == FALSE && sItemTemplate != "NW_IT_GOLD001") + { + + // * check to see if item is there in a stack, if not give warning + if (GetIsObjectValid(GetItemPossessedBy(oTarget, GetStringUpperCase(sItemTemplate))) == FALSE && + GetIsObjectValid(GetItemPossessedBy(oTarget, GetStringLowerCase(sItemTemplate))) == FALSE) + { + PrintString("**DESIGN***"); + PrintString("******" + sItemTemplate + " is an invalid item template. Please report as bug to Brent."); + PrintString("*******"); + } + } +*/ +} + + +// * +// * GET FUNCTIONS +// * + +// * Returns the object that either last opened the container or destroyed it +object GetLastOpener() +{ + if (GetIsObjectValid(GetLastOpenedBy()) == TRUE) + { + //dbSpeak("LastOpener: GetLastOpenedBy " + GetTag(GetLastOpenedBy())); + return GetLastOpenedBy(); + } + else + if (GetIsObjectValid(GetLastKiller()) == TRUE) + { + //dbSpeak("LastOpener: GetLastAttacker"); + return GetLastKiller(); + } + //dbSpeak("LastOpener: The Object is Invalid you weenie!"); + return OBJECT_INVALID; +} + +//:://///////////////////////////////////////////// +//:: GetRange +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Returns true if nHD matches the correct + level range for the indicated nCategory. + (i.e., First to Fourth level characters + are considered Range1) +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: +//::////////////////////////////////////////////// +int GetRange(int nCategory, int nHD) +{ + int nMin = 0; int nMax = 0; + switch (nCategory) + { + case 6: nMin = RANGE_6_MIN; nMax = RANGE_6_MAX; break; + case 5: nMin = RANGE_5_MIN; nMax = RANGE_5_MAX; break; + case 4: nMin = RANGE_4_MIN; nMax = RANGE_4_MAX; break; + case 3: nMin = RANGE_3_MIN; nMax = RANGE_3_MAX; break; + case 2: nMin = RANGE_2_MIN; nMax = RANGE_2_MAX; break; + case 1: nMin = RANGE_1_MIN; nMax = RANGE_1_MAX; break; + } + + //dbSpeak("nMin = " + IntToString(nMin)); + //dbSpeak("nMax = " + IntToString(nMax)); + //dbSpeak("GetRange.nHD = " + IntToString(nHD)); + if (nHD >= nMin && nHD <= nMax) + { + return TRUE; + } + + return FALSE; + +} + +//:://///////////////////////////////////////////// +//:: GetNumberOfItems +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Returns the number of items to create. +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: +//::////////////////////////////////////////////// +int GetNumberOfItems(int nTreasureType) +{ + int nItems = 0; + int nRandom = 0; + + int nProbThreeItems = 0; + int nProbTwoItems = 0; + int nProbOneItems = 0; + + if (nTreasureType == TREASURE_LOW) + { + nProbThreeItems = NUMBER_LOW_THREE; + nProbTwoItems = NUMBER_LOW_TWO; + nProbOneItems = NUMBER_LOW_ONE; + } + else + if (nTreasureType == TREASURE_MEDIUM) + { + nProbThreeItems = NUMBER_MED_THREE; + nProbTwoItems = NUMBER_MED_TWO; + nProbOneItems = NUMBER_MED_ONE; + } + else + if (nTreasureType == TREASURE_HIGH) + { + nProbThreeItems = NUMBER_HIGH_THREE; + nProbTwoItems = NUMBER_HIGH_TWO; + nProbOneItems = NUMBER_HIGH_ONE; + } + else + if (nTreasureType == TREASURE_BOSS) + { + nProbThreeItems = NUMBER_BOSS_THREE; + nProbTwoItems = NUMBER_BOSS_TWO; + nProbOneItems = NUMBER_BOSS_ONE; + } + else + if (nTreasureType == TREASURE_BOOK) + { + nProbThreeItems = NUMBER_BOOK_THREE; + nProbTwoItems = NUMBER_BOOK_TWO; + nProbOneItems = NUMBER_BOOK_ONE; + } + + + nRandom = d100(); + if (nRandom <= nProbThreeItems) + { + nItems = 3; + } + else + if (nRandom <= nProbTwoItems + nProbThreeItems) + { + nItems = 2; + } + else + { + nItems = 1; + } + + // * May 13 2002: Cap number of items, in case of logic error + if (nItems > 3) + { + nItems = 3; + } + + return nItems; +} + + +// * +// * TREASURE GENERATION FUNCTIONS +// * + // * + // * Non-Scaling Treasure + // * + void CreateBook(object oTarget) + { + int nBook1 = Random(31) + 1; + string sRes = "NW_IT_BOOK01"; + + if (nBook1 < 10) + { + sRes = "NW_IT_BOOK00" + IntToString(nBook1); + } + else + { + sRes = "NW_IT_BOOK0" + IntToString(nBook1); + } + //dbSpeak("Create book"); + dbCreateItemOnObject(sRes, oTarget); + } + + void CreateAnimalPart(object oTarget) + { + + string sRes = ""; + int nResult = Random(3) + 1; + switch (nResult) + { + case 1: sRes = "NW_IT_MSMLMISC20"; break; + case 2: sRes = "NW_IT_MMIDMISC05"; break; + case 3: sRes = "NW_IT_MMIDMISC06"; break; + } + //dbSpeak("animal"); + dbCreateItemOnObject(sRes, oTarget); + } + + void CreateJunk(object oTarget) + { + string sRes = "NW_IT_TORCH001"; + int NUM_ITEMS = 6; + int nResult = Random(NUM_ITEMS) + 1; + int nKit = 0; + switch (nResult) + { + case 1: sRes = "NW_IT_MPOTION021"; break; //ale + case 2: sRes = "NW_IT_MPOTION021"; break; // ale + case 3: sRes = "NW_IT_MPOTION023"; break; // wine + case 4: sRes = "NW_IT_MPOTION021"; break; // ale + case 5: sRes = "NW_IT_MPOTION022"; break; // spirits + case 6: sRes = "NW_IT_TORCH001"; break; //torch + } + //dbSpeak("CreateJunk"); + dbCreateItemOnObject(sRes, oTarget); + } + // * + // * Scaling Treasure + // * + void CreateGold(object oTarget, object oAdventurer, int nTreasureType, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + int nAmount = 0; + + if (GetRange(1, nHD)) + { + nAmount = d10(); + } + else if (GetRange(2, nHD)) + { + nAmount = d20(); + } + else if (GetRange(3, nHD)) + { + nAmount = d20(2); + } + else if (GetRange(4, nHD)) + { + nAmount = d20(5); + } + else if (GetRange(5, nHD)) + { + nAmount = d20(8); + } + else if (GetRange(6, nHD)) + { + nAmount = d20(10); + } + float nMod = 0.0; + if (nTreasureType == TREASURE_LOW) nMod = LOW_MOD_GOLD; + else if (nTreasureType == TREASURE_MEDIUM) nMod = MEDIUM_MOD_GOLD; + else if (nTreasureType == TREASURE_HIGH) nMod = HIGH_MOD_GOLD; + + // * always at least 1gp is created + nAmount = FloatToInt(nAmount * nMod); + if (nAmount <= 0) + { + nAmount = 1; + } + //dbSpeak("gold"); + dbCreateItemOnObject("NW_IT_GOLD001", oTarget, nAmount); + } + void CreateGem(object oTarget, object oAdventurer, int nTreasureType, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sGem = "nw_it_gem001"; + if (GetRange(1, nHD)) + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sGem = "nw_it_gem001"; break; + case 2: sGem = "nw_it_gem007"; break; + case 3: sGem = "nw_it_gem002"; break; + case 4: case 5: sGem = "nw_it_gem004"; break; + case 6: case 7: sGem = "nw_it_gem014"; break; + case 8: sGem = "nw_it_gem003"; break; + case 9: sGem = "nw_it_gem015"; break; + } + } + else if (GetRange(2, nHD)) // 30 GP Avg; 150 gp Max + { + int nRandom = d12(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem001"; break; + case 2: sGem = "nw_it_gem007"; break; + case 3: sGem = "nw_it_gem002"; break; + case 4: sGem = "nw_it_gem004"; break; + case 5: case 6: sGem = "nw_it_gem014"; break; + case 7: case 8: sGem = "nw_it_gem003"; break; + case 9: case 10: sGem = "nw_it_gem015"; break; + case 11: sGem = "nw_it_gem011"; break; + case 12: sGem = "nw_it_gem013"; break; + } + + } + else if (GetRange(3, nHD)) // 75GP Avg; 500 gp max + { + int nRandom = d2(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + } + + } + else if (GetRange(4, nHD)) // 150 gp avg; 1000 gp max + { + int nRandom = d3(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + case 3: sGem = "nw_it_gem008"; break; + } + } + else if (GetRange(5, nHD)) // 300 gp avg; any + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + case 3: case 4: sGem = "nw_it_gem008"; break; + case 5: sGem = "nw_it_gem009"; break; + case 6: sGem = "nw_it_gem009"; break; + } + } + else if (GetRange(6, nHD))// * Anything higher than level 15 500 gp avg; any + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sGem = "nw_it_gem013"; break; + case 2: sGem = "nw_it_gem010"; break; + case 3: case 4: sGem = "nw_it_gem008"; break; + case 5: sGem = "nw_it_gem009"; break; + case 6: sGem = "nw_it_gem009"; break; + case 7: sGem = "nw_it_gem006"; break; + case 8: sGem = "nw_it_gem012"; break; + } + } + //dbSpeak("Create Gem"); + dbCreateItemOnObject(sGem, oTarget, 1); + } + void CreateJewel(object oTarget, object oAdventurer, int nTreasureType, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sJewel = ""; + + if (GetRange(1, nHD)) // 15 gp avg; 75 gp max + { + int nRandom = d2(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: sJewel = "nw_it_mneck020"; break; + } + } + else if (GetRange(2, nHD)) // 30 GP Avg; 150 gp Max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: case 3: sJewel = "nw_it_mneck020"; break; + case 4: sJewel = "nw_it_mring022"; break; + case 5: case 6: sJewel = "nw_it_mneck023"; break; } + } + else if (GetRange(3, nHD)) // 75GP Avg; 500 gp max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: case 3: sJewel = "nw_it_mneck020"; break; + case 4: case 5: sJewel = "nw_it_mring022"; break; + case 6: sJewel = "nw_it_mneck021"; break; + } + } + else if (GetRange(4, nHD)) // 150 gp avg; 1000 gp max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring021"; break; + case 2: sJewel = "nw_it_mring022"; break; + case 3: case 4: case 5: sJewel = "nw_it_mneck021"; break; + case 6: sJewel = "nw_it_mring023"; break; + } + } + else if (GetRange(5, nHD)) // 300 gp avg; any + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring022"; break; + case 2: case 3: sJewel = "nw_it_mneck021"; break; + case 4: case 5: case 6: sJewel = "nw_it_mring023"; break; + case 7: case 8: sJewel = "nw_it_mneck022"; break; + } + } + else if (GetRange(6, nHD)) + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sJewel = "nw_it_mring022"; break; + case 2: sJewel = "nw_it_mneck021"; break; + case 3: case 4: sJewel = "nw_it_mring023"; break; + case 5: case 6: sJewel = "nw_it_mneck022"; break; + } + } + //dbSpeak("Create Jewel"); + + dbCreateItemOnObject(sJewel, oTarget, 1); + + } + // * returns the valid upper limit for any arcane spell scroll + int TrimLevel(int nScroll, int nLevel) + { int nMax = 5; + switch (nLevel) + { + case 0: nMax = 4; break; + case 1: nMax = 13; break; + case 2: nMax = 21; break; + case 3: nMax = 15; break; + case 4: nMax = 17; break; + case 5: nMax = 13; break; + case 6: nMax = 14; break; + case 7: nMax = 8; break; + case 8: nMax = 9; break; + case 9: nMax = 12; break; + } + if (nScroll > nMax) nScroll = nMax; + return nScroll; + + } + // * nModifier is to 'raise' the level of the oAdventurer + void CreateArcaneScroll(object oTarget, object oAdventurer, int nModifier = 0) + { + int nMaxSpells = 21; + int nHD = GetHitDice(oAdventurer) + nModifier; + int nScroll = 1; + int nLevel = 1; + + if (GetRange(1, nHD)) // l 1-2 + { + nLevel = d2(); + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(2, nHD)) // l 1-4 + { + nLevel = d4(); + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(3, nHD)) // l 2-6 + { + nLevel = d6(); + if (nLevel < 2) nLevel = 2; + + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(4, nHD)) // l 3-8 + { + nLevel = d8(); + if (nLevel < 3) nLevel = 3; + + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(5, nHD)) // l 4-9 + { + nLevel = d8() + 1; + if (nLevel < 4) nLevel = 4; + + nScroll = Random(nMaxSpells) + 1; + } + else if (GetRange(6, nHD)) // 5 -9 + { + nLevel = d8() + 1; + if (nLevel < 5) nLevel = 5; + + nScroll = Random(nMaxSpells) + 1; + } + + // * Trims the level of the scroll to match the max # of scrolls in each level range + nScroll = TrimLevel(nScroll, nLevel); + + string sRes = "nw_it_sparscr216"; + + if (nScroll < 10) + { + sRes = "NW_IT_SPARSCR" + IntToString(nLevel) + "0" + IntToString(nScroll); + } + else + { + sRes = "NW_IT_SPARSCR" + IntToString(nLevel) + IntToString(nScroll); + } + dbCreateItemOnObject(sRes, oTarget, 1); + } + + void CreateDivineScroll(object oTarget, object oAdventurer, int nModifier=0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sScroll = ""; + if (GetRange(1, nHD)) + { + int nRandom = d4(); + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr201"; break; + case 2: sScroll = "nw_it_spdvscr202"; break; + case 3: sScroll = "nw_it_spdvscr203"; break; + case 4: sScroll = "nw_it_spdvscr204"; break; + } + } + else if (GetRange(2, nHD)) + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr201"; break; + case 2: sScroll = "nw_it_spdvscr202";break; + case 3: sScroll = "nw_it_spdvscr203"; break; + case 4: sScroll = "nw_it_spdvscr204"; break; + case 5: sScroll = "nw_it_spdvscr301"; break; + case 6: sScroll = "nw_it_spdvscr302"; break; + case 7: sScroll = "nw_it_spdvscr401"; break; + case 8: sScroll = "nw_it_spdvscr402"; break; + } + + } + else if (GetRange(3, nHD)) + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr201"; break; + case 2: sScroll = "nw_it_spdvscr202"; break; + case 3: sScroll = "nw_it_spdvscr203"; break; + case 4: sScroll = "nw_it_spdvscr204"; break; + case 5: sScroll = "nw_it_spdvscr301"; break; + case 6: sScroll = "nw_it_spdvscr302"; break; + case 7: sScroll = "nw_it_spdvscr401"; break; + case 8: sScroll = "nw_it_spdvscr402"; break; + case 9: sScroll = "nw_it_spdvscr501"; break; + } + + } + else + { + int nRandom = Random(7) + 1; + switch (nRandom) + { + case 1: sScroll = "nw_it_spdvscr301"; break; + case 2: sScroll = "nw_it_spdvscr302"; break; + case 3: sScroll = "nw_it_spdvscr401"; break; + case 4: sScroll = "nw_it_spdvscr402"; break; + case 5: sScroll = "nw_it_spdvscr501"; break; + case 6: sScroll = "nw_it_spdvscr701"; break; + case 7: sScroll = "nw_it_spdvscr702"; break; + } + } + //dbSpeak("Divine Scroll"); + + dbCreateItemOnObject(sScroll, oTarget, 1); + + } + void CreateAmmo(object oTarget, object oAdventurer, int nModifier=0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sAmmo = ""; + + if (GetRange(1, nHD)) // * 200 gp max + { + int nRandom = d3(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wambo001"; break; + case 3: sAmmo = "nw_wambu001"; break; + } + } + else if (GetRange(2, nHD)) // * 800 gp max + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wambo001"; break; + case 3: sAmmo = "nw_wambu001"; break; + case 4: sAmmo = "nw_wammar001"; break; + case 5: sAmmo = "nw_wammbo001"; break; + case 6: sAmmo = "nw_wammbo002"; break; + } + } + else if (GetRange(3, nHD)) // * - 2500 gp + { + int nRandom = d20(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wambo001"; break; + case 3: sAmmo = "nw_wambu001"; break; + case 4: sAmmo = "nw_wammar001"; break; + case 5: sAmmo = "nw_wammbo001"; break; + case 6: sAmmo = "nw_wammbo002"; break; + case 7: sAmmo = "nw_wammbo003"; break; + case 8: sAmmo = "nw_wammbu002"; break; + case 9: sAmmo = "nw_wammar002"; break; + case 10: sAmmo = "nw_wammar001"; break; + case 11: sAmmo = "nw_wammar003"; break; + case 12: sAmmo = "nw_wammar004"; break; + case 13: sAmmo = "nw_wammar005"; break; + case 14: sAmmo = "nw_wammar006"; break; + case 15: sAmmo = "nw_wammbo004"; break; + case 16: sAmmo = "nw_wammbo005"; break; + case 17: sAmmo = "nw_wammbu004"; break; + case 18: sAmmo = "nw_wammbu005"; break; + case 19: sAmmo = "nw_wammbu006"; break; + case 20: sAmmo = "nw_wammbu007"; break; + } + } + else + { + int nRandom = d20(); + switch (nRandom) + { + case 1: sAmmo = "nw_wamar001"; break; + case 2: sAmmo = "nw_wammbu001"; break; + case 3: sAmmo = "nw_wammbu003"; break; + case 4: sAmmo = "nw_wammar001"; break; + case 5: sAmmo = "nw_wammbo001"; break; + case 6: sAmmo = "nw_wammbo002"; break; + case 7: sAmmo = "nw_wammbo003"; break; + case 8: sAmmo = "nw_wammbu002"; break; + case 9: sAmmo = "nw_wammar002"; break; + case 10: sAmmo = "nw_wammar001"; break; + case 11: sAmmo = "nw_wammar003"; break; + case 12: sAmmo = "nw_wammar004"; break; + case 13: sAmmo = "nw_wammar005"; break; + case 14: sAmmo = "nw_wammar006"; break; + case 15: sAmmo = "nw_wammbo004"; break; + case 16: sAmmo = "nw_wammbo005"; break; + case 17: sAmmo = "nw_wammbu004"; break; + case 18: sAmmo = "nw_wammbu005"; break; + case 19: sAmmo = "nw_wammbu006"; break; + case 20: sAmmo = "nw_wammbu007"; break; + } + } + //dbSpeak("ammo"); + dbCreateItemOnObject(sAmmo, oTarget, Random(30) + 1); // create up to 30 of the specified ammo type + } + + void CreateTrapKit(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sKit = ""; + if (GetRange(1, nHD)) // 200 + { + int nRandom = d3(); + switch (nRandom) + { + case 1: sKit = "nw_it_trap001"; break; + case 2: sKit = "nw_it_trap029"; break; + case 3: sKit = "nw_it_trap033"; break; + } + } + else if (GetRange(2, nHD)) // 800 + { + int nRandom = d12(); + switch (nRandom) + { + case 1: sKit = "nw_it_trap001"; break; + case 2: sKit = "nw_it_trap029"; break; + case 3: sKit = "nw_it_trap033"; break; + case 4: sKit = "nw_it_trap002"; break; + case 5: sKit = "nw_it_trap030"; break; + case 6: sKit = "nw_it_trap037"; break; + case 7: sKit = "nw_it_trap034"; break; + case 8: sKit = "nw_it_trap005"; break; + case 9: sKit = "nw_it_trap038"; break; + case 10: sKit = "nw_it_trap041"; break; + case 11: sKit = "nw_it_trap003"; break; + case 12: sKit = "nw_it_trap031"; break; + } + + } + else if (GetRange(3, nHD)) // 200 - 2500 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap002"; break; + case 2: sKit = "nw_it_trap030"; break; + case 3: sKit = "nw_it_trap037"; break; + case 4: sKit = "nw_it_trap034"; break; + case 5: sKit = "nw_it_trap005"; break; + case 6: sKit = "nw_it_trap038"; break; + case 7: sKit = "nw_it_trap041"; break; + case 8: sKit = "nw_it_trap003"; break; + case 9: sKit = "nw_it_trap031"; break; + case 10: sKit = "nw_it_trap035"; break; + case 11: sKit = "nw_it_trap006"; break; + case 12: sKit = "nw_it_trap042"; break; + case 13: sKit = "nw_it_trap004"; break; + case 14: sKit = "nw_it_trap032"; break; + case 15: sKit = "nw_it_trap039"; break; + case 16: sKit = "nw_it_trap009"; break; + case 17: sKit = "nw_it_trap036"; break; + } + + } + else if (GetRange(4, nHD)) // 800 - 10000 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap035"; break; + case 2: sKit = "nw_it_trap006"; break; + case 3: sKit = "nw_it_trap042"; break; + case 4: sKit = "nw_it_trap004"; break; + case 5: sKit = "nw_it_trap032"; break; + case 6: sKit = "nw_it_trap039"; break; + case 7: sKit = "nw_it_trap009"; break; + case 8: sKit = "nw_it_trap036"; break; + case 9: sKit = "nw_it_trap013"; break; + case 10: sKit = "nw_it_trap040"; break; + case 11: sKit = "nw_it_trap007"; break; + case 12: sKit = "nw_it_trap043"; break; + case 13: sKit = "nw_it_trap010"; break; + case 14: sKit = "nw_it_trap017"; break; + case 15: sKit = "nw_it_trap021"; break; + case 16: sKit = "nw_it_trap014"; break; + case 17: sKit = "nw_it_trap025"; break; + case 18: sKit = "nw_it_trap008"; break; + case 19: sKit = "nw_it_trap044"; break; + } + + } + else if (GetRange(5, nHD)) // 2000 -16500 + { + int nRandom = Random(18) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap039"; break; + case 2: sKit = "nw_it_trap009"; break; + case 3: sKit = "nw_it_trap036"; break; + case 4: sKit = "nw_it_trap013"; break; + case 5: sKit = "nw_it_trap040"; break; + case 6: sKit = "nw_it_trap007"; break; + case 7: sKit = "nw_it_trap043"; break; + case 8: sKit = "nw_it_trap010"; break; + case 9: sKit = "nw_it_trap017"; break; + case 10: sKit = "nw_it_trap021"; break; + case 11: sKit = "nw_it_trap014"; break; + case 12: sKit = "nw_it_trap025"; break; + case 13: sKit = "nw_it_trap008"; break; + case 14: sKit = "nw_it_trap044"; break; + case 15: sKit = "nw_it_trap018"; break; + case 16: sKit = "nw_it_trap011"; break; + case 17: sKit = "nw_it_trap022"; break; + case 18: sKit = "nw_it_trap026"; break; + } + + } + else if (GetRange(6, nHD)) // 2000 - ? + { + int nRandom = Random(27) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_trap039"; break; + case 2: sKit = "nw_it_trap009"; break; + case 3: sKit = "nw_it_trap036"; break; + case 4: sKit = "nw_it_trap013"; break; + case 5: sKit = "nw_it_trap040"; break; + case 6: sKit = "nw_it_trap007"; break; + case 7: sKit = "nw_it_trap043"; break; + case 8: sKit = "nw_it_trap010"; break; + case 9: sKit = "nw_it_trap017"; break; + case 10: sKit = "nw_it_trap021"; break; + case 11: sKit = "nw_it_trap014"; break; + case 12: sKit = "nw_it_trap025"; break; + case 13: sKit = "nw_it_trap008"; break; + case 14: sKit = "nw_it_trap044"; break; + case 15: sKit = "nw_it_trap018"; break; + case 16: sKit = "nw_it_trap011"; break; + case 17: sKit = "nw_it_trap022"; break; + case 18: sKit = "nw_it_trap026"; break; + case 19: sKit = "nw_it_trap015"; break; + case 20: sKit = "nw_it_trap012"; break; + case 21: sKit = "nw_it_trap019"; break; + case 22: sKit = "nw_it_trap023"; break; + case 23: sKit = "nw_it_trap016"; break; + case 24: sKit = "nw_it_trap027"; break; + case 25: sKit = "nw_it_trap020"; break; + case 26: sKit = "nw_it_trap024"; break; + case 27: sKit = "nw_it_trap028"; break; + } + + } + //dbSpeak("Create Trapkit"); + dbCreateItemOnObject(sKit, oTarget, 1); + + } + void CreateHealingKit(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sKit = ""; + if (GetRange(1, nHD)) // 200 + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit001"; break; + } + } + else if (GetRange(2, nHD)) // 800 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit001"; break; + case 2: sKit = "nw_it_medkit002"; break; + } + + } + else if (GetRange(3, nHD)) // 200 - 2500 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit002"; break; + case 2: sKit = "nw_it_medkit003"; break; + } + + } + else if (GetRange(4, nHD)) // 800 - 10000 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit003";break; + case 2: sKit = "nw_it_medkit004"; break; + } + + } + else if (GetRange(5, nHD)) // 2000 -16500 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit003"; break; + case 2: sKit = "nw_it_medkit004";break; + } + + } + else if (GetRange(6, nHD)) // 2000 - ? + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_medkit003"; break; + case 2: sKit = "nw_it_medkit004";break; + } + + } + //dbSpeak("Create Healing Kit"); + + dbCreateItemOnObject(sKit, oTarget, 1); + + } + void CreateLockPick(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sKit = ""; + if (GetRange(1, nHD)) // 200 + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sKit = "nw_it_picks001"; break; + case 2: sKit = "nw_it_picks002"; break; + case 3: sKit = "nw_it_picks001"; break; + case 4: sKit = "nw_it_picks001"; break; + case 5: sKit = "nw_it_picks001"; break; + case 6: sKit = "nw_it_picks001"; break; + case 7: sKit = "nw_it_picks001"; break; + case 8: sKit = "nw_it_picks001"; break; + } + } + else if (GetRange(2, nHD)) // 800 + { + int nRandom = d6(); + switch (nRandom) + { + case 1: sKit = "nw_it_picks001"; break; + case 2: sKit = "nw_it_picks002"; break; + case 3: sKit = "nw_it_picks003"; break; + case 4: sKit = "nw_it_picks002"; break; + case 5: sKit = "nw_it_picks002"; break; + case 6: sKit = "nw_it_picks002"; break; + } + + } + else if (GetRange(3, nHD)) // 200 - 2500 + { + int nRandom = Random(2) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks003"; break; + case 2: sKit = "nw_it_picks004"; break; + } + + } + else if (GetRange(4, nHD)) // 800 - 10000 + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks004"; break; + } + + } + else if (GetRange(5, nHD)) // 2000 -16500 + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks004"; break; + } + + } + else if (GetRange(6, nHD)) // 2000 - ? + { + int nRandom = Random(1) + 1; + switch (nRandom) + { + case 1: sKit = "nw_it_picks004"; break; + } + + } + //dbSpeak("Create Lockpick"); + + dbCreateItemOnObject(sKit, oTarget, 1); + + } + void CreateKit(object oTarget, object oAdventurer, int nModifier = 0) + { + // * April 23 2002: Major restructuring of this function + // * to allow me to + + switch (Random(8) + 1) + { + case 1: CreateTrapKit(oTarget, oAdventurer, nModifier); break; + case 2: case 3: case 4: case 5: CreateHealingKit(oTarget, oAdventurer, nModifier); break; + case 6: case 7: case 8: CreateLockPick(oTarget, oAdventurer, nModifier); break; + } + } + + void CreatePotion(object oTarget, object oAdventurer, int nModifier = 0) + { + string sPotion = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) + { + int nRandom = d10(); + switch (nRandom) + { + case 1: case 2: case 3: case 4: sPotion = "nw_it_mpotion001"; break; + case 5: case 6: case 7: sPotion = "nw_it_mpotion020"; break; + case 8: sPotion = "nw_it_mpotion002"; break; + case 9: sPotion = "nw_it_mpotion009"; break; + case 10: sPotion = "nw_it_mpotion005"; break; + } + + } + else if (GetRange(2, nHD)) + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: sPotion = "nw_it_mpotion001"; break; + case 4: case 5: case 6: case 7: case 8: sPotion = "nw_it_mpotion020"; break; + case 9: case 10: case 11: case 12: sPotion = "nw_it_mpotion002"; break; + case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + else if (GetRange(3, nHD)) + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 9: case 10: case 11: case 12: + case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + else if (GetRange(4, nHD)) + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 9: case 10: case 11: case 12: sPotion = "nw_it_mpotion003"; break; + case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + else // keep 5 and 6 the same + { + int nRandom = Random(29) + 1; + switch (nRandom) + { + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 9: sPotion = "nw_it_mpotion003" ; + case 10: case 11: case 12: case 13: case 14: sPotion = "nw_it_mpotion003"; break; + case 15: sPotion = "nw_it_mpotion009"; break; + case 16: sPotion = "nw_it_mpotion005"; break; + case 17: sPotion = "nw_it_mpotion007"; break; + case 18: sPotion = "nw_it_mpotion008"; break; + case 19: sPotion = "nw_it_mpotion010"; break; + case 20: sPotion = "nw_it_mpotion011"; break; + case 21: sPotion = "nw_it_mpotion013"; break; + case 22: sPotion = "nw_it_mpotion014"; break; + case 23: sPotion = "nw_it_mpotion015"; break; + case 24: sPotion = "nw_it_mpotion016"; break; + case 25: sPotion = "nw_it_mpotion017"; break; + case 26: sPotion = "nw_it_mpotion018"; break; + case 27: sPotion = "nw_it_mpotion019"; break; + case 28: sPotion = "nw_it_mpotion004"; break; + case 29: sPotion = "nw_it_mpotion006"; break; + } + } + //dbSpeak("Create Potion"); + dbCreateItemOnObject(sPotion, oTarget, 1); + } + //:://///////////////////////////////////////////// + //:: CreateTable2GenericItem + //:: Copyright (c) 2002 Bioware Corp. + //::////////////////////////////////////////////// + /* + Creates an item based upon the class of + oAdventurer + */ + //::////////////////////////////////////////////// + //:: Created By: Brent + //:: Created On: + //::////////////////////////////////////////////// + void CreateGenericMiscItem(object oTarget, object oAdventurer, int nModifier=0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sItem = ""; + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mglove004"; break; + case 2: sItem = "nw_it_mglove004"; break; + case 3: sItem = "nw_it_mglove005"; break; + case 4: sItem = "nw_it_mglove006"; break; + case 5: sItem = "nw_it_mglove007"; break; + case 6: sItem = "nw_it_mglove008"; break; + case 7: sItem = "nw_it_mglove009"; break; + case 8: sItem = "nw_mcloth006"; break; + case 9: sItem = "nw_it_mglove012"; break; + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(25) + 1; + switch (nRandom) + { + case 1: sItem = "nw_mcloth006"; break; + case 2: sItem = "nw_it_mring009"; break; + case 3: sItem = "nw_it_mring009"; break; + case 4: sItem = "nw_it_mring010"; break; + case 5: sItem = "nw_it_mring011"; break; + case 6: sItem = "nw_it_mboots010"; break; + case 7: sItem = "nw_it_mneck024"; break; + case 8: sItem = "nw_mcloth007"; break; + case 9: sItem = "nw_it_mring024"; break; + case 10: sItem = "nw_it_mring012"; break; + case 11: sItem = "nw_mcloth008"; break; + case 12: sItem = "nw_it_mglove010"; break; + case 13: sItem = "nw_it_mglove011"; break; + case 14: sItem = "nw_it_mglove013"; break; + case 15: sItem = "nw_it_mglove014"; break; + case 16: sItem = "nw_it_mglove015"; break; + case 17: sItem = "nw_maarcl097"; break; + case 18: sItem = "nw_maarcl097"; break; + case 19: sItem = "nw_maarcl099"; break; + case 20: sItem = "nw_it_mneck032"; break; + case 21: sItem = "nw_mcloth010"; break; + case 22: sItem = "nw_it_mbracer002"; break; + case 23: sItem = "nw_it_mneck001"; break; + case 24: sItem = "nw_maarcl055"; break; + case 25: sItem = "nw_mcloth009"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(44) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mring009"; break; + case 2: sItem = "nw_it_mring009"; break; + case 3: sItem = "nw_it_mring010"; break; + case 4: sItem = "nw_it_mring011"; break; + case 5: sItem = "nw_it_mboots010"; break; + case 6: sItem = "nw_it_mneck024"; break; + case 7: sItem = "nw_mcloth007"; break; + case 8: sItem = "nw_it_mring024"; break; + case 9: sItem = "nw_it_mring012"; break; + case 10: sItem = "nw_mcloth008"; break; + case 11: sItem = "nw_it_mglove010"; break; + case 12: sItem = "nw_it_mglove011"; break; + case 13: sItem = "nw_it_mglove013"; break; + case 14: sItem = "nw_it_mglove014"; break; + case 15: sItem = "nw_it_mglove015"; break; + case 16: sItem = "nw_it_contain003"; break; + case 17: sItem = "nw_maarcl097"; break; + case 18: sItem = "nw_maarcl099"; break; + case 19: sItem = "nw_it_mneck032"; break; + case 20: sItem = "nw_mcloth010"; break; + case 21: sItem = "nw_it_mbracer002"; break; + case 22: sItem = "nw_it_mneck001"; break; + case 23: sItem = "nw_maarcl055"; break; + case 24: sItem = "nw_mcloth009"; break; + case 25: sItem = "nw_it_mring001"; break; + case 26: sItem = "nw_it_mboots001"; break; + case 27: sItem = "nw_it_mbracer001"; break; + case 28: sItem = "nw_it_mneck007"; break; + case 29: sItem = "nw_maarcl096"; break; + case 30: sItem = "nw_it_mglove003"; break; + case 31: sItem = "nw_it_contain004"; break; + case 32: sItem = "nw_it_mneck031"; break; + case 33: sItem = "nw_it_mring006"; break; + case 34: sItem = "nw_it_mneck006"; break; + case 35: sItem = "nw_it_mneck029"; break; + case 36: sItem = "nw_it_mring013"; break; + case 37: sItem = "nw_it_mboots011"; break; + case 38: sItem = "nw_it_mneck025"; break; + case 39: sItem = "nw_it_mbelt009"; break; + case 40: sItem = "nw_it_mbelt010"; break; + case 41: sItem = "nw_it_mbelt011"; break; + case 42: sItem = "nw_it_mring025"; break; + case 43: sItem = "nw_it_mring025"; break; + case 44: sItem = "nw_maarcl031"; break; + + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(48) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mring001"; break; + case 2: sItem = "nw_it_mboots001"; break; + case 3: sItem = "nw_it_mbracer001"; break; + case 4: sItem = "nw_it_mneck007"; break; + case 5: sItem = "nw_maarcl096"; break; + case 6: sItem = "nw_it_mglove003"; break; + case 7: sItem = "nw_it_mneck031"; break; + case 8: sItem = "nw_it_mneck031"; break; + case 9: sItem = "nw_it_mring006"; break; + case 10: sItem = "nw_it_mneck006"; break; + case 11: sItem = "nw_it_mneck029"; break; + case 12: sItem = "nw_it_mring013"; break; + case 13: sItem = "nw_it_mboots011"; break; + case 14: sItem = "nw_it_mneck025"; break; + case 15: sItem = "nw_it_mbelt009"; break; + case 16: sItem = "nw_it_mbelt010"; break; + case 17: sItem = "nw_it_mbelt011"; break; + case 18: sItem = "nw_it_mring025"; break; + case 19: sItem = "nw_it_mring025"; break; + case 20: sItem = "nw_it_mbracer007"; break; + case 21: sItem = "nw_it_mbracer007"; break; + case 22: sItem = "nw_it_mneck012"; break; + case 23: sItem = "nw_maarcl088"; break; + case 24: sItem = "nw_it_mboots012"; break; + case 25: sItem = "nw_it_mneck026"; break; + case 26: sItem = "nw_it_mboots006"; break; + case 27: sItem = "nw_it_mbracer003"; break; + case 28: sItem = "nw_it_mneck008"; break; + case 29: sItem = "nw_it_mring008"; break; + case 30: sItem = "nw_maarcl056"; break; + case 31: sItem = "nw_maarcl092"; break; + case 32: sItem = "nw_it_mring014"; break; + case 33: sItem = "nw_it_mneck016"; break; + case 34: sItem = "nw_it_mboots013"; break; + case 35: sItem = "nw_it_mneck027"; break; + case 36: sItem = "nw_it_mbracer008"; break; + case 37: sItem = "nw_it_mneck013"; break; + case 38: sItem = "nw_maarcl089"; break; + case 39: sItem = "nw_it_mbelt012"; break; + case 40: sItem = "nw_it_mbelt013"; break; + case 41: sItem = "nw_it_mbelt014"; break; + case 42: sItem = "nw_it_mring027"; break; + case 43: sItem = "nw_it_mboots007"; break; + case 44: sItem = "nw_it_mbracer004"; break; + case 45: sItem = "nw_it_mneck009"; break; + case 46: sItem = "nw_it_mring018"; break; + case 47: sItem = "nw_maarcl093"; break; + case 48: sItem = "nw_it_mboots002"; break; + + } + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(42) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mbracer007"; break; + case 2: sItem = "nw_it_mbracer007"; break; + case 3: sItem = "nw_it_mneck012"; break; + case 4: sItem = "nw_maarcl088"; break; + case 5: sItem = "nw_it_mboots012"; break; + case 6: sItem = "nw_it_mneck026"; break; + case 7: sItem = "nw_it_mboots006"; break; + case 8: sItem = "nw_it_mbracer003"; break; + case 9: sItem = "nw_it_mneck008"; break; + case 10: sItem = "nw_it_mring008"; break; + case 11: sItem = "nw_maarcl056"; break; + case 12: sItem = "nw_maarcl092"; break; + case 13: sItem = "nw_it_mring014"; break; + case 14: sItem = "nw_it_mneck016"; break; + case 15: sItem = "nw_it_mboots013"; break; + case 16: sItem = "nw_it_mneck027"; break; + case 17: sItem = "nw_it_mbracer008"; break; + case 18: sItem = "nw_it_mneck013"; break; + case 19: sItem = "nw_maarcl089"; break; + case 20: sItem = "nw_it_mbelt012"; break; + case 21: sItem = "nw_it_mbelt013"; break; + case 22: sItem = "nw_it_mbelt014"; break; + case 23: sItem = "nw_it_mring027"; break; + case 24: sItem = "nw_it_mboots007"; break; + case 25: sItem = "nw_it_mbracer004"; break; + case 26: sItem = "nw_it_mneck009"; break; + case 27: sItem = "nw_it_mring018"; break; + case 28: sItem = "nw_maarcl093"; break; + case 29: sItem = "nw_it_mboots002"; break; + case 30: sItem = "nw_it_mboots014"; break; + case 31: sItem = "nw_it_mneck028"; break; + case 32: sItem = "nw_it_mring015"; break; + case 33: sItem = "nw_it_mbracer009"; break; + case 34: sItem = "nw_it_mneck014"; break; + case 35: sItem = "nw_maarcl090"; break; + case 36: sItem = "nw_it_mring028"; break; + case 37: sItem = "nw_it_mneck017"; break; + case 38: sItem = "nw_it_mboots008"; break; + case 39: sItem = "nw_it_mbracer005"; break; + case 40: sItem = "nw_it_mneck010"; break; + case 41: sItem = "nw_it_mmidmisc02"; break; + case 42: sItem = "nw_it_mring019"; break; + } + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(30) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mring027"; break; + case 2: sItem = "nw_it_mboots007"; break; + case 3: sItem = "nw_it_mbracer004"; break; + case 4: sItem = "nw_it_mneck009"; break; + case 5: sItem = "nw_it_mring018"; break; + case 6: sItem = "nw_maarcl093"; break; + case 7: sItem = "nw_it_mboots002"; break; + case 8: sItem = "nw_it_mboots014"; break; + case 9: sItem = "nw_it_mneck028"; break; + case 10: sItem = "nw_it_mring015"; break; + case 11: sItem = "nw_it_mbracer009"; break; + case 12: sItem = "nw_it_mneck014"; break; + case 13: sItem = "nw_maarcl090"; break; + case 14: sItem = "nw_it_mring028"; break; + case 15: sItem = "nw_it_mneck017"; break; + case 16: sItem = "nw_it_mboots008"; break; + case 17: sItem = "nw_it_mbracer005"; break; + case 18: sItem = "nw_it_mneck010"; break; + case 19: sItem = "nw_it_mmidmisc02"; break; + case 20: sItem = "nw_maarcl094"; break; + case 21: sItem = "nw_it_mring019"; break; + case 22: sItem = "nw_it_mring016"; break; + case 23: sItem = "nw_it_mbracer010"; break; + case 24: sItem = "nw_it_mneck015"; break; + case 25: sItem = "nw_maarcl091"; break; + case 26: sItem = "nw_it_mboots009"; break; + case 27: sItem = "nw_it_mbracer006"; break; + case 28: sItem = "nw_it_mneck011"; break; + case 29: sItem = "nw_maarcl095"; break; + case 30: sItem = "nw_it_mneck018"; break; + } + } + //dbSpeak("Create Misc"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + + // * this function just returns an item that is more appropriate + // * for this class. Only wizards, sorcerers, clerics, monks, rogues and bards get this + void CreateGenericClassItem(object oTarget, object oAdventurer, int nSpecific =0) + { + + + if (GetLevelByClass(CLASS_TYPE_DRUID, oAdventurer)>= 1) + { + if (nSpecific == 0) + { + CreateGenericDruidWeapon(oTarget, oAdventurer); + } + else + { + CreateSpecificDruidWeapon(oTarget, oAdventurer); + } + } + else + if (GetLevelByClass(CLASS_TYPE_WIZARD, oAdventurer)>= 1 || GetLevelByClass(CLASS_TYPE_SORCERER, oAdventurer) >= 1) + { + // * 30% chance of getting a magic scroll else get a weapon suited for a wizard + if (Random(100) + 1 > 70) + { + // * grab an arcane scroll as if the wizard had +4 levels + CreateArcaneScroll(oTarget, oAdventurer, 4); + } + else + if (nSpecific == 0) + { + CreateGenericWizardWeapon(oTarget, oAdventurer); + } + else + { + CreateSpecificWizardWeapon(oTarget, oAdventurer); + } + + + } + else + if (GetLevelByClass(CLASS_TYPE_CLERIC, oAdventurer)>= 1) + { + int nRandom = Random(4) + 1; + string sItem = "nw_it_medkit001"; + switch (nRandom) + { + case 1: sItem = "nw_it_medkit001"; break; + case 2: sItem = "nw_it_medkit002"; break; + case 3: sItem = "nw_it_medkit003"; break; + case 4: sItem = "nw_it_medkit004"; break; + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + else + if (GetLevelByClass(CLASS_TYPE_MONK, oAdventurer)>= 1) + { + //dbSpeak("in monk function"); + if (nSpecific == 0) + { + CreateGenericMonkWeapon(oTarget, oAdventurer); + } + else + { + CreateSpecificMonkWeapon(oTarget, oAdventurer); + } + } + else + if (GetLevelByClass(CLASS_TYPE_ROGUE, oAdventurer)>= 1) + { + // * give a misc item as if a couple levels higher + CreateGenericMiscItem(oTarget, oAdventurer, 2); + } + else + if (GetLevelByClass(CLASS_TYPE_BARD, oAdventurer)>= 1) + { + // * give a misc item as if a couple levels higher + CreateGenericMiscItem(oTarget, oAdventurer, 2); + } + + } + void CreateGenericRodStaffWand(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn013"; break; + case 2: sItem = "nw_wmgwn006"; break; + case 3: sItem = "nw_it_gem002"; break; // gem for variety + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn013"; break; + case 2: sItem = "nw_wmgwn006"; break; + case 3: sItem = "nw_it_gem002"; break;// gem for variety + } + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(4) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn006"; break; + case 2: sItem = "nw_wmgwn004"; break; + case 3: sItem = "nw_wmgrd002"; break; + case 4: sItem = "nw_wmgwn012"; break; + } + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgwn004"; break; + case 2: sItem = "nw_wmgwn002"; break; + case 3: sItem = "nw_wmgwn007"; break; + case 4: sItem = "nw_wmgwn003"; break; + case 5: sItem = "nw_wmgwn010"; break; + case 6: sItem = "nw_wmgwn011"; break; + case 7: sItem = "nw_wmgwn005"; break; + case 8: sItem = "nw_wmgwn008"; break; + case 9: sItem = "nw_wmgwn009"; break; + case 10: sItem = "nw_wmgrd002"; break; + case 11: sItem = "nw_wmgwn012"; break; + } + + } + else // * 2500 - 16500 + { + int nRandom = d8(); + switch (nRandom) + { + case 1: sItem = "nw_wmgwn002"; break; + case 2: sItem = "nw_wmgwn007"; break; + case 3: sItem = "nw_wmgwn003"; break; + case 4: sItem = "nw_wmgwn010"; break; + case 5: sItem = "nw_wmgwn011"; break; + case 6: sItem = "nw_wmgwn005"; break; + case 7: sItem = "nw_wmgwn008"; break; + case 8: sItem = "nw_wmgwn009"; break; + } + + } + //dbSpeak("Generic Rod staff wand"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + + void CreateGenericMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wbwsl001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspka001"; break; + case 7: sItem = "nw_wbwxh001"; break; + case 8: sItem = "nw_waxhn001"; break; + case 9: sItem = "nw_wbwxl001"; break; + case 10: sItem = "nw_wthmsh002"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wbwsl001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspka001"; break; + case 7: sItem = "nw_wbwxh001"; break; + case 8: sItem = "nw_waxhn001"; break; + case 9: sItem = "nw_wbwxl001"; break; + case 10: sItem = "nw_wthmsh002"; break; + case 11: sItem = "nw_wbwmsl001"; break; + case 12: sItem = "nw_wbwmxh002"; break; + case 13: sItem = "nw_wthmsh008"; break; + case 14: sItem = "nw_wbwmxl002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsl001"; break; + case 2: sItem = "nw_wbwmxh002"; break; + case 3: sItem = "nw_wthmsh008"; break; + case 4: sItem = "nw_wbwmxl002"; break; + case 5: sItem = "nw_wthmsh009"; break; + case 6: sItem = "nw_wblmcl002"; break; + case 7: sItem = "nw_wdbmqs002"; break; + case 8: sItem = "nw_wswmdg002"; break; + case 9: sItem = "nw_wspmka002"; break; + case 10: sItem = "nw_waxmhn002"; break; + case 11: sItem = "nw_wbwmsl009"; break; + case 12: sItem = "nw_wbwmxh008"; break; + case 13: sItem = "nw_wbwmxl008"; break; + } + + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh009"; break; + case 2: sItem = "nw_wblmcl002"; break; + case 3: sItem = "nw_wdbmqs002"; break; + case 4: sItem = "nw_wswmdg002"; break; + case 5: sItem = "nw_wspmka002"; break; + case 6: sItem = "nw_waxmhn002"; break; + case 7: sItem = "nw_wbwmsl009"; break; + case 8: sItem = "nw_wbwmxh008"; break; + case 9: sItem = "nw_wbwmxl008"; break; + case 10: sItem = "nw_wbwmsl010"; break; + case 11: sItem = "nw_wbwmxh009"; break; + case 12: sItem = "nw_wbwmxl009"; break; + case 13: sItem = "nw_wblmcl010"; break; + case 14: sItem = "nw_wdbmqs008"; break; + case 15: sItem = "nw_wswmdg008"; break; + case 16: sItem = "nw_wspmka008"; break; + case 17: sItem = "nw_waxmhn010"; break; + } + } + else // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsl010"; break; + case 2: sItem = "nw_wbwmxh009"; break; + case 3: sItem = "nw_wbwmxl009"; break; + case 4: sItem = "nw_wblmcl010"; break; + case 5: sItem = "nw_wdbmqs008"; break; + case 6: sItem = "nw_wswmdg008"; break; + case 7: sItem = "nw_wspmka008"; break; + case 8: sItem = "nw_waxmhn010"; break; + case 9: sItem = "nw_wblmcl011"; break; + case 10: sItem = "nw_wdbmqs009"; break; + case 11: sItem = "nw_wswmdg009"; break; + case 12: sItem = "nw_wspmka009"; break; + case 13: sItem = "nw_waxmhn011"; break; + } + } + //dbSpeak("Generic Monk Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificMonkWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 800 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh003"; break; + case 2: sItem = "nw_wthmsh006"; break; + case 3: CreateGenericMonkWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; break; + } + + } + else if (GetRange(2, nHD)) // * 2500 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh003"; break; + case 2: sItem = "nw_wthmsh006"; break; + case 3: sItem = "nw_wthmsh004"; break; + case 4: sItem = "nw_wthmsh007"; break; + case 5: sItem = "NW_IT_MGLOVE016"; break; + case 6: sItem = "NW_IT_MGLOVE021"; break; + case 7: sItem = "NW_IT_MGLOVE026"; break; + case 8: CreateGenericMonkWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(21) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh006"; break; + case 2: sItem = "nw_wthmsh004"; break; + case 3: sItem = "nw_wthmsh007"; break; + case 4: sItem = "nw_wbwmsl005"; break; + case 5: sItem = "nw_wbwmxh005"; break; + case 6: sItem = "nw_wspmka004"; break; + case 7: sItem = "nw_wbwmxl005"; break; + case 8: sItem = "nw_wspmka007"; break; + case 9: sItem = "nw_wswmdg006"; break; + case 10: sItem = "nw_wspmka005"; break; + case 11: sItem = "NW_IT_MGLOVE016"; break; + case 12: sItem = "NW_IT_MGLOVE021"; break; + case 13: sItem = "NW_IT_MGLOVE026"; break; + + case 14: sItem = "NW_IT_MGLOVE017"; break; + case 15: sItem = "NW_IT_MGLOVE022"; break; + case 16: sItem = "NW_IT_MGLOVE027"; break; + + case 17: sItem = "NW_IT_MGLOVE018"; break; + case 18: sItem = "NW_IT_MGLOVE023"; break; + case 19: sItem = "NW_IT_MGLOVE028"; break; + + case 20: sItem = "NW_IT_MGLOVE029"; break; + case 21: sItem = "NW_IT_MGLOVE030"; break; + + + } + + } + else if (GetRange(4, nHD)) // * 2500 -16500 + { + int nRandom = Random(22) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsl005"; break; + case 2: sItem = "nw_wbwmxh005"; break; + case 3: sItem = "nw_wspmka004"; break; + case 4: sItem = "nw_wbwmxl005"; break; + case 5: sItem = "nw_wspmka007"; break; + case 6: sItem = "nw_wswmdg006"; break; + case 7: sItem = "nw_wspmka005"; break; + case 8: sItem = "nw_wblmcl004"; break; + case 9: sItem = "nw_wblmcl003"; break; + case 10: sItem = "nw_wbwmsl003"; break; + case 11: sItem = "nw_wbwmxh003"; break; + case 12: sItem = "nw_waxmhn004"; break; + case 13: sItem = "nw_wbwmxl003"; break; + + case 14: sItem = "NW_IT_MGLOVE017"; break; + case 15: sItem = "NW_IT_MGLOVE022"; break; + + case 16: sItem = "NW_IT_MGLOVE018"; break; + case 17: sItem = "NW_IT_MGLOVE023"; break; + case 18: sItem = "NW_IT_MGLOVE028"; break; + + case 19: sItem = "NW_IT_MGLOVE029"; break; + case 20: sItem = "NW_IT_MGLOVE030"; break; + + case 21: sItem = "NW_IT_MGLOVE019"; break; + case 22: sItem = "NW_IT_MGLOVE024"; break; + + + } + + } + else // * 16000 + + { + int nRandom = Random(24) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl003"; break; + case 2: sItem = "nw_wspmka006"; break; + case 3: sItem = "nw_wbwmxl004"; break; + case 4: sItem = "nw_wspmka003"; break; + case 5: sItem = "nw_wbwmxl007"; break; + case 6: sItem = "nw_waxmhn003"; break; + case 7: sItem = "nw_wblmcl005"; break; + case 8: sItem = "nw_wswmdg004"; break; + case 9: sItem = "nw_wbwmsl007"; break; + case 10: sItem = "nw_wbwmxh004"; break; + case 11: sItem = "nw_waxmhn005"; break; + case 12: sItem = "nw_wbwmxh007"; break; + case 13: sItem = "nw_wswmdg003"; break; + case 14: sItem = "nw_wswmdg007"; break; + case 15: sItem = "nw_wbwmsl006"; break; + case 16: sItem = "nw_wbwmsl008"; break; + case 17: sItem = "nw_wblmcl006"; break; + case 18: sItem = "nw_wbwmsl004"; break; + case 19: sItem = "nw_waxmhn006"; break; + case 20: sItem = "nw_wbwmxh006"; break; + case 21: sItem = "nw_wswmdg005"; break; + case 22: sItem = "nw_wbwmxl006"; break; + + case 23: sItem = "NW_IT_MGLOVE020"; break; + case 24: sItem = "NW_IT_MGLOVE025"; break; + + } + + } + //dbSpeak("Specific Monk Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + + void CreateGenericDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspsc001"; break; + case 7: sItem = "nw_wswsc001"; break; + case 8: sItem = "nw_wthmdt002"; break; + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wdbqs001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wswdg001"; break; + case 6: sItem = "nw_wspsc001"; break; + case 7: sItem = "nw_wswsc001"; break; + case 8: sItem = "nw_wthmdt002"; break; + case 9: sItem = "nw_wthmdt005"; break; + case 10: sItem = "nw_wbwmsl001"; break; + case 11: sItem = "nw_wthmdt008"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt005"; break; + case 2: sItem = "nw_wbwmsl001"; break; + case 3: sItem = "nw_wthmdt008"; break; + case 4: sItem = "nw_wthmdt009"; break; + case 5: sItem = "nw_wthmdt006"; break; + case 6: sItem = "nw_wblmcl002"; break; + case 7: sItem = "nw_wdbmqs002"; break; + case 8: sItem = "nw_wplmss002"; break; + case 9: sItem = "nw_wswmdg002"; break; + case 10: sItem = "nw_wspmsc002"; break; + case 11: sItem = "nw_wswmsc002"; break; + case 12: sItem = "nw_wthmdt003"; break; + case 13: sItem = "nw_wbwmsl009"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt009"; break; + case 2: sItem = "nw_wthmdt006"; break; + case 3: sItem = "nw_wblmcl002"; break; + case 4: sItem = "nw_wdbmqs002"; break; + case 5: sItem = "nw_wplmss002"; break; + case 6: sItem = "nw_wswmdg002"; break; + case 7: sItem = "nw_wspmsc002"; break; + case 8: sItem = "nw_wswmsc002"; break; + case 9: sItem = "nw_wthmdt003"; break; + case 10: sItem = "nw_wbwmsl009"; break; + case 11: sItem = "nw_wthmdt007"; break; + case 12: sItem = "nw_wthmdt004"; break; + case 13: sItem = "nw_wbwmsl010"; break; + case 14: sItem = "nw_wblmcl010"; break; + case 15: sItem = "nw_wdbmqs008"; break; + case 16: sItem = "nw_wplmss010"; break; + case 17: sItem = "nw_wswmdg008"; break; + case 18: sItem = "nw_wspmsc010"; break; + case 19: sItem = "nw_wswmsc010"; break; + } + + } + else // * 2500 - 16500 + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt007"; break; + case 2: sItem = "nw_wthmdt004"; break; + case 3: sItem = "nw_wbwmsl010"; break; + case 4: sItem = "nw_wblmcl010"; break; + case 5: sItem = "nw_wdbmqs008"; break; + case 6: sItem = "nw_wplmss010"; break; + case 7: sItem = "nw_wswmdg008"; break; + case 8: sItem = "nw_wspmsc010"; break; + case 9: sItem = "nw_wswmsc010"; break; + case 10: sItem = "nw_wblmcl011"; break; + case 11: sItem = "nw_wdbmqs009"; break; + case 12: sItem = "nw_wplmss011"; break; + case 13: sItem = "nw_wswmdg009"; break; + case 14: sItem = "nw_wspmsc011"; break; + case 15: sItem = "nw_wswmsc011"; break; + } + + } + //dbSpeak("Generic Druid weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + + } + void CreateSpecificDruidWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericDruidWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + + } + else if (GetRange(2, nHD)) // * 2500 + { + CreateGenericDruidWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmsl005"; break; + case 4: sItem = "nw_wswmdg006"; break; + case 5: CreateGenericDruidWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 -16500 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmsl005"; break; + case 4: sItem = "nw_wswmdg006"; break; + case 5: sItem = "nw_wblmcl004"; break; + case 6: sItem = "nw_wdbmqs004"; break; + case 7: sItem = "nw_wblmcl003"; break; + case 8: sItem = "nw_wbwmsl003"; break; + case 9: sItem = "nw_wswmsc004"; break; + case 10: sItem = "nw_wplmss005"; break; + } + + } + else // * 16000 + + { + int nRandom = Random(18) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs003"; break; + case 2: sItem = "nw_wblmcl005"; break; + case 3: sItem = "nw_wplmss007"; break; + case 4: sItem = "nw_wswmdg004"; break; + case 5: sItem = "nw_wbwmsl007"; break; + case 6: sItem = "nw_wplmss006"; break; + case 7: sItem = "nw_wswmsc006"; break; + case 8: sItem = "nw_wswmdg003"; break; + case 9: sItem = "nw_wswmdg007"; break; + case 10: sItem = "nw_wswmsc007"; break; + case 11: sItem = "nw_wbwmsl006"; break; + case 12: sItem = "nw_wbwmsl008"; break; + case 13: sItem = "nw_wdbmqs007"; break; + case 14: sItem = "nw_wblmcl006"; break; + case 15: sItem = "nw_wbwmsl004"; break; + case 16: sItem = "nw_wswmsc005"; break; + case 17: sItem = "nw_wplmss004"; break; + case 18: sItem = "nw_wswmdg005"; break; + } + + } + //dbSpeak("specific druid weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + + void CreateGenericWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblcl001"; break; + case 2: sItem = "nw_wdbqs001"; break; + case 3: sItem = "nw_wswdg001"; break; + case 4: sItem = "nw_wbwxh001"; break; + case 5: sItem = "nw_wbwxl001"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblcl001"; break; + case 2: sItem = "nw_wdbqs001"; break; + case 3: sItem = "nw_wswdg001"; break; + case 4: sItem = "nw_wbwxh001"; break; + case 5: sItem = "nw_wbwxl001"; break; + case 6: sItem = "nw_wbwmxl002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl002"; break; + case 2: sItem = "nw_wblmcl002"; break; + case 3: sItem = "nw_wdbmqs002"; break; + case 4: sItem = "nw_wswmdg002"; break; + case 5: sItem = "nw_wbwmxh008"; break; + case 6: sItem = "nw_wbwmxl008"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmcl002"; break; + case 2: sItem = "nw_wdbmqs002"; break; + case 3: sItem = "nw_wswmdg002"; break; + case 4: sItem = "nw_wbwmxh008"; break; + case 5: sItem = "nw_wbwmxl008"; break; + case 6: sItem = "nw_wbwmxh009"; break; + case 7: sItem = "nw_wbwmxl009"; break; + case 8: sItem = "nw_wblmcl010"; break; + case 9: sItem = "nw_wdbmqs008"; break; + case 10: sItem = "nw_wswmdg008"; break; + } + + } + else // * 2500 - 16500 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxh009"; break; + case 2: sItem = "nw_wbwmxl009"; break; + case 3: sItem = "nw_wblmcl010"; break; + case 4: sItem = "nw_wdbmqs008"; break; + case 5: sItem = "nw_wswmdg008"; break; + case 6: sItem = "nw_wblmcl011"; break; + case 7: sItem = "nw_wdbmqs009"; break; + case 8: sItem = "nw_wswmdg009"; break; + } + + } + //dbSpeak("Generic Wizard or Sorcerer Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + void CreateSpecificWizardWeapon(object oTarget, object oAdventurer, int nModifier = 0) + { + + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericWizardWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + } + else if (GetRange(2, nHD)) // * 2500 + { + CreateGenericWizardWeapon(oTarget, oAdventurer, JUMP_LEVEL); return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmxh005"; break; + case 4: sItem = "nw_wbwmxl005"; break; + case 5: sItem = "nw_wswmdg006"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 -16500 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs006"; break; + case 3: sItem = "nw_wbwmxh005"; break; + case 4: sItem = "nw_wbwmxl005"; break; + case 5: sItem = "nw_wswmdg006"; break; + case 6: sItem = "nw_wblmcl004"; break; + case 7: sItem = "nw_wdbmqs004"; break; + case 8: sItem = "nw_wblmcl003"; break; + case 9: sItem = "nw_wbwmxh003"; break; + case 10: sItem = "nw_wbwmxl003"; break; + } + + } + else // * 16000 + + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl003"; break; + case 2: sItem = "nw_wdbmqs003"; break; + case 3: sItem = "nw_wbwmxl004"; break; + case 4: sItem = "nw_wbwmxl007"; break; + case 5: sItem = "nw_wblmcl005"; break; + case 6: sItem = "nw_wswmdg004"; break; + case 7: sItem = "nw_wbwmxh004"; break; + case 8: sItem = "nw_wbwmxh007"; break; + case 9: sItem = "nw_wswmdg003"; break; + case 10: sItem = "nw_wswmdg007"; break; + case 11: sItem = "nw_wdbmqs007"; break; + case 12: sItem = "nw_wblmcl006"; break; + case 13: sItem = "nw_wbwmxh006"; break; + case 14: sItem = "nw_wswmdg005"; break; + case 15: sItem = "nw_wbwmxl006"; break; + } + + } + //dbSpeak("Specific Wizard or Sorcerer Weapon"); + + dbCreateItemOnObject(sItem, oTarget, 1); + + } + + void CreateGenericSimple(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = d12(); + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wbwsl001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wdbqs001"; break; + case 6: sItem = "nw_wswdg001"; break; + case 7: sItem = "nw_wblml001"; break; + case 8: sItem = "nw_wbwxh001"; break; + case 9: sItem = "nw_wspsc001"; break; + case 10: sItem = "nw_wblms001"; break; + case 11: sItem = "nw_wbwxl001"; break; + case 12: sItem = "nw_wthmdt002"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthdt001"; break; + case 2: sItem = "nw_wblcl001"; break; + case 3: sItem = "nw_wbwsl001"; break; + case 4: sItem = "nw_wplss001"; break; + case 5: sItem = "nw_wdbqs001"; break; + case 6: sItem = "nw_wswdg001"; break; + case 7: sItem = "nw_wblml001"; break; + case 8: sItem = "nw_wbwxh001"; break; + case 9: sItem = "nw_wspsc001"; break; + case 10: sItem = "nw_wblms001"; break; + case 11: sItem = "nw_wbwxl001"; break; + case 12: sItem = "nw_wthmdt002"; break; + case 13: sItem = "nw_wthmdt005"; break; + case 14: sItem = "nw_wbwmsl001"; break; + case 15: sItem = "nw_wbwmxh002"; break; + case 16: sItem = "nw_wthmdt008"; break; + case 17: sItem = "nw_wbwmxl002"; break; + } + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt005"; break; + case 2: sItem = "nw_wbwmsl001"; break; + case 3: sItem = "nw_wbwmxh002"; break; + case 4: sItem = "nw_wthmdt008"; break; + case 5: sItem = "nw_wbwmxl002"; break; + case 6: sItem = "nw_wthmdt009"; break; + case 7: sItem = "nw_wthmdt006"; break; + case 8: sItem = "nw_wblmcl002"; break; + case 9: sItem = "nw_wplmss002"; break; + case 10: sItem = "nw_wdbmqs002"; break; + case 11: sItem = "nw_wswmdg002"; break; + case 12: sItem = "nw_wblmml002"; break; + case 13: sItem = "nw_wspmsc002"; break; + case 14: sItem = "nw_wblmms002"; break; + case 15: sItem = "nw_wthmdt003"; break; + case 16: sItem = "nw_wthmdt003"; break; + case 17: sItem = "nw_wbwmsl009"; break; + case 18: sItem = "nw_wbwmxh008"; break; + case 19: sItem = "nw_wbwmxl008"; break; + } + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(27) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt009"; break; + case 2: sItem = "nw_wthmdt006"; break; + case 3: sItem = "nw_wblmcl002"; break; + case 4: sItem = "nw_wplmss002"; break; + case 5: sItem = "nw_wdbmqs002"; break; + case 6: sItem = "nw_wswmdg002"; break; + case 7: sItem = "nw_wblmml002"; break; + case 8: sItem = "nw_wspmsc002"; break; + case 9: sItem = "nw_wblmms002"; break; + case 10: sItem = "nw_wthmdt003"; break; + case 11: sItem = "nw_wthmdt003"; break; + case 12: sItem = "nw_wbwmsl009"; break; + case 13: sItem = "nw_wbwmxh008"; break; + case 14: sItem = "nw_wbwmxl008"; break; + case 15: sItem = "nw_wthmdt007"; break; + case 16: sItem = "nw_wthmdt004"; break; + case 17: sItem = "nw_wbwmsl010"; break; + case 18: sItem = "nw_wbwmxh009"; break; + case 19: sItem = "nw_wbwmxl009"; break; + case 20: sItem = "nw_wbwmsl005"; break; + case 21: sItem = "nw_wblmcl010"; break; + case 22: sItem = "nw_wplmss010"; break; + case 23: sItem = "nw_wdbmqs008"; break; + case 24: sItem = "nw_wswmdg008"; break; + case 25: sItem = "nw_wblmml011"; break; + case 26: sItem = "nw_wspmsc010"; break; + case 27: sItem = "nw_wblmms010"; break; + + + + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(23) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmdt007"; break; + case 2: sItem = "nw_wthmdt004"; break; + case 3: sItem = "nw_wbwmsl010"; break; + case 4: sItem = "nw_wbwmxh009"; break; + case 5: sItem = "nw_wbwmxl009"; break; + case 6: sItem = "nw_wbwmsl005"; break; + case 7: sItem = "nw_wblmcl010"; break; + case 8: sItem = "nw_wplmss010"; break; + case 9: sItem = "nw_wdbmqs008"; break; + case 10: sItem = "nw_wswmdg008"; break; + case 11: sItem = "nw_wblmml011"; break; + case 12: sItem = "nw_wspmsc010"; break; + case 13: sItem = "nw_wblmms010"; break; + case 14: sItem = "nw_wblmms010"; break; + case 15: sItem = "nw_wblmms010"; break; + case 16: sItem = "nw_wblmms010"; break; + case 17: sItem = "nw_wblmcl011"; break; + case 18: sItem = "nw_wplmss011"; break; + case 19: sItem = "nw_wdbmqs009"; break; + case 20: sItem = "nw_wswmdg009"; break; + case 21: sItem = "nw_wblmml012"; break; + case 22: sItem = "nw_wspmsc011"; break; + case 23: sItem = "nw_wblmms011"; break; + + + + } + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(7) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmcl011"; break; + case 2: sItem = "nw_wplmss011"; break; + case 3: sItem = "nw_wdbmqs009"; break; + case 4: sItem = "nw_wswmdg009"; break; + case 5: sItem = "nw_wblmml012"; break; + case 6: sItem = "nw_wspmsc011"; break; + case 7: sItem = "nw_wblmms011"; break; + + + + } + } + //dbSpeak("Create Generic SImple; Specific = " + IntToString(nModifier)); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericMartial(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + + int nHD = GetHitDice(oAdventurer) +nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthax001"; break; + case 2: sItem = "nw_wblhl001"; break; + case 3: sItem = "nw_waxhn001"; break; + case 4: sItem = "nw_wblfl001"; break; + case 5: sItem = "nw_waxbt001"; break; + case 6: sItem = "nw_wplhb001"; break; + case 7: sItem = "nw_wswss001"; break; + case 8: sItem = "nw_wblhw001"; break; + case 9: sItem = "nw_wblfh001"; break; + case 10: sItem = "nw_wswls001"; break; + case 11: sItem = "nw_wswsc001"; break; + case 12: sItem = "nw_waxgr001"; break; + case 13: sItem = "nw_wswrp001"; break; + case 14: sItem = "nw_wbwsh001"; break; + case 15: sItem = "nw_wswbs001"; break; + case 16: sItem = "nw_wswgs001"; break; + case 17: sItem = "nw_wbwln001"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(20) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthax001"; break; + case 2: sItem = "nw_wblhl001"; break; + case 3: sItem = "nw_waxhn001"; break; + case 4: sItem = "nw_wblfl001"; break; + case 5: sItem = "nw_waxbt001"; break; + case 6: sItem = "nw_wplhb001"; break; + case 7: sItem = "nw_wswss001"; break; + case 8: sItem = "nw_wblhw001"; break; + case 9: sItem = "nw_wblfh001"; break; + case 10: sItem = "nw_wswls001"; break; + case 11: sItem = "nw_wswsc001"; break; + case 12: sItem = "nw_waxgr001"; break; + case 13: sItem = "nw_wswrp001"; break; + case 14: sItem = "nw_wbwsh001"; break; + case 15: sItem = "nw_wswbs001"; break; + case 16: sItem = "nw_wswgs001"; break; + case 17: sItem = "nw_wbwln001"; break; + case 18: sItem = "nw_wthmax002"; break; + case 19: sItem = "nw_wbwmsh002"; break; + case 20: sItem = "nw_wbwmln002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(20) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmax002"; break; + case 2: sItem = "nw_wbwmsh002"; break; + case 3: sItem = "nw_wbwmln002"; break; + case 4: sItem = "nw_wblmhl002"; break; + case 5: sItem = "nw_waxmhn002"; break; + case 6: sItem = "nw_wblmfl002"; break; + case 7: sItem = "nw_waxmbt002"; break; + case 8: sItem = "nw_wplmhb002"; break; + case 9: sItem = "nw_wblmhw002"; break; + case 10: sItem = "nw_wblmfh002"; break; + case 11: sItem = "nw_wswmls002"; break; + case 12: sItem = "nw_wswmsc002"; break; + case 13: sItem = "nw_waxmgr002"; break; + case 14: sItem = "nw_wswmrp002"; break; + case 15: sItem = "nw_wswmbs002"; break; + case 16: sItem = "nw_wswmgs002"; break; + case 17: sItem = "nw_wthmax008"; break; + case 18: sItem = "nw_wbwmsh008"; break; + case 19: sItem = "nw_wbwmln008"; break; + case 20: sItem = "nw_wswmss002"; break; + + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(33) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmhl002"; break; + case 2: sItem = "nw_waxmhn002"; break; + case 3: sItem = "nw_wblmfl002"; break; + case 4: sItem = "nw_waxmbt002"; break; + case 5: sItem = "nw_wplmhb002"; break; + case 6: sItem = "nw_wblmhw002"; break; + case 7: sItem = "nw_wblmfh002"; break; + case 8: sItem = "nw_wswmls002"; break; + case 9: sItem = "nw_wswmsc002"; break; + case 10: sItem = "nw_waxmgr002"; break; + case 11: sItem = "nw_wswmrp002"; break; + case 12: sItem = "nw_wswmbs002"; break; + case 13: sItem = "nw_wswmgs002"; break; + case 14: sItem = "nw_wthmax008"; break; + case 15: sItem = "nw_wbwmsh008"; break; + case 16: sItem = "nw_wbwmln008"; break; + case 17: sItem = "nw_wbwmsh009"; break; + case 18: sItem = "nw_wbwmln009"; break; + case 19: sItem = "nw_wblmhl010"; break; + case 20: sItem = "nw_waxmhn010"; break; + case 21: sItem = "nw_wblmfl010"; break; + case 22: sItem = "nw_waxmbt010"; break; + case 23: sItem = "nw_wplmhb010"; break; + case 24: sItem = "nw_wblmhw011"; break; + case 25: sItem = "nw_wblmfh010"; break; + case 26: sItem = "nw_wswmls010"; break; + case 27: sItem = "nw_waxmgr009"; break; + case 28: sItem = "nw_wswmbs009"; break; + case 29: sItem = "nw_wswmgs011"; break; + case 30: sItem = "nw_wswmrp010"; break; + case 31: sItem = "nw_wswmsc010"; break; + case 32: sItem = "nw_wswmss002"; break; + case 33: sItem = "nw_wswmss009"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(20) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmsh009"; break; + case 2: sItem = "nw_wbwmln009"; break; + case 3: sItem = "nw_wblmhl010"; break; + case 4: sItem = "nw_waxmhn010"; break; + case 5: sItem = "nw_wblmfl010"; break; + case 6: sItem = "nw_waxmbt010"; break; + case 7: sItem = "nw_wplmhb010"; break; + case 8: sItem = "nw_wblmhw011"; break; + case 9: sItem = "nw_wblmfh010"; break; + case 10: sItem = "nw_wswmls010"; break; + case 11: sItem = "nw_waxmgr009"; break; + case 12: sItem = "nw_wswmbs009"; break; + case 13: sItem = "nw_wswmgs011"; break; + case 14: sItem = "nw_wthmax009"; break; + case 15: sItem = "nw_wswmrp010"; break; + case 16: sItem = "nw_wswmrp011"; break; + case 17: sItem = "nw_wswmsc010"; break; + case 18: sItem = "nw_wswmss009"; break; + case 19: sItem = "nw_wswmsc011"; break; + case 20: sItem = "nw_wswmss011"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmax009"; break; + case 2: sItem = "nw_waxmhn011"; break; + case 3: sItem = "nw_wblmfl011"; break; + case 4: sItem = "nw_waxmbt011"; break; + case 5: sItem = "nw_wplmhb011"; break; + case 6: sItem = "nw_wblmhw012"; break; + case 7: sItem = "nw_wblmfh011"; break; + case 8: sItem = "nw_wswmls012"; break; + case 9: sItem = "nw_waxmgr011"; break; + case 10: sItem = "nw_wswmbs010"; break; + case 11: sItem = "nw_wswmgs012"; break; + case 12: sItem = "nw_wswmrp011"; break; + case 13: sItem = "nw_wswmsc011"; break; + case 14: sItem = "nw_wswmss011"; break; + } + + } + + //dbSpeak("Create Generic Martial"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericExotic(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wspka001"; break; + case 3: sItem = "nw_wspku001"; break; + case 4: sItem = "nw_wplsc001"; break; + case 5: sItem = "nw_wdbax001"; break; + case 6: sItem = "nw_wdbma001"; break; + case 7: sItem = "nw_wswka001"; break; + case 8: sItem = "nw_wthmsh002"; break; + case 9: sItem = "nw_wdbsw001"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthsh001"; break; + case 2: sItem = "nw_wspka001"; break; + case 3: sItem = "nw_wspku001"; break; + case 4: sItem = "nw_wplsc001"; break; + case 5: sItem = "nw_wdbax001"; break; + case 6: sItem = "nw_wdbma001"; break; + case 7: sItem = "nw_wswka001"; break; + case 8: sItem = "nw_wthmsh002"; break; + case 9: sItem = "nw_wdbsw001"; break; + case 10: sItem = "nw_wthmsh005"; break; + case 11: sItem = "nw_wspmka002"; break; + case 12: sItem = "nw_wspmku002"; break; + case 13: sItem = "nw_wplmsc002"; break; + case 14: sItem = "nw_wdbmax002"; break; + case 15: sItem = "nw_wdbmma002"; break; + case 16: sItem = "nw_wswmka002"; break; + case 17: sItem = "nw_wdbmsw002"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbsw001"; break; + case 2: sItem = "nw_wthmsh005"; break; + case 3: sItem = "nw_wspmka002"; break; + case 4: sItem = "nw_wspmku002"; break; + case 5: sItem = "nw_wplmsc002"; break; + case 6: sItem = "nw_wdbmax002"; break; + case 7: sItem = "nw_wdbmma002"; break; + case 8: sItem = "nw_wswmka002"; break; + case 9: sItem = "nw_wdbmsw002"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh005"; break; + case 2: sItem = "nw_wspmka002"; break; + case 3: sItem = "nw_wspmku002"; break; + case 4: sItem = "nw_wplmsc002"; break; + case 5: sItem = "nw_wdbmax002"; break; + case 6: sItem = "nw_wdbmma002"; break; + case 7: sItem = "nw_wswmka002"; break; + case 8: sItem = "nw_wdbmsw002"; break; + case 9: sItem = "nw_wthmsh008"; break; + case 10: sItem = "nw_wspmka008"; break; + case 11: sItem = "nw_wspmku008"; break; + case 12: sItem = "nw_wplmsc010"; break; + case 13: sItem = "nw_wdbmax010"; break; + case 14: sItem = "nw_wdbmma010"; break; + case 15: sItem = "nw_wswmka010"; break; + case 16: sItem = "nw_wdbmsw010"; break; + case 17: sItem = "nw_wthmsh009"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wspmka008"; break; + case 2: sItem = "nw_wspmku008"; break; + case 3: sItem = "nw_wplmsc010"; break; + case 4: sItem = "nw_wdbmax010"; break; + case 5: sItem = "nw_wdbmma010"; break; + case 6: sItem = "nw_wswmka010"; break; + case 7: sItem = "nw_wdbmsw010"; break; + case 8: sItem = "nw_wthmsh009"; break; + case 9: sItem = "nw_wspmka009"; break; + case 10: sItem = "nw_wspmku009"; break; + case 11: sItem = "nw_wplmsc011"; break; + case 12: sItem = "nw_wdbmax011"; break; + case 13: sItem = "nw_wdbmma011"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmsw010"; break; + case 2: sItem = "nw_wthmsh009"; break; + case 3: sItem = "nw_wspmka009"; break; + case 4: sItem = "nw_wspmku009"; break; + case 5: sItem = "nw_wplmsc011"; break; + case 6: sItem = "nw_wdbmax011"; break; + case 7: sItem = "nw_wdbmma011"; break; + case 8: sItem = "nw_wswmka011"; break; + case 9: sItem = "nw_wdbmsw011"; break; + } + + } + //dbSpeak("Create generic exotic"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericLightArmor(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_aarcl009"; break; + case 2: sItem = "nw_ashsw001"; break; + case 3: sItem = "nw_aarcl001"; break; + case 4: sItem = "nw_aarcl002"; break; + case 5: sItem = "nw_aarcl012"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_aarcl009"; break; + case 2: sItem = "nw_ashsw001"; break; + case 3: sItem = "nw_aarcl001"; break; + case 4: sItem = "nw_aarcl002"; break; + case 5: sItem = "nw_aarcl012"; break; + case 6: sItem = "nw_maarcl043"; break; + case 7: sItem = "nw_ashmsw002"; break; + case 8: sItem = "nw_maarcl044"; break; + case 9: sItem = "nw_maarcl045"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl043"; break; + case 2: sItem = "nw_ashmsw002"; break; + case 3: sItem = "nw_maarcl044"; break; + case 4: sItem = "nw_maarcl045"; break; + case 5: sItem = "nw_maarcl072"; break; + case 6: sItem = "nw_ashmsw008"; break; + case 7: sItem = "nw_maarcl071"; break; + case 8: sItem = "nw_maarcl075"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl072"; break; + case 2: sItem = "nw_ashmsw008"; break; + case 3: sItem = "nw_maarcl071"; break; + case 4: sItem = "nw_maarcl075"; break; + case 5: sItem = "nw_maarcl084"; break; + case 6: sItem = "nw_ashmsw009"; break; + case 7: sItem = "nw_maarcl083"; break; + case 8: sItem = "nw_maarcl087"; break; + case 9: sItem = "nw_maarcl079"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl084"; break; + case 2: sItem = "nw_ashmsw009"; break; + case 3: sItem = "nw_maarcl083"; break; + case 4: sItem = "nw_maarcl087"; break; + case 5: sItem = "nw_maarcl079"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl084"; break; + case 2: sItem = "nw_ashmsw009"; break; + case 3: sItem = "nw_maarcl083"; break; + case 4: sItem = "nw_maarcl087"; break; + case 5: sItem = "nw_maarcl079"; break; + } + + } + //dbSpeak("Create Generic light"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericMediumArmor(object oTarget, object oAdventurer, int nModifier = 0) + { + int nHD = GetHitDice(oAdventurer) + nModifier; + string sItem = ""; + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_arhe001"; break; + case 2: sItem = "nw_arhe002"; break; + case 3: sItem = "nw_arhe003"; break; + case 4: sItem = "nw_arhe004"; break; + case 5: sItem = "nw_arhe005"; break; + case 6: sItem = "nw_aarcl008"; break; + case 7: sItem = "nw_ashlw001"; break; + case 8: sItem = "nw_aarcl003"; break; + case 9: sItem = "nw_aarcl004"; break; + case 10: sItem = "nw_aarcl010"; break; + } + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_arhe001"; break; + case 2: sItem = "nw_arhe002"; break; + case 3: sItem = "nw_arhe003"; break; + case 4: sItem = "nw_arhe004"; break; + case 5: sItem = "nw_arhe005"; break; + case 6: sItem = "nw_aarcl008"; break; + case 7: sItem = "nw_ashlw001"; break; + case 8: sItem = "nw_aarcl003"; break; + case 9: sItem = "nw_aarcl004"; break; + case 10: sItem = "nw_aarcl010"; break; + case 11: sItem = "nw_maarcl047"; break; + case 12: sItem = "nw_ashmlw002"; break; + case 13: sItem = "nw_maarcl046"; break; + case 14: sItem = "nw_maarcl048"; break; + case 15: sItem = "nw_maarcl035"; break; + case 16: sItem = "nw_maarcl049"; break; + case 17: sItem = "nw_maarcl050"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl047"; break; + case 2: sItem = "nw_ashmlw002"; break; + case 3: sItem = "nw_maarcl046"; break; + case 4: sItem = "nw_maarcl048"; break; + case 5: sItem = "nw_maarcl035"; break; + case 6: sItem = "nw_maarcl049"; break; + case 7: sItem = "nw_maarcl050"; break; + case 8: sItem = "nw_maarcl070"; break; + case 9: sItem = "nw_ashmlw008"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl035"; break; + case 2: sItem = "nw_maarcl049"; break; + case 3: sItem = "nw_maarcl050"; break; + case 4: sItem = "nw_maarcl070"; break; + case 5: sItem = "nw_ashmlw008"; break; + case 6: sItem = "nw_maarcl067"; break; + case 7: sItem = "nw_maarcl073"; break; + case 8: sItem = "nw_maarcl065"; break; + case 9: sItem = "nw_maarcl066"; break; + case 10: sItem = "nw_maarcl082"; break; + case 11: sItem = "nw_ashmlw009"; break; + case 12: sItem = "nw_maarcl085"; break; + case 13: sItem = "nw_maarcl077"; break; + case 14: sItem = "nw_maarcl078"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl070"; break; + case 2: sItem = "nw_ashmlw008"; break; + case 3: sItem = "nw_maarcl067"; break; + case 4: sItem = "nw_maarcl073"; break; + case 5: sItem = "nw_maarcl065"; break; + case 6: sItem = "nw_maarcl066"; break; + case 7: sItem = "nw_maarcl082"; break; + case 8: sItem = "nw_ashmlw009"; break; + case 9: sItem = "nw_maarcl085"; break; + case 10: sItem = "nw_maarcl077"; break; + case 11: sItem = "nw_maarcl078"; break; + } + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl070"; break; + case 2: sItem = "nw_ashmlw008"; break; + case 3: sItem = "nw_maarcl067"; break; + case 4: sItem = "nw_maarcl073"; break; + case 5: sItem = "nw_maarcl065"; break; + case 6: sItem = "nw_maarcl066"; break; + case 7: sItem = "nw_maarcl082"; break; + case 8: sItem = "nw_ashmlw009"; break; + case 9: sItem = "nw_maarcl085"; break; + case 10: sItem = "nw_maarcl077"; break; + case 11: sItem = "nw_maarcl078"; break; + } + + } + //dbSpeak("Create Generic medium"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateGenericHeavyArmor(object oTarget, object oAdventurer, int nModifier = 0) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer) + nModifier; + + if (GetRange(1, nHD)) // * 200 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashto001"; break; + case 2: sItem = "nw_aarcl005"; break; + case 3: sItem = "nw_aarcl011"; break; + } + + } + else if (GetRange(2, nHD)) // * 800 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashto001"; break; + case 2: sItem = "nw_aarcl005"; break; + case 3: sItem = "nw_aarcl011"; break; + case 4: sItem = "nw_aarcl006"; break; + case 5: sItem = "nw_ashmto002"; break; + case 6: sItem = "nw_maarcl051"; break; + } + + } + else if (GetRange(3, nHD)) // * 200 - 2500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_aarcl005"; break; + case 2: sItem = "nw_aarcl011"; break; + case 3: sItem = "nw_aarcl006"; break; + case 4: sItem = "nw_ashmto002"; break; + case 5: sItem = "nw_maarcl051"; break; + case 6: sItem = "nw_maarcl052"; break; + case 7: sItem = "nw_aarcl007"; break; + case 8: sItem = "nw_maarcl053"; break; + case 9: sItem = "nw_ashmto008"; break; + } + + } + else if (GetRange(4, nHD)) // * 800 - 10000 + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl051"; break; + case 2: sItem = "nw_maarcl052"; break; + case 3: sItem = "nw_aarcl007"; break; + case 4: sItem = "nw_maarcl053"; break; + case 5: sItem = "nw_ashmto008"; break; + case 6: sItem = "nw_maarcl064"; break; + case 7: sItem = "nw_maarcl074"; break; + case 8: sItem = "nw_maarcl069"; break; + case 9: sItem = "nw_maarcl068"; break; + case 10: sItem = "nw_ashmto003"; break; + case 11: sItem = "nw_ashmto009"; break; + case 12: sItem = "nw_maarcl076"; break; + case 13: sItem = "nw_maarcl086"; break; + case 14: sItem = "nw_maarcl081"; break; + case 15: sItem = "nw_maarcl080"; break; + } + + } + else if (GetRange(5, nHD)) // * 2500 - 16500 + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashmto008"; break; + case 2: sItem = "nw_maarcl064"; break; + case 3: sItem = "nw_maarcl074"; break; + case 4: sItem = "nw_maarcl069"; break; + case 5: sItem = "nw_maarcl068"; break; + case 6: sItem = "nw_ashmto009"; break; + case 7: sItem = "nw_maarcl076"; break; + case 8: sItem = "nw_maarcl086"; break; + case 9: sItem = "nw_maarcl081"; break; + case 10: sItem = "nw_maarcl080"; break; + } + + + } + else if (GetRange(6, nHD)) // * 8000 - 25000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashmto009"; break; + case 2: sItem = "nw_maarcl076"; break; + case 3: sItem = "nw_maarcl086"; break; + case 4: sItem = "nw_maarcl081"; break; + case 5: sItem = "nw_maarcl080"; break; + } + + } + // dbSpeak("Create Generic heavy"); + + dbCreateItemOnObject(sItem, oTarget, 1); + } + // * + // * SPECIC TREASURE ITEMS (re: Named Items) + // * + void CreateSpecificMiscItem(object oTarget,object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericMiscItem(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericMiscItem(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_maarcl057"; break; + case 3: sItem = "nw_it_mbelt005"; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl057"; break; + case 2: sItem = "nw_it_mbelt005"; break; + case 3: sItem = "nw_maarcl101"; break; + case 4: sItem = "nw_maarcl102"; break; + case 5: sItem = "nw_maarcl103"; break; + case 6: sItem = "nw_it_mglove001"; break; + case 7: sItem = "nw_maarcl100"; break; + case 8: sItem = "nw_it_mbracer011"; break; + case 9: sItem = "nw_it_mmidmisc04"; break; + case 10: sItem = "nw_it_mring003"; break; + case 11: sItem = "nw_it_mbelt006"; break; + case 12: sItem = "nw_it_mbelt002"; break; + case 13: sItem = "nw_it_mmidmisc03"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl101"; break; + case 2: sItem = "nw_maarcl101"; break; + case 3: sItem = "nw_maarcl102"; break; + case 4: sItem = "nw_maarcl103"; break; + case 5: sItem = "nw_it_mglove001"; break; + case 6: sItem = "nw_maarcl100"; break; + case 7: sItem = "nw_it_mbracer011"; break; + case 8: sItem = "nw_it_mmidmisc04"; break; + case 9: sItem = "nw_it_mring003"; break; + case 10: sItem = "nw_it_mbelt006"; break; + case 11: sItem = "nw_it_mbelt002"; break; + case 12: sItem = "nw_it_mmidmisc03"; break; + case 13: sItem = "nw_it_mring002"; break; + case 14: sItem = "nw_it_mbelt004"; break; + case 15: sItem = "nw_it_mring005"; break; + case 16: sItem = "nw_it_mboots005"; break; + case 17: sItem = "nw_it_mring007"; break; + case 18: sItem = "nw_it_mneck003"; break; + case 19: sItem = "nw_it_mbelt007"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(15) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mbelt002"; break; + case 2: sItem = "nw_it_mbelt002"; break; + case 3: sItem = "nw_it_mmidmisc03"; break; + case 4: sItem = "nw_it_mring002"; break; + case 5: sItem = "nw_it_mbelt004"; break; + case 6: sItem = "nw_it_mring005"; break; + case 7: sItem = "nw_it_mboots005"; break; + case 8: sItem = "nw_it_mring007"; break; + case 9: sItem = "nw_it_mneck003"; break; + case 10: sItem = "nw_it_mbelt007"; break; + case 11: sItem = "nw_it_mboots004"; break; + case 12: sItem = "nw_it_mboots003"; break; + case 13: sItem = "nw_it_mneck005"; break; + case 14: sItem = "nw_it_mbelt008"; break; + case 15: sItem = "nw_it_mring020"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(19) + 1; + switch (nRandom) + { + case 1: sItem = "nw_it_mboots004"; break; + case 2: sItem = "nw_it_mboots004"; break; + case 3: sItem = "nw_it_mboots003"; break; + case 4: sItem = "nw_it_mneck005"; break; + case 5: sItem = "nw_it_mbelt008"; break; + case 6: sItem = "nw_it_mring020"; break; + case 7: sItem = "nw_it_mbelt001"; break; + case 8: sItem = "nw_it_mring017"; break; + case 9: sItem = "nw_mcloth001"; break; + case 10: sItem = "nw_it_mneck019"; break; + case 11: sItem = "nw_it_mneck002"; break; + case 12: sItem = "nw_it_mneck004"; break; + case 13: sItem = "nw_it_mmidmisc01"; break; + case 14: sItem = "nw_mcloth002"; break; + case 15: sItem = "nw_mcloth003"; break; + case 16: sItem = "nw_mcloth004"; break; + case 17: sItem = "nw_it_mbelt003"; break; + // * new items + case 18: sItem = "NW_IT_MBELT020"; break; + case 19: sItem = "NW_IT_MBELT021"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificRodStaffWand(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericRodStaffWand(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericRodStaffWand(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(4) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgst004"; break; + case 2: sItem = "nw_wmgst006"; break; + case 3: sItem = "nw_wmgmrd003"; break; + case 4: sItem = "nw_wmgst004"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(7) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgmrd003"; break; + case 2: sItem = "nw_wmgst006"; break; + case 3: sItem = "nw_wmgmrd003"; break; + case 4: sItem = "nw_wmgst004"; break; + case 5: sItem = "nw_wmgst005"; break; + case 6: sItem = "nw_wmgmrd004"; break; + case 7: sItem = "nw_wmgrd002"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(8) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmcl012"; break; + case 2: sItem = "nw_wmgmrd003"; break; + case 3: sItem = "nw_wmgst004"; break; + case 4: sItem = "nw_wmgst005"; break; + case 5: sItem = "nw_wblmcl012"; break; + case 6: sItem = "nw_wmgmrd004"; break; + case 7: sItem = "nw_wmgst002"; break; + case 8: sItem = "nw_wmgmrd005"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wmgmrd004"; break; + case 2: sItem = "nw_wmgst002"; break; + case 3: sItem = "nw_wmgmrd005"; break; + case 4: sItem = "nw_wmgmrd002"; break; + case 5: sItem = "nw_wmgst003"; break; + case 6: sItem = "nw_wblmcl012"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + + + void CreateSpecificSimple(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericSimple(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericSimple(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs005"; break; + case 2: sItem = "nw_wdbmqs005"; break; + case 3: sItem = "nw_wdbmqs006"; break; + case 4: sItem = "nw_wbwmxh005"; break; + case 5: sItem = "nw_wbwmxl005"; break; + case 6: sItem = "nw_wswmdg006"; break; + case 7: sItem = "nw_wblmml006"; break; + case 8: sItem = "nw_wspmsc004"; break; + case 9: sItem = "nw_wblmms007"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(22) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmqs006"; break; + case 2: sItem = "nw_wdbmqs005"; break; + case 3: sItem = "nw_wdbmqs006"; break; + case 4: sItem = "nw_wbwmxh005"; break; + case 5: sItem = "nw_wbwmxl005"; break; + case 6: sItem = "nw_wswmdg006"; break; + case 7: sItem = "nw_wblmml006"; break; + case 8: sItem = "nw_wspmsc004"; break; + case 9: sItem = "nw_wblmms007"; break; + case 10: sItem = "nw_wblmms003"; break; + case 11: sItem = "nw_wblmcl004"; break; + case 12: sItem = "nw_wspmsc006"; break; + case 13: sItem = "nw_wspmsc006"; break; + case 14: sItem = "nw_wdbmqs004"; break; + case 15: sItem = "nw_wblmcl003"; break; + case 16: sItem = "nw_wbwmsl003"; break; + case 17: sItem = "nw_wbwmxh003"; break; + case 18: sItem = "nw_wspmsc003"; break; + case 19: sItem = "nw_wplmss005"; break; + case 20: sItem = "nw_wplmss005"; break; + case 21: sItem = "nw_wbwmxl003"; break; + case 22: sItem = "nw_wblmml004"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(27) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmms003"; break; + case 2: sItem = "nw_wblmms003"; break; + case 3: sItem = "nw_wblmcl004"; break; + case 4: sItem = "nw_wspmsc006"; break; + case 5: sItem = "nw_wspmsc006"; break; + case 6: sItem = "nw_wdbmqs004"; break; + case 7: sItem = "nw_wblmcl003"; break; + case 8: sItem = "nw_wbwmsl003"; break; + case 9: sItem = "nw_wbwmxh003"; break; + case 10: sItem = "nw_wspmsc003"; break; + case 11: sItem = "nw_wplmss005"; break; + case 12: sItem = "nw_wplmss005"; break; + case 13: sItem = "nw_wbwmxl003"; break; + case 14: sItem = "nw_wblmml004"; break; + case 15: sItem = "nw_wdbmqs003"; break; + case 16: sItem = "nw_wbwmxl004"; break; + case 17: sItem = "nw_wbwmxl007"; break; + case 18: sItem = "nw_wblmml005"; break; + case 19: sItem = "nw_wblmcl005"; break; + case 20: sItem = "nw_wplmss007"; break; + case 21: sItem = "nw_wswmdg004"; break; + case 22: sItem = "nw_wbwmsl007"; break; + case 23: sItem = "nw_wblmml007"; break; + case 24: sItem = "nw_wblmml007"; break; + case 25: sItem = "nw_wbwmxh004"; break; + case 26: sItem = "nw_wplmss006"; break; + case 27: sItem = "nw_wbwmxh007"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(31) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wbwmxl003"; break; + case 2: sItem = "nw_wbwmxl003"; break; + case 3: sItem = "nw_wblmml004"; break; + case 4: sItem = "nw_wdbmqs003"; break; + case 5: sItem = "nw_wbwmxl004"; break; + case 6: sItem = "nw_wbwmxl007"; break; + case 7: sItem = "nw_wblmml005"; break; + case 8: sItem = "nw_wblmcl005"; break; + case 9: sItem = "nw_wplmss007"; break; + case 10: sItem = "nw_wswmdg004"; break; + case 11: sItem = "nw_wbwmsl007"; break; + case 12: sItem = "nw_wblmml007"; break; + case 13: sItem = "nw_wblmml007"; break; + case 14: sItem = "nw_wbwmxh004"; break; + case 15: sItem = "nw_wplmss006"; break; + case 16: sItem = "nw_wbwmxh007"; break; + case 17: sItem = "nw_wblmms006"; break; + case 18: sItem = "nw_wswmdg003"; break; + case 19: sItem = "nw_wswmdg007"; break; + case 20: sItem = "nw_wblmms004"; break; + case 21: sItem = "nw_wbwmsl006"; break; + case 22: sItem = "nw_wbwmsl008"; break; + case 23: sItem = "nw_wblmml008"; break; + case 24: sItem = "nw_wdbmqs007"; break; + case 25: sItem = "nw_wblmcl006"; break; + case 26: sItem = "nw_wbwmsl004"; break; + case 27: sItem = "nw_wbwmxh006"; break; + case 28: sItem = "nw_wplmss004"; break; + case 29: sItem = "nw_wswmdg005"; break; + case 30: sItem = "nw_wbwmxl006"; break; + case 31: sItem = "nw_wspmsc005"; break; + + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificMartial(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericMartial(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericMartial(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_wthmax005"; break; + case 3: sItem = "nw_wthmax007"; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmax003"; break; + case 2: sItem = "nw_wthmax005"; break; + case 3: sItem = "nw_wthmax007"; break; + case 4: sItem = "nw_wthmax003"; break; + case 5: sItem = "nw_wthmax004"; break; + case 6: sItem = "nw_wthmax006"; break; + case 7: sItem = "nw_wswmrp004"; break; + case 8: sItem = "nw_wswmrp004"; break; + case 9: sItem = "nw_wblmfl004"; break; + case 10: sItem = "nw_wblmhl004"; break; + case 11: sItem = "nw_wbwmsh003"; break; + case 12: sItem = "nw_wblmhw006"; break; + case 13: sItem = "nw_wblmhw006"; break; + case 14: sItem = "nw_wbwmln004"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(28) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmfl005"; break; + case 2: sItem = "nw_wthmax007"; break; + case 3: sItem = "nw_wthmax003"; break; + case 4: sItem = "nw_wthmax004"; break; + case 5: sItem = "nw_wthmax006"; break; + case 6: sItem = "nw_wswmrp004"; break; + case 7: sItem = "nw_wswmrp004"; break; + case 8: sItem = "nw_wblmfl004"; break; + case 9: sItem = "nw_wblmhl004"; break; + case 10: sItem = "nw_wbwmsh003"; break; + case 11: sItem = "nw_wblmhw006"; break; + case 12: sItem = "nw_wblmhw006"; break; + case 13: sItem = "nw_wbwmln004"; break; + case 14: sItem = "nw_wblmfl005"; break; + case 15: sItem = "nw_wswmgs006"; break; + case 16: sItem = "nw_waxmgr003"; break; + case 17: sItem = "nw_wplmhb004"; break; + case 18: sItem = "nw_wblmhw005"; break; + case 19: sItem = "nw_wblmfh004"; break; + case 20: sItem = "nw_wblmfh008"; break; + case 21: sItem = "nw_wbwmsh006"; break; + case 22: sItem = "nw_wswmsc004"; break; + case 23: sItem = "nw_waxmgr006"; break; + case 24: sItem = "nw_wswmrp005"; break; + case 25: sItem = "nw_wswmls007"; break; + case 26: sItem = "nw_wswmgs004"; break; + case 27: sItem = "nw_waxmhn004"; break; + case 28: sItem = "nw_wswmbs005"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(42) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmhw006"; break; + case 2: sItem = "nw_wblmhw006"; break; + case 3: sItem = "nw_wblmhw006"; break; + case 4: sItem = "nw_wbwmln004"; break; + case 5: sItem = "nw_wblmfl005"; break; + case 6: sItem = "nw_wswmgs006"; break; + case 7: sItem = "nw_waxmgr003"; break; + case 8: sItem = "nw_wplmhb004"; break; + case 9: sItem = "nw_wblmhw005"; break; + case 10: sItem = "nw_wblmfh004"; break; + case 11: sItem = "nw_wblmfh008"; break; + case 12: sItem = "nw_wbwmsh006"; break; + case 13: sItem = "nw_wswmsc004"; break; + case 14: sItem = "nw_waxmgr006"; break; + case 15: sItem = "nw_wswmrp005"; break; + case 16: sItem = "nw_wswmls007"; break; + case 17: sItem = "nw_wswmgs004"; break; + case 18: sItem = "nw_waxmhn004"; break; + case 19: sItem = "nw_wswmbs005"; break; + case 20: sItem = "nw_wblmhl005"; break; + case 21: sItem = "nw_wblmhl011"; break; + case 22: sItem = "nw_wswmss005"; break; + case 23: sItem = "nw_wplmhb003"; break; + case 24: sItem = "nw_wbwmln007"; break; + case 25: sItem = "nw_wbwmln007"; break; + case 26: sItem = "nw_wbwmsh007"; break; + case 27: sItem = "nw_waxmbt006"; break; + case 28: sItem = "nw_wswmbs006"; break; + case 29: sItem = "nw_wblmfl007"; break; + case 30: sItem = "nw_waxmhn003"; break; + case 31: sItem = "nw_wblmhl006"; break; + case 32: sItem = "nw_wblmfl006"; break; + case 33: sItem = "nw_wswmls005"; break; + case 34: sItem = "nw_wswmss004"; break; + case 35: sItem = "nw_wbwmln006"; break; + case 36: sItem = "nw_wblmhw003"; break; + case 37: sItem = "nw_wblmfh006"; break; + case 38: sItem = "nw_wswmsc006"; break; + case 39: sItem = "nw_waxmhn005"; break; + case 40: sItem = "nw_wblmfh003"; break; + case 41: sItem = "nw_wswmls006"; break; + case 42: sItem = "nw_wswmrp007"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(55) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wblmhl005"; break; + case 2: sItem = "nw_wblmhl005"; break; + case 3: sItem = "nw_wblmhl011"; break; + case 4: sItem = "nw_wswmss005"; break; + case 5: sItem = "nw_wplmhb003"; break; + case 6: sItem = "nw_wbwmln007"; break; + case 7: sItem = "nw_wbwmln007"; break; + case 8: sItem = "nw_wbwmsh007"; break; + case 9: sItem = "nw_waxmbt006"; break; + case 10: sItem = "nw_wswmbs006"; break; + case 11: sItem = "nw_wblmfl007"; break; + case 12: sItem = "nw_waxmhn003"; break; + case 13: sItem = "nw_wblmhl006"; break; + case 14: sItem = "nw_wblmfl006"; break; + case 15: sItem = "nw_wswmls005"; break; + case 16: sItem = "nw_wswmss004"; break; + case 17: sItem = "nw_wbwmln006"; break; + case 18: sItem = "nw_wblmhw003"; break; + case 19: sItem = "nw_wblmfh006"; break; + case 20: sItem = "nw_wswmsc006"; break; + case 21: sItem = "nw_waxmhn005"; break; + case 22: sItem = "nw_wblmfh003"; break; + case 23: sItem = "nw_wswmls006"; break; + case 24: sItem = "nw_wswmrp007"; break; + case 25: sItem = "nw_wswmgs005"; break; + case 26: sItem = "nw_wswmgs005"; break; + case 27: sItem = "nw_waxmgr005"; break; + case 28: sItem = "nw_wplmhb007"; break; + case 29: sItem = "nw_wswmsc007"; break; + case 30: sItem = "nw_wswmrp006"; break; + case 31: sItem = "nw_wswmss006"; break; + case 32: sItem = "nw_wblmhl009"; break; + case 33: sItem = "nw_wswmbs007"; break; + case 34: sItem = "nw_wbwmln005"; break; + case 35: sItem = "nw_wblmfh005"; break; + case 36: sItem = "nw_wswmgs003"; break; + case 37: sItem = "nw_waxmbt003"; break; + case 38: sItem = "nw_wswmls004"; break; + case 39: sItem = "nw_wbwmsh005"; break; + case 40: sItem = "nw_wbwmsh005"; break; + case 41: sItem = "nw_waxmbt004"; break; + case 42: sItem = "nw_waxmbt004"; break; + case 43: sItem = "nw_wblmhl003"; break; + case 44: sItem = "nw_wblmhl003"; break; + case 45: sItem = "nw_wswmbs003"; break; + case 46: sItem = "nw_waxmbt005"; break; + case 47: sItem = "nw_waxmhn006"; break; + case 48: sItem = "nw_wswmss003"; break; + case 49: sItem = "nw_wswmsc005"; break; + case 50: sItem = "nw_wplmhb006"; break; + case 51: sItem = "nw_wbwmsh004"; break; + case 52: sItem = "nw_wswmbs004"; break; + case 53: sItem = "nw_wbwmln003"; break; + case 54: sItem = "nw_wblmhw004"; break; + case 55: sItem = "nw_waxmgr004"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificExotic(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericExotic(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_wthmsh003"; break; + case 3: sItem = "nw_wthmsh006"; break; + } + + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: CreateGenericExotic(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_wthmsh003"; break; + case 3: sItem = "nw_wthmsh006"; break; + case 4: sItem = "nw_wthmsh004"; break; + case 5: sItem = "nw_wthmsh007"; break; + } + + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(14) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wthmsh006"; break; + case 2: sItem = "nw_wthmsh006"; break; + case 3: sItem = "nw_wthmsh004"; break; + case 4: sItem = "nw_wthmsh007"; break; + case 5: sItem = "nw_wspmku006"; break; + case 6: sItem = "nw_wdbmma003"; break; + case 7: sItem = "nw_wswmka005"; break; + case 8: sItem = "nw_wspmka004"; break; + case 9: sItem = "nw_wspmka007"; break; + case 10: sItem = "nw_wdbmax006"; break; + case 11: sItem = "nw_wdbmsw006"; break; + case 12: sItem = "nw_wspmku005"; break; + case 13: sItem = "nw_wdbmsw007"; break; + case 14: sItem = "nw_wspmka005"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(16) + 1; + switch (nRandom) + { + case 1:sItem = "nw_wthmsh007"; break; + case 2: sItem = "nw_wthmsh007"; break; + case 3: sItem = "nw_wspmku006"; break; + case 4: sItem = "nw_wdbmma003"; break; + case 5: sItem = "nw_wswmka005"; break; + case 6: sItem = "nw_wspmka004"; break; + case 7: sItem = "nw_wspmka007"; break; + case 8: sItem = "nw_wdbmax006"; break; + case 9: sItem = "nw_wdbmsw006"; break; + case 10: sItem = "nw_wspmku005"; break; + case 11: sItem = "nw_wdbmsw007"; break; + case 12: sItem = "nw_wspmka005"; break; + case 13: sItem = "nw_wplmsc003"; break; + case 14: sItem = "nw_wdbmax005"; break; + case 15: sItem = "nw_wspmku004"; break; + case 16: sItem = "nw_wdbmma005"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(17) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wplmsc003"; break; + case 2: sItem = "nw_wspmka005"; break; + case 3: sItem = "nw_wplmsc003"; break; + case 4: sItem = "nw_wdbmax005"; break; + case 5: sItem = "nw_wspmku004"; break; + case 6: sItem = "nw_wdbmma005"; break; + case 7: sItem = "nw_wdbmma005"; break; + case 8: sItem = "nw_wdbmax004"; break; + case 9: sItem = "nw_wdbmma004"; break; + case 10: sItem = "nw_wswmka007"; break; + case 11: sItem = "nw_wdbmsw005"; break; + case 12: sItem = "nw_wspmka006"; break; + case 13: sItem = "nw_wspmka003"; break; + case 14: sItem = "nw_wdbmax007"; break; + case 15: sItem = "nw_wplmsc006"; break; + case 16: sItem = "nw_wspmku007"; break; + case 17: sItem = "nw_wdbmma006"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(21) + 1; + switch (nRandom) + { + case 1: sItem = "nw_wdbmma005"; break; + case 2: sItem = "nw_wdbmma005"; break; + case 3: sItem = "nw_wdbmma005"; break; + case 4: sItem = "nw_wdbmax004"; break; + case 5: sItem = "nw_wdbmma004"; break; + case 6: sItem = "nw_wswmka007"; break; + case 7: sItem = "nw_wdbmsw005"; break; + case 8: sItem = "nw_wspmka006"; break; + case 9: sItem = "nw_wspmka003"; break; + case 10: sItem = "nw_wdbmax007"; break; + case 11: sItem = "nw_wplmsc006"; break; + case 12: sItem = "nw_wspmku007"; break; + case 13: sItem = "nw_wdbmma006"; break; + case 14: sItem = "nw_wspmku003"; break; + case 15: sItem = "nw_wswmka006"; break; + case 16: sItem = "nw_wplmsc005"; break; + case 17: sItem = "nw_wplmsc005"; break; + case 18: sItem = "nw_wswmka004"; break; + case 19: sItem = "nw_wswmka004"; break; + case 20: sItem = "nw_wdbmsw004"; break; + case 21: sItem = "nw_wplmsc004"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificLightArmor(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericLightArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + int nRandom = Random(3) + 1; + switch (nRandom) + { + case 1: CreateGenericLightArmor(oTarget, oAdventurer, JUMP_LEVEL); return; break; + case 2: sItem = "nw_ashmsw011"; break; + case 3: sItem = "nw_ashmsw010"; break; + } + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_ashmsw011"; break; + case 2: sItem = "nw_ashmsw011"; break; + case 3: sItem = "nw_ashmsw010"; break; + case 4: sItem = "nw_maarcl011"; break; + case 5: sItem = "nw_ashmsw006"; break; + case 6: sItem = "nw_maarcl017"; break; + case 7: sItem = "nw_ashmsw005"; break; + case 8: sItem = "nw_maarcl013"; break; + case 9: sItem = "nw_maarcl012"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl011"; break; + case 2: sItem = "nw_maarcl011"; break; + case 3: sItem = "nw_ashmsw006"; break; + case 4: sItem = "nw_maarcl017"; break; + case 5: sItem = "nw_ashmsw005"; break; + case 6: sItem = "nw_maarcl013"; break; + case 7: sItem = "nw_maarcl012"; break; + case 8: sItem = "nw_ashmsw004"; break; + case 9: sItem = "nw_maarcl006"; break; + case 10: sItem = "nw_maarcl032"; break; + case 11: sItem = "nw_maarcl003"; break; + case 12: sItem = "nw_maarcl002"; break; + case 13: sItem = "nw_maarcl007"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl012"; break; + case 2: sItem = "nw_maarcl012"; break; + case 3: sItem = "nw_ashmsw004"; break; + case 4: sItem = "nw_maarcl006"; break; + case 5: sItem = "nw_maarcl032"; break; + case 6: sItem = "nw_maarcl003"; break; + case 7: sItem = "nw_maarcl002"; break; + case 8: sItem = "nw_maarcl005"; break; + case 9: sItem = "nw_ashmsw003"; break; + case 10: sItem = "nw_maarcl001"; break; + case 11: sItem = "nw_maarcl034"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(11) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl005"; break; + case 2: sItem = "nw_maarcl005"; break; + case 3: sItem = "nw_ashmsw003"; break; + case 4: sItem = "nw_maarcl001"; break; + case 5: sItem = "nw_maarcl034"; break; + case 6: sItem = "nw_maarcl008"; break; + case 7: sItem = "nw_ashmsw007"; break; + case 8: sItem = "nw_maarcl033"; break; + case 9: sItem = "nw_mcloth005"; break; + case 10: sItem = "nw_maarcl009"; break; + case 11: sItem = "nw_maarcl004"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificMediumArmor(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericMediumArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericMediumArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(5) + 1; + switch (nRandom) + { + case 1: sItem = "nw_armhe008"; break; + case 2: sItem = "nw_armhe008"; break; + case 3: sItem = "nw_armhe007"; break; + case 4: sItem = "nw_armhe009"; break; + case 5: sItem = "nw_armhe010"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(9) + 1; + switch (nRandom) + { + case 1: sItem = "nw_armhe008"; break; + case 2: sItem = "nw_armhe008"; break; + case 3: sItem = "nw_armhe007"; break; + case 4: sItem = "nw_armhe009"; break; + case 5: sItem = "nw_armhe010"; break; + case 6: sItem = "nw_armhe006"; break; + case 7: sItem = "nw_ashmlw007"; break; + case 8: sItem = "nw_ashmlw005"; break; + case 9: sItem = "nw_maarcl016"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(12) + 1; + switch (nRandom) + { + case 1: sItem = "nw_armhe009"; break; + case 2: sItem = "nw_armhe009"; break; + case 3: sItem = "nw_armhe010"; break; + case 4: sItem = "nw_armhe006"; break; + case 5: sItem = "nw_ashmlw007"; break; + case 6: sItem = "nw_ashmlw005"; break; + case 7: sItem = "nw_maarcl016"; break; + case 8: sItem = "nw_maarcl036"; break; + case 9: sItem = "nw_ashmlw004"; break; + case 10: sItem = "nw_maarcl037"; break; + case 11: sItem = "nw_maarcl040"; break; + case 12: sItem = "nw_ashmlw006"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(12) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl016"; break; + case 2: sItem = "nw_maarcl016"; break; + case 3: sItem = "nw_maarcl036"; break; + case 4: sItem = "nw_ashmlw004"; break; + case 5: sItem = "nw_maarcl037"; break; + case 6: sItem = "nw_maarcl040"; break; + case 7: sItem = "nw_ashmlw006"; break; + case 8: sItem = "nw_ashmlw003"; break; + case 9: sItem = "nw_maarcl014"; break; + case 10: sItem = "nw_maarcl039"; break; + case 11: sItem = "nw_maarcl010"; break; + case 12: sItem = "nw_maarcl015"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + } + void CreateSpecificHeavyArmor(object oTarget, object oAdventurer) + { + string sItem = ""; + int nHD = GetHitDice(oAdventurer); + + if (GetRange(1, nHD)) // * 800 + { + CreateGenericHeavyArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(2, nHD)) // * 200 - 2500 + { + CreateGenericHeavyArmor(oTarget, oAdventurer, JUMP_LEVEL); + return; + } + else if (GetRange(3, nHD)) // * 800 - 10000 + { + int nRandom = Random(6) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl026"; break; + case 2: sItem = "nw_maarcl026"; break; + case 3: sItem = "nw_maarcl021"; break; + case 4: sItem = "nw_ashmto003"; break; + case 5: sItem = "nw_maarcl029"; break; + case 6: sItem = "nw_maarcl020"; break; + } + + } + else if (GetRange(4, nHD)) // * 2500 - 16500 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl021"; break; + case 2: sItem = "nw_maarcl026"; break; + case 3: sItem = "nw_maarcl021"; break; + case 4: sItem = "nw_ashmto003"; break; + case 5: sItem = "nw_maarcl029"; break; + case 6: sItem = "nw_maarcl020"; break; + case 7: sItem = "nw_ashmto006"; break; + case 8: sItem = "nw_maarcl041"; break; + case 9: sItem = "nw_ashmto005"; break; + case 10: sItem = "nw_ashmto007"; break; + case 11: sItem = "nw_ashmto010"; break; + case 12: sItem = "nw_maarcl022"; break; + case 13: sItem = "nw_maarcl018"; break; + } + + } + else if (GetRange(5, nHD)) // * 8000 - 25000 + { + int nRandom = Random(13) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl020"; break; + case 2: sItem = "nw_maarcl020"; break; + case 3: sItem = "nw_ashmto006"; break; + case 4: sItem = "nw_maarcl041"; break; + case 5: sItem = "nw_ashmto005"; break; + case 6: sItem = "nw_ashmto007"; break; + case 7: sItem = "nw_ashmto010"; break; + case 8: sItem = "nw_maarcl022"; break; + case 9: sItem = "nw_maarcl018"; break; + case 10: sItem = "nw_maarcl024"; break; + case 11: sItem = "nw_ashmto011"; break; + case 12: sItem = "nw_maarcl042"; break; + case 13: sItem = "nw_maarcl054"; break; + } + + } + else if (GetRange(6, nHD)) // * 16000 and up + { + int nRandom = Random(10) + 1; + switch (nRandom) + { + case 1: sItem = "nw_maarcl018"; break; + case 2: sItem = "nw_maarcl018"; break; + case 3: sItem = "nw_maarcl024"; break; + case 4: sItem = "nw_ashmto011"; break; + case 5: sItem = "nw_maarcl042"; break; + case 6: sItem = "nw_maarcl054"; break; + case 7: sItem = "nw_ashmto004"; break; + case 8: sItem = "nw_maarcl025"; break; + case 9: sItem = "nw_maarcl028"; break; + case 10: sItem = "nw_maarcl027"; break; + } + + } + dbCreateItemOnObject(sItem, oTarget, 1); + + } + // * if nSpecific is = 1 then spawn in 'named' items at the higher levels + void CreateTable2Item(object oTarget, object oAdventurer, int nSpecific=0) + { + //dbSpeak("In CreateTable2Item"); + string sItem = ""; + int nProbMisc = 0; + int nProbClass = 0; + int nProbRodStaffWand = 0; + int nProbSimple = 0; + int nProbMartial = 0; + int nProbExotic = 0; + int nProbLight = 0; + int nProbMedium = 0; + int nProbHeavy = 0; + + int nSpecialRanger = 0; // 2 Means to treat the ranger as a barbarian. A 1 is to treat it as a fighter + + + // * May 2002: Changed using Preston's multiclass function + // * it randomly chooses one of your classes + int nClass = nDetermineClassToUse(oAdventurer); + + + // * SPECIAL RANGER BEHAVIOR + // * If the ranger has the Heavy Armor proficiency, will treat the ranger + if ( nClass == CLASS_TYPE_RANGER && GetHasFeat(FEAT_ARMOR_PROFICIENCY_HEAVY)) + { + nSpecialRanger = 1; + } + else + if (nClass == CLASS_TYPE_RANGER) + { + nSpecialRanger = 2; + } + + + + //* SETUP probabilities based on Class + if ( nClass == CLASS_TYPE_FIGHTER || nClass == CLASS_TYPE_PALADIN || nSpecialRanger == 1 + || nClass == CLASS_TYPE_ANTI_PALADIN || nClass == CLASS_TYPE_BRAWLER || nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_DUSKBLADE || nClass == CLASS_TYPE_KNIGHT || nClass == CLASS_TYPE_MARSHAL + || nClass == CLASS_TYPE_PSYWAR || nClass == CLASS_TYPE_SOHEI) + { + //dbSpeak("I am fighter or paladin or heavy ranger"); + nProbMisc = 20; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 5; + nProbMartial = 20; + nProbExotic = 10; + nProbLight = 5; + nProbMedium = 15; + nProbHeavy = 20; + } + else + if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER) + { + //dbSpeak("I am wizard or sorcerer"); + nProbMisc = 40; + nProbClass = 30; + nProbRodStaffWand = 15; + nProbSimple = 3; + nProbMartial = 3; + nProbExotic = 3; + nProbLight = 2; + nProbMedium = 2; + nProbHeavy = 2; + } + else + if (nClass == CLASS_TYPE_BARBARIAN || nSpecialRanger == 2 || nClass == CLASS_TYPE_BOWMAN + || nClass == CLASS_TYPE_HEXBLADE || nClass == CLASS_TYPE_WARBLADE) + { + //dbSpeak("I am barbarian or light ranger"); + + nProbMisc = 20; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 17; + nProbMartial = 27; + nProbExotic = 15; + nProbLight = 8; + nProbMedium = 5; + nProbHeavy = 3; + } + else + if (nClass == CLASS_TYPE_ARCHIVIST || nClass == CLASS_TYPE_DRAGON_SHAMAN || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_MYSTIC || nClass == CLASS_TYPE_WARMAGE || nClass == CLASS_TYPE_TEMPLAR) + { + //type 1 + nProbMisc = 25; + nProbClass = 0; + nProbRodStaffWand = 15; + nProbSimple = 15; + nProbMartial = 8; + nProbExotic = 6; + nProbLight = 15; + nProbMedium = 10; + nProbHeavy = 6; + } + else + if (nClass == CLASS_TYPE_NOBLE || nClass == CLASS_TYPE_SWASHBUCKLER || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_ULTIMATE_RANGER) + { + //type 2 + nProbMisc = 27; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 15; + nProbMartial = 20; + nProbExotic = 10; + nProbLight = 10; + nProbMedium = 8; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_BEGUILER || nClass == CLASS_TYPE_DREAD_NECROMANCER || nClass == CLASS_TYPE_HEALER + || nClass == CLASS_TYPE_SCOUT || nClass == CLASS_TYPE_SHAMAN || nClass == CLASS_TYPE_SOULKNIFE + || nClass == CLASS_TYPE_TRUENAMER || nClass == CLASS_TYPE_WARLOCK || nClass == CLASS_TYPE_WILDER) + { + //type 3 + nProbMisc = 45; + nProbClass = 0; + nProbRodStaffWand = 7; + nProbSimple = 15; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 15; + nProbMedium = 4; + nProbHeavy = 4; + } + else + if (nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || nClass == CLASS_TYPE_PSION || nClass == CLASS_TYPE_WITCH) + { + //type 4 + nProbMisc = 50; + nProbClass = 0; + nProbRodStaffWand = 10; + nProbSimple = 20; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 4; + nProbMedium = 3; + nProbHeavy = 3; + } + else + if (nClass == CLASS_TYPE_NINJA) + { + //type 5 + nProbMisc = 45; + nProbClass = 0; + nProbRodStaffWand = 2; + nProbSimple = 12; + nProbMartial = 6; + nProbExotic = 26; + nProbLight = 3; + nProbMedium = 3; + nProbHeavy = 3; + } + else + if (nClass == CLASS_TYPE_CW_SAMURAI || nClass == CLASS_TYPE_SAMURAI) + { + //type 6 + nProbMisc = 25; + nProbClass = 0; + nProbRodStaffWand = 5; + nProbSimple = 5; + nProbMartial = 10; + nProbExotic = 20; + nProbLight = 10; + nProbMedium = 20; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_CLERIC) + { + //dbSpeak("I am cleric"); + + nProbMisc = 20; + nProbClass = 10; + nProbRodStaffWand = 10; + nProbSimple = 25; + nProbMartial = 7; + nProbExotic = 5; + nProbLight = 5; + nProbMedium = 8; + nProbHeavy = 10; + } + else + if (nClass == CLASS_TYPE_DRUID) + { + //dbSpeak("I am druid"); + + nProbMisc = 20; + nProbClass = 25; + nProbRodStaffWand = 15; + nProbSimple = 10; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 10; + nProbMedium = 5; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_MONK) + { + //dbSpeak("I am monk"); + nProbMisc = 20; + nProbClass = 50; + nProbRodStaffWand = 2; + nProbSimple = 7; + nProbMartial = 2; + nProbExotic = 7; + nProbLight = 4; + nProbMedium = 4; + nProbHeavy = 4; + } + else + if (nClass == CLASS_TYPE_ROGUE || nClass == CLASS_TYPE_PSYCHIC_ROGUE) + { + //dbSpeak("I am rogue"); + + nProbMisc = 25; + nProbClass = 10; + nProbRodStaffWand = 10; + nProbSimple = 25; + nProbMartial = 5; + nProbExotic = 5; + nProbLight = 10; + nProbMedium = 5; + nProbHeavy = 5; + } + else + if (nClass == CLASS_TYPE_BARD) + { + //dbSpeak("I am bard"); + + nProbMisc = 25; + nProbClass = 5; + nProbRodStaffWand = 5; + nProbSimple = 25; + nProbMartial = 10; + nProbExotic = 10; + nProbLight = 10; + nProbMedium = 5; + nProbHeavy = 5; + } + //else + //{ + // dbSpeak("No Valid Class"); + //} + //dbSpeak("Table2Item: After Class Distribution"); + //* Create Items based on Probabilities + int nRandom = d100(); + if (nRandom <= nProbMisc) + { + if (nSpecific == 0) CreateGenericMiscItem(oTarget, oAdventurer); + else CreateSpecificMiscItem(oTarget, oAdventurer); + + } + else + if (nRandom <= nProbMisc + nProbClass) + { // * no need for a seperate specific function here + CreateGenericClassItem(oTarget, oAdventurer, nSpecific); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand) + { + if (nSpecific == 0) CreateGenericRodStaffWand(oTarget, oAdventurer); + else CreateSpecificRodStaffWand(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple) + { + if (nSpecific == 0) CreateGenericSimple(oTarget, oAdventurer); + else CreateSpecificSimple(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial) + { + + if (nSpecific == 0) CreateGenericMartial(oTarget, oAdventurer); + else CreateSpecificMartial(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic) + { + if (nSpecific == 0) CreateGenericExotic(oTarget, oAdventurer); + else CreateSpecificExotic(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic + nProbLight) + { + if (nSpecific == 0) CreateGenericLightArmor(oTarget, oAdventurer); + else CreateSpecificLightArmor(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic + nProbLight + nProbMedium) + { + if (nSpecific == 0) CreateGenericMediumArmor(oTarget, oAdventurer); + else CreateSpecificMediumArmor(oTarget, oAdventurer); + } + else + if (nRandom <= nProbMisc + nProbClass + nProbRodStaffWand + nProbSimple + nProbMartial + nProbExotic + nProbLight + nProbMedium + nProbHeavy) + { + if (nSpecific == 0) CreateGenericHeavyArmor(oTarget, oAdventurer); + else CreateSpecificHeavyArmor(oTarget, oAdventurer); + } + //else + //{ + // dbSpeak("Generic Generic or Specific; error: 3524"); + //} + } + +//:://///////////////////////////////////////////// +//:: GenerateTreasure +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Generate Treasure + NOTE: When used by NPCs, the treasure is scaled + to how powerful the NPC is. + + If used by containers, it is scaled by how + powerful the PC is. + + PARAMETERS + oLastOpener = The creature that opened the container + oCreateOn = The place to put the treasure. If this is + invalid then the treasure is placed on oLastOpener + + +*/ +//::////////////////////////////////////////////// +//:: Created By: Andrew +//:: Created On: +//::////////////////////////////////////////////// +void GenerateTreasure(int nTreasureType, object oLastOpener, object oCreateOn) +{ + + //dbSpeak("*********************NEW TREASURE*************************"); + + // * abort treasure if no one opened the container + if (GetIsObjectValid(oLastOpener) == FALSE) + { + //dbSpeak("Aborted. No valid Last Opener"); + return; + } + + // * if no valid create on object, then create on oLastOpener + if (oCreateOn == OBJECT_INVALID) + { + oCreateOn = oLastOpener; + } + + // * if an Animal then generate 100% animal treasure + + // not done yet + // * VARIABLES + int nProbBook = 0; + int nProbAnimal = 0; + int nProbJunk = 0; + int nProbGold = 0; + int nProbGem = 0; + int nProbJewel = 0; + int nProbArcane = 0; + int nProbDivine = 0; + int nProbAmmo = 0; + int nProbKit = 0; + int nProbPotion = 0; + int nProbTable2 = 0; + + int nSpecific = 0; + int i = 0; + int nNumberItems = GetNumberOfItems(nTreasureType); + + // * Set Treasure Type Values + if (nTreasureType == TREASURE_LOW) + { + nProbBook = LOW_PROB_BOOK; + nProbAnimal = LOW_PROB_ANIMAL; + nProbJunk = LOW_PROB_JUNK; + nProbGold = LOW_PROB_GOLD; + nProbGem = LOW_PROB_GEM; + nProbJewel = LOW_PROB_JEWEL; + nProbArcane = LOW_PROB_ARCANE; + nProbDivine = LOW_PROB_DIVINE; + nProbAmmo = LOW_PROB_AMMO ; + nProbKit = LOW_PROB_KIT; + nProbPotion = LOW_PROB_POTION; + nProbTable2 = LOW_PROB_TABLE2; + } + else if (nTreasureType == TREASURE_MEDIUM) + { + nProbBook = MEDIUM_PROB_BOOK; + nProbAnimal = MEDIUM_PROB_ANIMAL; + nProbJunk = MEDIUM_PROB_JUNK; + nProbGold = MEDIUM_PROB_GOLD; + nProbGem = MEDIUM_PROB_GEM; + nProbJewel = MEDIUM_PROB_JEWEL; + nProbArcane = MEDIUM_PROB_ARCANE; + nProbDivine = MEDIUM_PROB_DIVINE; + nProbAmmo = MEDIUM_PROB_AMMO ; + nProbKit = MEDIUM_PROB_KIT; + nProbPotion = MEDIUM_PROB_POTION; + nProbTable2 = MEDIUM_PROB_TABLE2; + } + else if (nTreasureType == TREASURE_HIGH) + { + nProbBook = HIGH_PROB_BOOK; + nProbAnimal = HIGH_PROB_ANIMAL; + nProbJunk = HIGH_PROB_JUNK; + nProbGold = HIGH_PROB_GOLD; + nProbGem = HIGH_PROB_GEM; + nProbJewel = HIGH_PROB_JEWEL; + nProbArcane = HIGH_PROB_ARCANE; + nProbDivine = HIGH_PROB_DIVINE; + nProbAmmo = HIGH_PROB_AMMO ; + nProbKit = HIGH_PROB_KIT; + nProbPotion = HIGH_PROB_POTION; + nProbTable2 = HIGH_PROB_TABLE2; + } + else if (nTreasureType == TREASURE_BOSS) + { //dbSpeak("boss"); + nProbTable2 = 100; + nSpecific = 1; + } + else if (nTreasureType == TREASURE_BOOK) + { + nProbBook = 90; + nProbArcane = 6; + nProbDivine = 4; + } + + //dbSpeak("Generate Treasure nSpecific = " + IntToString(nSpecific)); + + for (i = 1; i <= nNumberItems; i++) + { + int nRandom = d100(); + if (nRandom <= nProbBook) + CreateBook(oCreateOn); // * Book + else if (nRandom <= nProbBook + nProbAnimal) + CreateAnimalPart(oCreateOn); // * Animal + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk) + CreateJunk(oCreateOn); // * Junk + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold) + CreateGold(oCreateOn, oLastOpener, nTreasureType); // * Gold + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem) + CreateGem(oCreateOn, oLastOpener, nTreasureType); // * Gem + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel) + CreateJewel(oCreateOn, oLastOpener, nTreasureType); // * Jewel + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane) + CreateArcaneScroll(oCreateOn, oLastOpener); // * Arcane Scroll + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine) + CreateDivineScroll(oCreateOn, oLastOpener); // * Divine Scroll + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo) + CreateAmmo(oCreateOn, oLastOpener); // * Ammo + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo + nProbKit) + CreateKit(oCreateOn, oLastOpener); // * Healing, Trap, or Thief kit + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo + nProbKit + nProbPotion) + CreatePotion(oCreateOn, oLastOpener); // * Potion + else if (nRandom <= nProbBook + nProbAnimal + nProbJunk + nProbGold + nProbGem + nProbJewel + nProbArcane + nProbDivine + nProbAmmo + nProbKit + nProbPotion + nProbTable2) + { + CreateTable2Item(oCreateOn, oLastOpener, nSpecific); // * Weapons, Armor, Misc - Class based + } + //else + // dbSpeak("other stuff"); + + + + } +} +void GenerateLowTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_LOW, oLastOpener, oCreateOn); +} +void GenerateMediumTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_MEDIUM, oLastOpener, oCreateOn); +} +void GenerateHighTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_HIGH, oLastOpener, oCreateOn); +} +void GenerateBossTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_BOSS, oLastOpener, oCreateOn); +} +void GenerateBookTreasure(object oLastOpener, object oCreateOn=OBJECT_INVALID) +{ + GenerateTreasure(TREASURE_BOOK, oLastOpener, oCreateOn); +} +//:://///////////////////////////////////////////// +//:: GenerateNPCTreasure +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Preferrably called from OnSpawn scripts. + Use the random treasure functions to generate + appropriate treasure for the creature to drop. +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: January 2002 +//::////////////////////////////////////////////// + +void GenerateNPCTreasure(int nTreasureValue=1, object oTreasureGetter=OBJECT_SELF, object oKiller=OBJECT_SELF) +{ + //DestroyObject(OBJECT_SELF); + // * if I am an animal ,then give me animal stuff instead + if (GetObjectType(oTreasureGetter) == OBJECT_TYPE_CREATURE) + { + if ( + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_UNDEAD) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_ANIMAL) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_BEAST) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_MAGICAL_BEAST) || + (GetRacialType(oTreasureGetter) == RACIAL_TYPE_VERMIN) + ) + { + //CreateAnimalPart(oTreasureGetter); + // April 23 2002: Removed animal parts. They are silly. + return; + } + } + + if (nTreasureValue == 1) + { + // April 2002: 30% chance of not getting any treasure now + // if a creature + if (Random(100)+1 >= 75) + { + GenerateTreasure(TREASURE_LOW, oTreasureGetter, oKiller); + } + } + else + if (nTreasureValue == 2) + { + GenerateTreasure(TREASURE_MEDIUM, oTreasureGetter, oKiller); + } + else + if (nTreasureValue == 3) + { + GenerateTreasure(TREASURE_HIGH, oTreasureGetter, oKiller); + } + else + if (nTreasureValue == 4) + { + GenerateBossTreasure(oKiller, oTreasureGetter); + } + +} + +// * +// * Theft Prevention +// * + +//:://///////////////////////////////////////////// +//:: ShoutDisturbed +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + +*/ +//::////////////////////////////////////////////// +//:: Created By: +//:: Created On: +//::////////////////////////////////////////////// + +// * Container shouts if disturbed +void ShoutDisturbed() +{ + if (GetIsDead(OBJECT_SELF) == TRUE) + { + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget)) + { + if (GetFactionEqual(oTarget, OBJECT_SELF) == TRUE) + { + // * Make anyone who is a member of my faction hostile if I am violated + object oAttacker = GetLastAttacker(); + SetIsTemporaryEnemy(oAttacker,oTarget); + AssignCommand(oTarget, ActionAttack(oAttacker)); + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + } + } + else if (GetIsOpen(OBJECT_SELF) == TRUE) + { + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget)) + { + if (GetFactionEqual(oTarget, OBJECT_SELF) == TRUE) + { + // * Make anyone who is a member of my faction hostile if I am violated + object oAttacker = GetLastOpener(); + SetIsTemporaryEnemy(oAttacker,oTarget); + AssignCommand(oTarget, ActionAttack(oAttacker)); + + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, GetLocation(OBJECT_SELF), TRUE, OBJECT_TYPE_CREATURE); + } + } +} + +int nGetIsBaseClass(int nClass) +{ + return (nClass <= CLASS_TYPE_WIZARD || + nClass == CLASS_TYPE_ANTI_PALADIN || + nClass == CLASS_TYPE_ARCHIVIST || + nClass == CLASS_TYPE_BEGUILER || + nClass == CLASS_TYPE_BOWMAN || + nClass == CLASS_TYPE_BRAWLER || + nClass == CLASS_TYPE_CRUSADER || + nClass == CLASS_TYPE_DRAGON_SHAMAN || + nClass == CLASS_TYPE_DRAGONFIRE_ADEPT || + nClass == CLASS_TYPE_DREAD_NECROMANCER || + nClass == CLASS_TYPE_DUSKBLADE || + nClass == CLASS_TYPE_FAVOURED_SOUL || + nClass == CLASS_TYPE_HEALER || + nClass == CLASS_TYPE_HEXBLADE || + nClass == CLASS_TYPE_KNIGHT || + nClass == CLASS_TYPE_MARSHAL || + nClass == CLASS_TYPE_MYSTIC || + nClass == CLASS_TYPE_NINJA || + nClass == CLASS_TYPE_NOBLE || + nClass == CLASS_TYPE_PSION || + nClass == CLASS_TYPE_PSYWAR || + nClass == CLASS_TYPE_PSYCHIC_ROGUE || + nClass == CLASS_TYPE_SAMURAI || + nClass == CLASS_TYPE_CW_SAMURAI || + nClass == CLASS_TYPE_SCOUT || + nClass == CLASS_TYPE_SHAMAN || + nClass == CLASS_TYPE_SOHEI || + nClass == CLASS_TYPE_SOULKNIFE || + nClass == CLASS_TYPE_SWASHBUCKLER || + nClass == CLASS_TYPE_SWORDSAGE || + nClass == CLASS_TYPE_TRUENAMER || + nClass == CLASS_TYPE_ULTIMATE_RANGER || + nClass == CLASS_TYPE_WARBLADE || + nClass == CLASS_TYPE_WARLOCK || + nClass == CLASS_TYPE_WARMAGE || + nClass == CLASS_TYPE_WILDER || + nClass == CLASS_TYPE_WITCH || + nClass == CLASS_TYPE_TEMPLAR); +} + +//:://///////////////////////////////////////////// +//:: Determine Class to Use +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Determines which of a NPCs three classes to + use in the random treasure system +*/ +//::////////////////////////////////////////////// +//:: Created By: Preston Watamaniuk +//:: Created On: April 4, 2002 +//::////////////////////////////////////////////// + +int nDetermineClassToUse(object oCharacter) +{ + int nClass; + int nTotal = GetHitDice(oCharacter); + //dbSpeak("Hit dice " + IntToString(nTotal)); + if (nTotal < 1) + { + nTotal = 1; + } +/* + float fTotal = IntToFloat(nTotal); + //if (GetIsObjectValid(oCharacter) == FALSE) + //{ + // dbSpeak("DetermineClassToUse: This character is invalid"); + //} + + int nClass1 = GetClassByPosition(1, oCharacter); + int nState1 = FloatToInt((IntToFloat(GetLevelByClass(nClass1, oCharacter)) / fTotal) * 100); + //dbSpeak("Level 1 Class Level = " + IntToString(GetLevelByClass(nClass1,oCharacter))); + + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + GetTag(oCharacter) + "Class 1 " + IntToString(nState1)); + //dbSpeak("State 1 " + IntToString(nState1)); + int nClass2 = GetClassByPosition(2, oCharacter); + int nState2 = FloatToInt((IntToFloat(GetLevelByClass(nClass2, oCharacter)) / fTotal) * 100) + nState1; + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + GetTag(oCharacter) + "Class 2 " + IntToString(nState2)); + + int nClass3 = GetClassByPosition(3, oCharacter); + int nState3 = FloatToInt((IntToFloat(GetLevelByClass(nClass3, oCharacter)) / fTotal) * 100) + nState2; + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + GetTag(oCharacter) + "Class 3 " + IntToString(nState3)); +*/ + int nClass1 = GetClassByPosition(1, oCharacter); + int nClass2 = GetClassByPosition(2, oCharacter); + int nClass3 = GetClassByPosition(3, oCharacter); + int nClass4 = GetClassByPosition(4, oCharacter); + int nClass5 = GetClassByPosition(5, oCharacter); + int nClass6 = GetClassByPosition(6, oCharacter); + int nClass7 = GetClassByPosition(7, oCharacter); + int nClass8 = GetClassByPosition(8, oCharacter); + + int nState1 = GetLevelByClass(nClass1, oCharacter) * 100 / nTotal; + int nState2 = GetLevelByClass(nClass2, oCharacter) * 100 / nTotal + nState1; + int nState3 = GetLevelByClass(nClass3, oCharacter) * 100 / nTotal + nState2; + int nState4 = GetLevelByClass(nClass4, oCharacter) * 100 / nTotal + nState3; + int nState5 = GetLevelByClass(nClass5, oCharacter) * 100 / nTotal + nState4; + int nState6 = GetLevelByClass(nClass6, oCharacter) * 100 / nTotal + nState5; + int nState7 = GetLevelByClass(nClass7, oCharacter) * 100 / nTotal + nState6; + + // nState8 will always be 100 if there is an eigth class, or 0 if there isn't + //int nState8 = GetLevelByClass(nClass3, oCharacter) * 100 / nTotal + nState7; + + // correct for unrecognized classes - assumes the first class will be a non-prestige player class + if(nClass2 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass2)) + { + nClass2 = CLASS_TYPE_INVALID; + nState1 = nState2; + } + if(nClass3 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass3)) + { + nClass3 = CLASS_TYPE_INVALID; + nState1 = nState3; + } + if(nClass4 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass4)) + { + nClass4 = CLASS_TYPE_INVALID; + nState1 = nState4; + } + if(nClass5 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass5)) + { + nClass5 = CLASS_TYPE_INVALID; + nState1 = nState5; + } + if(nClass6 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass6)) + { + nClass6 = CLASS_TYPE_INVALID; + nState1 = nState6; + } + if(nClass7 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass7)) + { + nClass7 = CLASS_TYPE_INVALID; + nState1 = nState7; + } + if(nClass8 != CLASS_TYPE_INVALID && !nGetIsBaseClass(nClass8)) + { + nClass8 = CLASS_TYPE_INVALID; + if(nClass7 != CLASS_TYPE_INVALID) + nState7 = 100; + else nState1 = 100; + } + + int nUseClass = d100(); + //PrintString("GENERIC SCRIPT DEBUG STRING ********** " + "D100 Roll " +IntToString(nUseClass)); + + + //dbSpeak("Before comparison : " + IntToString(nClass1)); + if(nUseClass <= nState1) + { + nClass = nClass1; + } + else if(nUseClass > nState1 && nUseClass <= nState2) + { + nClass = nClass2; + } + else + { + // might be possible to end up here by accident because of a rounding error + // so just in case... + if(nClass3 == CLASS_TYPE_INVALID) nClass = nClass1; + else nClass = nClass3; + } + + //dbSpeak("Class from determineClass " + IntToString(nClass)); + return nClass; +} + +//:: Test Void +//void main () {} + diff --git a/src/include/pnp_lich_inc.nss b/src/include/pnp_lich_inc.nss new file mode 100644 index 0000000..bd7f8f2 --- /dev/null +++ b/src/include/pnp_lich_inc.nss @@ -0,0 +1,475 @@ +//:://///////////////////////////////////////////// +//:: Name Lich +//:: FileName pnp_lich_inc +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* Functions needed to handle the amulet, soul gem, and hide + +*/ +//::////////////////////////////////////////////// +//:: Created By: Shane Hennessy +//:: Created On: +//::////////////////////////////////////////////// + +// Returns the lich amulet level +int GetAmuletLevel(object oAmulet); +// Sets the passed in amulet to nLevel +void LevelUpAmulet(object oAmulet,int nLevel); +// Returns the lich power level +int GetPowerLevel(object oHide); +// Sets the passed in hide on the PC to nLevel +void LevelUpHide(object oPC, object oHide, int nLevel); +// Creates some VFX on the object when crafting +void CraftVFX(object oObject); + +#include "inc_utility" +#include "pnp_shft_main" + +void LichSkills(object oHide, int iLevel) +{ + SetCompositeBonus(oHide, "LichSkillHide", iLevel, ITEM_PROPERTY_SKILL_BONUS, SKILL_HIDE); + SetCompositeBonus(oHide, "LichSkillListen", iLevel, ITEM_PROPERTY_SKILL_BONUS, SKILL_LISTEN); + SetCompositeBonus(oHide, "LichSkillPersuade", iLevel, ITEM_PROPERTY_SKILL_BONUS, SKILL_PERSUADE); + SetCompositeBonus(oHide, "LichSkillSilent", iLevel, ITEM_PROPERTY_SKILL_BONUS, SKILL_MOVE_SILENTLY); + SetCompositeBonus(oHide, "LichSkillSearch", iLevel, ITEM_PROPERTY_SKILL_BONUS, SKILL_SEARCH); + SetCompositeBonus(oHide, "LichSkillSpot", iLevel, ITEM_PROPERTY_SKILL_BONUS, SKILL_SPOT); +} + +int GetAmuletLevel(object oAmulet) +{ + object oPC = GetFirstPC(); + //SendMessageToPC(oPC,"Amulet level func"); + itemproperty iProp = GetFirstItemProperty(oAmulet); + int nLevel = 0; + + while (GetIsItemPropertyValid(iProp)) + { + if (GetItemPropertyType(iProp) == ITEM_PROPERTY_AC_BONUS) + { + //SendMessageToPC(oPC," AC found"); + int nAC = GetItemPropertyCostTableValue(iProp); + //SendMessageToPC(oPC, "AC = " + IntToString(nAC)); + switch (nAC) + { + case 2: + return 1; + case 3: + return 2; + case 4: + return 3; + case 5: + // cant return because anything above has this AC 5 bonus + nLevel = 4; + break; + default: + return 0; + } + } + // for levels above 4 use a junk item like weight reduction + if (GetItemPropertyType(iProp) == ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION) + { + int nWt = GetItemPropertyCostTableValue(iProp); + //SendMessageToPC(oPC, "wt = " + IntToString(nWt)); + switch(nWt) + { + case IP_CONST_REDUCEDWEIGHT_10_PERCENT: + return 5; + case IP_CONST_REDUCEDWEIGHT_20_PERCENT: + return 6; + case IP_CONST_REDUCEDWEIGHT_40_PERCENT: + return 7; + case IP_CONST_REDUCEDWEIGHT_60_PERCENT: + return 8; + case IP_CONST_REDUCEDWEIGHT_80_PERCENT: + return 9; + default: + return 0; + } + } + // level 10 gets something special (we ran out of weight reduction) + if (GetItemPropertyType(iProp) == ITEM_PROPERTY_CAST_SPELL) + { + int nSpell = GetItemPropertySubType(iProp); + + if (nSpell == IP_CONST_CASTSPELL_CREATE_GREATER_UNDEAD_18) + return 10; + } + + iProp = GetNextItemProperty(oAmulet); + } + return nLevel; +} + +int GetPowerLevel(object oPC) +{ + return GetLocalInt(oPC, "PNP_LichPowerLevel"); +} + +void LevelUpHide(object oPC, object oHide, int nLevel) +{ + itemproperty iprop; + + // Clean the hide of all things that dont stack + // remember to put everything for every level back on! + // - Now the event scripts give us a new hide. + //RemoveAllNonComposite(oHide); + + // Common things for being undead and a lich + if (nLevel >= 4) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichCon", 12, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CON); + IPSafeAddItemProperty(oHide, PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNDEAD_HD), 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + // Undead abilities + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_DEATH_MAGIC); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_DISEASE); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_LEVEL_ABIL_DRAIN); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_MINDSPELLS); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_PARALYSIS); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + iprop = ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_POISON); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // 100 % immune to cold + iprop = ItemPropertyDamageImmunity(IP_CONST_DAMAGETYPE_COLD,IP_CONST_DAMAGEIMMUNITY_100_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // 100 % immune to electric + iprop = ItemPropertyDamageImmunity(IP_CONST_DAMAGETYPE_ELECTRICAL,IP_CONST_DAMAGEIMMUNITY_100_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // Bugfix + iprop = ItemPropertyDamageImmunity(IP_CONST_DAMAGETYPE_NEGATIVE, IP_CONST_DAMAGEIMMUNITY_100_PERCENT); + IPSafeAddItemProperty(oHide, iprop, 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + + // Level 1 hide + if (nLevel == 1) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 1, ITEM_PROPERTY_TURN_RESISTANCE); + + //Lich skills +2 + LichSkills(oHide, 2); + + //Damage reduction 5/- cold + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_COLD,IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 5/- electric + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ELECTRICAL,IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + // Level 2 + else if (nLevel == 2) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 2, ITEM_PROPERTY_TURN_RESISTANCE); + + //Lich skills +4 + LichSkills(oHide, 4); + + //Damage reduction 5/+1 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_1,IP_CONST_DAMAGESOAK_5_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- cold + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_COLD,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- electric + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ELECTRICAL,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 3) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 3, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +6 + LichSkills(oHide, 6); + + + //Damage reduction 10/1 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_1,IP_CONST_DAMAGESOAK_10_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 20/- cold + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_COLD,IP_CONST_DAMAGERESIST_20); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 20/- electric + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ELECTRICAL,IP_CONST_DAMAGERESIST_20); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 4) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 4, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +8 + LichSkills(oHide, 8); + + //Damage reduction 15/1 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_1,IP_CONST_DAMAGESOAK_15_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 5) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 3, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 3, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 3, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 5, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +10 + LichSkills(oHide, 10); + + + //Damage reduction 5/+5 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_5,IP_CONST_DAMAGESOAK_5_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 5/- ACID + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 5/- fire + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE,IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 5/- sonic + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC,IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // Spell level immune to 1 and lower + iprop = ItemPropertyImmunityToSpellLevel(1); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 6) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 4, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 4, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 4, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 8, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +12 + LichSkills(oHide, 12); + + //Damage reduction 10/+8 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_8,IP_CONST_DAMAGESOAK_10_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- ACID + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- fire + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- sonic + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // Spell level immune to 3 and lower + iprop = ItemPropertyImmunityToSpellLevel(3); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 7) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 6, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 6, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 6, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 11, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +14 + LichSkills(oHide, 14); + + //Damage reduction 15/+11 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_11,IP_CONST_DAMAGESOAK_15_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- ACID + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- fire + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 10/- sonic + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC,IP_CONST_DAMAGERESIST_10); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // Spell level immune to 5 and lower + iprop = ItemPropertyImmunityToSpellLevel(5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 8) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 7, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 7, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 7, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 14, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +16 + LichSkills(oHide, 16); + + //Damage reduction 20/+14 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_14,IP_CONST_DAMAGESOAK_20_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 15/- ACID + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGERESIST_15); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 15/- fire + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE,IP_CONST_DAMAGERESIST_15); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 15/- sonic + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC,IP_CONST_DAMAGERESIST_15); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + + // Spell level immune to 7 and lower + iprop = ItemPropertyImmunityToSpellLevel(7); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 9) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 8, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 8, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 8, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 17, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +18 + LichSkills(oHide, 18); + + //Damage reduction 25/+17 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_17,IP_CONST_DAMAGESOAK_25_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 15/- ACID + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGERESIST_15); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 15/- fire + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE,IP_CONST_DAMAGERESIST_15); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 15/- sonic + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC,IP_CONST_DAMAGERESIST_15); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + + // Spell level immune to 8 and lower + iprop = ItemPropertyImmunityToSpellLevel(8); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + else if (nLevel == 10) + { + // Ability bonus + //SetCompositeBonus(oHide, "LichInt", 10, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_INT); + //SetCompositeBonus(oHide, "LichWis", 10, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_WIS); + //SetCompositeBonus(oHide, "LichCha", 10, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CHA); + // Turn resistance + SetCompositeBonus(oHide, "LichTurn", 20, ITEM_PROPERTY_TURN_RESISTANCE); + //Lich skills +20 + LichSkills(oHide, 20); + + //Damage reduction 30/+20 + iprop = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_20,IP_CONST_DAMAGESOAK_30_HP); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 20/- ACID + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGERESIST_20); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 20/- fire + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE,IP_CONST_DAMAGERESIST_20); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + //Damage reduction 20/- sonic + iprop = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC,IP_CONST_DAMAGERESIST_20); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + // Spell level immune to 9 and lower + iprop = ItemPropertyImmunityToSpellLevel(9); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oHide); + } + + SetLocalInt(oPC, "PNP_LichPowerLevel", nLevel); +} + +void LevelUpAmulet(object oAmulet,int nLevel) +{ + RemoveAllItemProperties(oAmulet); + itemproperty iprop; + + // Common level 4 and above things + if (nLevel >= 4) + { + // Ac bonus + iprop = ItemPropertyACBonus(5); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + // Extra so the amulet is useful til 20th level + iprop = ItemPropertyRegeneration(1); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + iprop = ItemPropertyCastSpell(IP_CONST_CASTSPELL_ANIMATE_DEAD_15,IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + iprop = PRCItemPropertyBonusFeat(IP_CONST_FEAT_SPELLFOCUSNEC); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + + // Level 2 + if (nLevel == 2) + { + // Ac bonus + iprop = ItemPropertyACBonus(3); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 3) + { + // Ac bonus + iprop = ItemPropertyACBonus(4); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 4) + { + // nothing + } + else if (nLevel == 5) + { + // reduction is used to permenantly track how much the PC has paid for level ups + // because reduction of 1/2 lb is nothing usefull + iprop = ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_10_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 6) + { + iprop = ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_20_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 7) + { + iprop = ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_40_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 8) + { + iprop = ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_60_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 9) + { + iprop = ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_80_PERCENT); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } + else if (nLevel == 10) + { + iprop = ItemPropertyCastSpell(IP_CONST_CASTSPELL_CREATE_GREATER_UNDEAD_18,IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,iprop,oAmulet); + } +} + +void CraftVFX(object oObject) +{ + effect eFx = EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_EVIL); + ApplyEffectToObject(DURATION_TYPE_INSTANT,eFx,oObject,3.0); + eFx = EffectVisualEffect(VFX_FNF_LOS_EVIL_30); + ApplyEffectToObject(DURATION_TYPE_INSTANT,eFx,oObject, 4.0); +} + + diff --git a/src/include/pnp_shft_main.nss b/src/include/pnp_shft_main.nss new file mode 100644 index 0000000..28eaa1d --- /dev/null +++ b/src/include/pnp_shft_main.nss @@ -0,0 +1,2294 @@ +//:://///////////////////////////////////////////// +//:: Name Shifter PnP functions +//:: FileName +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Functions used by the shifter class to better simulate the PnP rules + +*/ +//::////////////////////////////////////////////// +//:: Created By: Shane Hennessy +//:: Created On: +//::////////////////////////////////////////////// + + + +//see inc_prc_poly.nss for using shifter code for other spells/abilitys + +//unequips then destroys items +void ClearCreatureItem(object oPC, object oTarget); +//shift from quickslot info +void QuickShift(object oPC, int iQuickSlot); +//asign form to your quick slot +void SetQuickSlot(object oPC, int iIndex, int iQuickSlot, int iEpic); +// Determine the level of the Shifter needed to take on +// oTargets shape. +// Returns 1-10 for Shifter level, 11+ for Total levels +int GetShifterLevelRequired(object oTarget); +// Can the shifter (oPC) assume the form of the target +// return Values: TRUE or FALSE +int GetValidShift(object oPC, object oTarget); +// Determine if the oCreature can wear certain equipment +// nInvSlot INVENTORY_SLOT_* +// Return values: TRUE or FALSE +int GetCanFormEquip(object oCreature, int nInvSlot); +// Determine if the oCreature has the ability to cast spells +// Return values: TRUE or FALSE +int GetCanFormCast(object oCreature); +// Determines if the oCreature is harmless enough to have +// special effects applied to the shifter +// Return values: TRUE or FALSE +int GetIsCreatureHarmless(object oCreature); +// Determines the APPEARANCE_TYPE_* for the PC +// based on the players RACIAL type +int GetTrueForm(object oPC); + +/** +* Checks if the target is mounted by checking the bX3_IS_MOUNTED variable (Bioware's default). +* A duplicate of Bioware's HorseGetIsMounted() script with almost no changes. From x3_inc_horse. +* Here because it's called most places ShifterCheck() is called, also added to CanShift() +* Bioware's one not used to avoid circular include hell +* @param oTarget +* @return TRUE if oTarget is mounted +*/ +int PRCHorseGetIsMounted(object oTarget); + + +//is inventory full if yes then CanShift = false else CanShift = true +int CanShift(object oPC); + +// Transforms the oPC into the oTarget using the epic rules +// Assumes oTarget is already a valid target +// Return values: TRUE or FALSE +int SetShiftEpic(object oPC, object oTarget); + +// helper function to SetVisualTrueForm() for DelayCommand() to work on +void SetBodyPartTrueForm(object oPC, int i); +// Transforms the oPC back to thier true form if they are shifted +// Return values: TRUE or FALSE +void SetShiftTrueForm(object oPC); +// Creates a temporary creature for the shifter to shift into +// Validates the shifter is able to become that creature based on level +// Return values: TRUE or FALSE +int SetShiftFromTemplateValidate(object oPC, string sTemplate, int iEpic); + +// Extra item functions +// Copys all the item properties from the target to the destination +void CopyAllItemProperties(object oDestination,object oTarget); +// Removes all the item properties from the item +void RemoveAllItemProperties(object oItem); +// Gets an IP_CONST_FEAT_* from FEAT_* +// returns -1 if the feat is not available +int GetIPFeatFromFeat(int nFeat); +// Determines if the target creature has a certain type of spell +// and sets the powers onto the object item +void SetItemSpellPowers(object oItem, object oTarget); + +// Removes leftover aura effects +void RemoveAuraEffect( object oPC ); +// Adds a creature to the list of valid GWS shift possibilities +void RecognizeCreature( object oPC, string sTemplate, string sCreatureName ); +// Checks to see if the specified creature is a valid GWS shift choice +int IsKnownCreature( object oPC, string sTemplate ); +// Shift based on position in the known array +// oTemplate is either the epic or normal template +void ShiftFromKnownArray(int nIndex,int iEpic, object oPC); +//delete form from spark +void DeleteFromKnownArray(int nIndex, object oPC); +//store the appearance of the pc away for unshifting +void StoreAppearance(object oPC); +// Transforms the oPC into the oTarget +// Assumes oTarget is already a valid target +// this is 2 stage +// these 2 scripts replace the origanel setshift script +// (setshift_02 is almost all of the origenal setshift script) +void SetShift(object oPC, object oTarget); +void SetShift_02(object oPC, object oTarget); + + +// Generic includes +#include "prc_inc_function" + + +void StoreAppearance(object oPC) +{ + if (GetLocalInt(oPC, "shifting") || GetPersistantLocalInt(oPC, "nPCShifted")) + return; + + int iIsStored = GetPersistantLocalInt( oPC, "AppearanceIsStored" ); + + if (iIsStored == 6) + { + //already stored + } + else + { + SetPersistantLocalInt(oPC, "AppearanceIsStored", 6); + SetPersistantLocalInt(oPC, "AppearanceStored", GetAppearanceType(oPC)); + SetPersistantLocalInt(oPC, "AppearanceStoredPortraitID", GetPortraitId(oPC)); + SetPersistantLocalString(oPC, "AppearanceStoredPortraitResRef", GetPortraitResRef(oPC)); + SetPersistantLocalInt(oPC, "AppearanceStoredTail", GetCreatureTailType(oPC)); + SetPersistantLocalInt(oPC, "AppearanceStoredWing", GetCreatureWingType(oPC)); + int i; + for(i=0;i<=20;i++) + { + SetPersistantLocalInt(oPC, "AppearanceStoredPart"+IntToString(i), GetCreatureBodyPart(i, oPC)); + } + } +} + +// PURPOSE: Return whether oTarget is mounted +int PRCHorseGetIsMounted(object oTarget) +{ + if(GetObjectType(oTarget) == OBJECT_TYPE_CREATURE) + { + //original function uses GetSkinInt() so we have to get creature skin here + object oSkin = GetIsPC(oTarget) ? GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTarget) : oTarget; + if(GetLocalInt(oSkin, "bX3_IS_MOUNTED")) + return TRUE; + } + return FALSE; +} + +int CanShift(object oPC) +{ + int iOutcome = FALSE; + // stop if mounted + if(PRCHorseGetIsMounted(oPC)) + { + // bio default poly floaty text + // "You cannot shapeshift while mounted." + if (GetIsPC(oPC)) FloatingTextStrRefOnCreature(111982,oPC,FALSE); + return iOutcome; + } + + if (GetLocalInt(oPC, "shifting") || GetPersistantLocalInt(oPC, "nPCShifted")) + { + return iOutcome; + } + + object oItem1 = CreateItemOnObject("pnp_shft_tstpkup", oPC); + object oItem2 = CreateItemOnObject("pnp_shft_tstpkup", oPC); + object oItem3 = CreateItemOnObject("pnp_shft_tstpkup", oPC); + object oItem4 = CreateItemOnObject("pnp_shft_tstpkup", oPC); + if ((GetItemPossessor(oItem1) == oPC) && (GetItemPossessor(oItem2) == oPC) && (GetItemPossessor(oItem3) == oPC) && (GetItemPossessor(oItem4) == oPC)) + { + iOutcome = TRUE; + } + else + { + SendMessageToPC(oPC, "Your inventory is too full to allow you to (un)shift."); + SendMessageToPC(oPC, "Please make room enough for 4 Helm-sized items and then try again."); + } + + DestroyObject(oItem1); + DestroyObject(oItem2); + DestroyObject(oItem3); + DestroyObject(oItem4); + + //there are issues with shifting will polymorphed + effect eEff = GetFirstEffect(oPC); + while (GetIsEffectValid(eEff)) + { + int eType = GetEffectType(eEff); + if (eType == EFFECT_TYPE_POLYMORPH) + { + SendMessageToPC(oPC, "Shifting when polymorphed has been disabled."); + SendMessageToPC(oPC, "Please cancel your polymorph first."); + return FALSE; + } + eEff = GetNextEffect(oPC); + } + return iOutcome; +} + +void QuickShift(object oPC, int iQuickSlot) +{ + int iMaxIndex = GetPersistantLocalInt(oPC, "num_creatures"); + persistant_array_create(oPC, "QuickSlotIndex"); + persistant_array_create(oPC, "QuickSlotEpic"); + int iIndex = persistant_array_get_int(oPC, "QuickSlotIndex", iQuickSlot); + int iEpic = persistant_array_get_int(oPC, "QuickSlotEpic", iQuickSlot); + if(!(iIndex>iMaxIndex)) + ShiftFromKnownArray(iIndex, iEpic, oPC); +} + +void SetQuickSlot(object oPC, int iIndex, int iQuickSlot, int iEpic) +{ + persistant_array_create(oPC, "QuickSlotIndex"); + persistant_array_create(oPC, "QuickSlotEpic"); + persistant_array_set_int(oPC,"QuickSlotIndex",iQuickSlot,iIndex); + persistant_array_set_int(oPC,"QuickSlotEpic",iQuickSlot,iEpic); +} + +// Transforms the oPC into the oTarget +// Assumes oTarget is already a valid target +// starts here and then goes to SetShift_02 + +// stage 1: +// if the shifter if already shifted call unshift to run after this stage ends +// call next stage to start after this stage ends +void SetShift(object oPC, object oTarget) +{ + SetLocalInt(oPC, "shifting", TRUE); + + SetShiftTrueForm(oPC); + DelayCommand(0.10, SetShift_02(oPC, oTarget)); +} +// stage 1 end: +// the shifter is unshifted if need be +// and the next stage is called + +// stage 2: +// this is most of what the old SetShift script did +// the changes are: +// no check for if shifted is needed and has been removed +// the epic ability item is done here (if epicshifter var is 1) +// oTarget is destryed in this script if its from the convo +void SetShift_02(object oPC, object oTarget) +{ + int nMaxBonus = GetPRCSwitch(PRC_PNP_SHIFTER_BONUS); + int nMaxPen = -10; + + //get all the creature items from the target + object oHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oTarget); + object oWeapCR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oTarget); + object oWeapCL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oTarget); + object oWeapCB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oTarget); + + //get all the creature items from the pc + object oHidePC = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + object oWeapCRPC = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeapCLPC = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeapCBPC = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + + //creature item handling + if (!GetIsObjectValid(oHidePC)) //if you dont have a hide + { + oHidePC = CopyObject(oHide, GetLocation(oPC), oPC); //copy the targets hide + if (!GetIsObjectValid(oHidePC)) //if the target dont have a hide + { + oHidePC = CreateItemOnObject("shifterhide", oPC); //use a blank shifter hide + } + // Need to ID the stuff before we can put it on the PC + SetIdentified(oHidePC, TRUE); + } + else // if you do have a hide + { + ScrubPCSkin(oPC, oHidePC); //clean off all old props + CopyAllItemProperties(oHidePC, oHide); //copy all target props to our hide + } + DelayCommand(0.05, AssignCommand(oPC, ActionEquipItem(oHidePC, INVENTORY_SLOT_CARMOUR))); //reequip the hide to get item props to update properly + + // Set a flag on the PC to tell us that they are shifted + // set this early to prevent alot of unequip code from firing and causing an overflow + SetPersistantLocalInt(oPC,"nPCShifted",TRUE); + + + //copy targets right creature weapon + if (GetIsObjectValid(oWeapCRPC)) //if we still have a creature weapon + { + //remove and destroy the weapon we have + DelayCommand(0.90, ClearCreatureItem(oPC, oWeapCRPC)); + } + if (GetIsObjectValid(oWeapCR)) //if the target has a weapon + { + oWeapCRPC = CreateItemOnObject("pnp_shft_cweap", oPC); //create a shifter blank creature weapon + CopyAllItemProperties(oWeapCRPC, oWeapCR); //copy all target props over + SetIdentified(oWeapCRPC, TRUE); //id so we dont get any funny stuff when equiping + DelayCommand(0.2, AssignCommand(oPC, ActionEquipItem(oWeapCRPC, INVENTORY_SLOT_CWEAPON_R))); //reequip the item to get item props to update properly + } + + //copy targets left creature weapon + if (GetIsObjectValid(oWeapCLPC)) //if we still have a creature weapon + { + //remove and destroy the weapon we have + DelayCommand(0.90, ClearCreatureItem(oPC, oWeapCLPC)); + } + if (GetIsObjectValid(oWeapCL)) //if the target has a weapon + { + oWeapCLPC = CreateItemOnObject("pnp_shft_cweap", oPC); //create a shifter blank creature weapon + CopyAllItemProperties(oWeapCLPC, oWeapCL); //copy all target props over + SetIdentified(oWeapCLPC, TRUE); //id so we dont get any funny stuff when equiping + DelayCommand(0.2, AssignCommand(oPC, ActionEquipItem(oWeapCLPC, INVENTORY_SLOT_CWEAPON_L))); //reequip the item to get item props to update properly + } + //copy targets special creature weapons + if (GetIsObjectValid(oWeapCBPC)) //if we still have a creature weapon + { + //remove and destroy the weapon we have + DelayCommand(0.90, ClearCreatureItem(oPC, oWeapCBPC)); + } + if (GetIsObjectValid(oWeapCB)) //if the target has a weapon + { + oWeapCBPC = CreateItemOnObject("pnp_shft_cweap", oPC); //create a shifter blank creature weapon + CopyAllItemProperties(oWeapCBPC, oWeapCB); //copy all target props over + SetIdentified(oWeapCBPC, TRUE); //id so we dont get any funny stuff when equiping + DelayCommand(0.2, AssignCommand(oPC, ActionEquipItem(oWeapCBPC, INVENTORY_SLOT_CWEAPON_B))); //reequip the item to get item props to update properly + } + + // Get the Targets str, dex, and con + int nTStr = GetAbilityScore(oTarget, ABILITY_STRENGTH); + int nTDex = GetAbilityScore(oTarget, ABILITY_DEXTERITY); + int nTCon = GetAbilityScore(oTarget, ABILITY_CONSTITUTION); + + // Get the PCs str, dex, and con from the clone + int nPCStr = GetAbilityScore(oPC, ABILITY_STRENGTH, TRUE); + int nPCDex = GetAbilityScore(oPC, ABILITY_DEXTERITY, TRUE); + int nPCCon = GetAbilityScore(oPC, ABILITY_CONSTITUTION, TRUE); + + // Get the deltas + int nStrDelta = nTStr - nPCStr; + int nDexDelta = nTDex - nPCDex; + int nConDelta = nTCon - nPCCon; + + int iRemainingSTR; + int iRemainingCON; + int iRemainingDEX; + + // Cap max to +nMaxBonus til they can fix it and nMaxPen for the low value + // get remaining bonus/penelty for later + if (nStrDelta > nMaxBonus) + { + iRemainingSTR = nStrDelta - nMaxBonus; + nStrDelta = nMaxBonus; + } + if (nStrDelta < nMaxPen) + { + iRemainingSTR = nStrDelta + 10; + nStrDelta = nMaxPen; + } + if (nDexDelta > nMaxBonus) + { + iRemainingDEX = nDexDelta - nMaxBonus; + nDexDelta = nMaxBonus; + } + if (nDexDelta < nMaxPen) + { + iRemainingDEX = nDexDelta + 10; + nDexDelta = nMaxPen; + } + if (nConDelta > nMaxBonus) + { + iRemainingCON = nConDelta - nMaxBonus; + nConDelta = nMaxBonus; + } + if (nConDelta < nMaxPen) + { + iRemainingCON = nConDelta + 10; + nConDelta = nMaxPen; + } + + // Big problem with < 0 to abilities, if they have immunity to ability drain + // the "-" to the ability wont do anything + + // Apply these boni to the creature hide + if (nStrDelta > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(IP_CONST_ABILITY_STR, nStrDelta), oHidePC); + else + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(IP_CONST_ABILITY_STR, nStrDelta*-1), oHidePC); + if (nDexDelta > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(IP_CONST_ABILITY_DEX, nDexDelta), oHidePC); + else + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(IP_CONST_ABILITY_DEX, nDexDelta*-1), oHidePC); + if (nConDelta > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(IP_CONST_ABILITY_CON, nConDelta), oHidePC); + else + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(IP_CONST_ABILITY_CON, nConDelta*-1), oHidePC); + + //add extra str bonuses to pc as attack bonues and damage bonus + int iExtSTRBon; + effect eAttackIncrease; + effect eDamageIncrease; + if (iRemainingSTR != 0) + { + int iDamageType = DAMAGE_TYPE_BLUDGEONING; + iExtSTRBon = FloatToInt(iRemainingSTR/2.0); + + if (GetIsObjectValid(oWeapCR)) + { + int iCR = GetBaseItemType(oWeapCR); + if ((iCR == BASE_ITEM_CSLASHWEAPON) || (iCR == BASE_ITEM_CSLSHPRCWEAP)) + iDamageType = DAMAGE_TYPE_SLASHING; + else if (iCR == BASE_ITEM_CPIERCWEAPON) + iDamageType = DAMAGE_TYPE_PIERCING; + } + else if (GetIsObjectValid(oWeapCL)) + { + int iCL = GetBaseItemType(oWeapCL); + if ((iCL == BASE_ITEM_CSLASHWEAPON) || (iCL == BASE_ITEM_CSLSHPRCWEAP)) + iDamageType = DAMAGE_TYPE_SLASHING; + else if (iCL == BASE_ITEM_CPIERCWEAPON) + iDamageType = DAMAGE_TYPE_PIERCING; + } + else if (GetIsObjectValid(oWeapCB)) + { + int iCB = GetBaseItemType(oWeapCB); + if ((iCB == BASE_ITEM_CSLASHWEAPON) || (iCB == BASE_ITEM_CSLSHPRCWEAP)) + iDamageType = DAMAGE_TYPE_SLASHING; + else if (iCB == BASE_ITEM_CPIERCWEAPON) + iDamageType = DAMAGE_TYPE_PIERCING; + } + + int iDamageB; + switch (iExtSTRBon) + { + case 0: + iDamageB = 0; + break; + case 1: + case -1: + iDamageB = DAMAGE_BONUS_1; + break; + case 2: + case -2: + iDamageB = DAMAGE_BONUS_2; + break; + case 3: + case -3: + iDamageB = DAMAGE_BONUS_3; + break; + case 4: + case -4: + iDamageB = DAMAGE_BONUS_4; + break; + case 5: + case -5: + iDamageB = DAMAGE_BONUS_5; + break; + case 6: + case -6: + iDamageB = DAMAGE_BONUS_6; + break; + case 7: + case -7: + iDamageB = DAMAGE_BONUS_7; + break; + case 8: + case -8: + iDamageB = DAMAGE_BONUS_8; + break; + case 9: + case -9: + iDamageB = DAMAGE_BONUS_9; + break; + case 10: + case -10: + iDamageB = DAMAGE_BONUS_10; + break; + case 11: + case -11: + iDamageB = DAMAGE_BONUS_11; + break; + case 12: + case -12: + iDamageB = DAMAGE_BONUS_12; + break; + case 13: + case -13: + iDamageB = DAMAGE_BONUS_13; + break; + case 14: + case -14: + iDamageB = DAMAGE_BONUS_14; + break; + case 15: + case -15: + iDamageB = DAMAGE_BONUS_15; + break; + case 16: + case -16: + iDamageB = DAMAGE_BONUS_16; + break; + case 17: + case -17: + iDamageB = DAMAGE_BONUS_17; + break; + case 18: + case -18: + iDamageB = DAMAGE_BONUS_18; + break; + case 19: + case -19: + iDamageB = DAMAGE_BONUS_19; + break; + default: + iDamageB = DAMAGE_BONUS_20; + break; + } + + if (iRemainingSTR > 0) + { + eAttackIncrease = EffectAttackIncrease(iDamageB, ATTACK_BONUS_MISC); + eDamageIncrease = EffectDamageIncrease(iDamageB, iDamageType); + } + else if (iRemainingSTR < 0) + { + eAttackIncrease = EffectAttackDecrease(iDamageB, ATTACK_BONUS_MISC); + eDamageIncrease = EffectDamageDecrease(iDamageB, iDamageType); + } + + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eAttackIncrease),oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eDamageIncrease),oPC); + } + + //add extra con bonus as temp HP + if (iRemainingCON > 0) + { + int iExtCONBon = FloatToInt(iRemainingCON/2.0); + effect eTemporaryHitpoints = EffectTemporaryHitpoints(iExtCONBon * GetHitDice(oPC)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eTemporaryHitpoints),oPC); + } + + // Apply the natural AC bonus to the hide + // First get the AC from the target + int nTAC = GetAC(oTarget); + nTAC -= GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + // All creatures have 10 base AC + nTAC -= 10; + int i; + for (i=0; i < NUM_INVENTORY_SLOTS; i++) + { + nTAC -= GetItemACValue(GetItemInSlot(i,oTarget)); + } + + if (nTAC > 0) + { + effect eAC = EffectACIncrease(nTAC,AC_NATURAL_BONUS); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eAC),oPC); + } + + //add extra dex bonus as dodge ac + if (iRemainingDEX != 0) + { + int iExtDEXBon = FloatToInt(iRemainingDEX/2.0); + effect eACIncrease; + if (iRemainingDEX > 0) + { + object oPCArmour = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC); + if (GetIsObjectValid(oPCArmour)) + { + int iACArmour = GetItemACValue(oPCArmour); + int iMaxDexBon; + int iCurentDexBon; + iCurentDexBon = FloatToInt(((nPCDex + nStrDelta)-10.0)/2.0); + switch (iACArmour) + { + case 8: + case 7: + case 6: + iMaxDexBon = 1; + break; + case 5: + iMaxDexBon = 2; + break; + case 4: + case 3: + iMaxDexBon = 4; + break; + case 2: + iMaxDexBon = 6; + break; + case 1: + iMaxDexBon = 8; + break; + default: + iMaxDexBon = 100; + break; + } + if (iCurentDexBon > iMaxDexBon) + { + iExtDEXBon = 0; + } + else + { + if ((iExtDEXBon+iCurentDexBon) > iMaxDexBon) + { + iExtDEXBon = iMaxDexBon - iCurentDexBon; + } + } + } + eACIncrease = EffectACIncrease(iExtDEXBon); + } + else if (iRemainingDEX < 0) + { + eACIncrease = EffectACDecrease(iExtDEXBon * -1); + } + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eACIncrease),oPC); + } + + // Apply any feats the target has to the hide as a bonus feat + for (i = 0; i< 500; i++) + { + if (GetHasFeat(i,oTarget)) + { + int nIP = GetIPFeatFromFeat(i); + if(nIP != -1) + { + itemproperty iProp = PRCItemPropertyBonusFeat(nIP); + AddItemProperty(DURATION_TYPE_PERMANENT,iProp,oHidePC); + } + + } + } + // Fix the biobugged Improved Critical (creature) by giving the PC Improved Critical (unarmed) which seems + // to work with creature weapons + if (!GetHasFeat(FEAT_IMPROVED_CRITICAL_UNARMED_STRIKE, oPC) && GetHasFeat(FEAT_IMPROVED_CRITICAL_CREATURE, oPC)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_IMPCRITUNARM),oHidePC); + + + // If they dont have the natural spell feat they can only cast spells in certain shapes + if (!GetHasFeat(FEAT_PRESTIGE_SHIFTER_NATURALSPELL,oPC)) + { + if (!GetCanFormCast(oTarget)) + { + // remove the ability from the PC to cast + effect eNoCast = EffectSpellFailure(); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eNoCast),oPC); + } + } + + // If the creature is "harmless" give it a perm invis for stealth + if(GetIsCreatureHarmless(oTarget)) + { + effect eInvis = EffectInvisibility(INVISIBILITY_TYPE_NORMAL); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(eInvis),oPC); + } + + + // Change the Appearance of the PC + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_POLYMORPH), oPC); + //get the appearance of oTarget + int iAppearance = GetLocalInt(oTarget,"Appearance"); + if (iAppearance>0) + SetCreatureAppearanceType(oPC,iAppearance); + else + SetCreatureAppearanceType(oPC,GetAppearanceType(oTarget)); + //do 1.67 stuff + //wing/tails + SetCreatureWingType(GetCreatureWingType(oTarget), oPC); + SetCreatureTailType(GetCreatureTailType(oTarget), oPC); + //portrait + SetPortraitResRef(oPC, GetPortraitResRef(oTarget)); + SetPortraitId(oPC, GetPortraitId(oTarget)); + //bodyparts + for(i=0;i<=20;i++) + { + DelayCommand(1.0, SetCreatureBodyPart(i, GetCreatureBodyPart(i, oTarget), oPC)); + } + + // For spells to make sure they now treat you like the new race + SetLocalInt(oPC,"RACIAL_TYPE",MyPRCGetRacialType(oTarget)+1); + + // PnP rules say the shifter would heal as if they rested + effect eHeal = EffectHeal(GetHitDice(oPC)*d4()); + ApplyEffectToObject(DURATION_TYPE_INSTANT,eHeal,oPC); + + //epic shift + if (GetLocalInt(oPC,"EpicShift")) + { + // Create some sort of usable item to represent monster spells + object oEpicPowersItem; // = GetItemPossessedBy(oPC,"EpicShifterPowers"); + //if (!GetIsObjectValid(oEpicPowersItem)) + oEpicPowersItem = CreateItemOnObject("epicshifterpower",oPC); + SetItemSpellPowers(oEpicPowersItem,oTarget); + } + + //clear epic shift var + SetLocalInt(oPC,"EpicShift",0); + + //remove oTarget if it is from the template + int iDeleteMe = GetLocalInt(oTarget,"pnp_shifter_deleteme"); + if (iDeleteMe==1) + { + // Remove the temporary creature + AssignCommand(oTarget,SetIsDestroyable(TRUE,FALSE,FALSE)); + SetPlotFlag(oTarget,FALSE); + SetImmortal(oTarget,FALSE); + DestroyObject(oTarget); + } + + // Reset any PRC feats that might have been lost from the shift + DelayCommand(1.0, EvalPRCFeats(oPC)); + + DelayCommand(3.0, DeleteLocalInt(oPC, "shifting")); + SendMessageToPC(oPC, "Finished shifting"); +} + +// stage 2 end: +// the target is distroyed(target only if not mimic target) +// all effects and item propertys are applyed to you and your hide/cweapons + + +void RecognizeCreature( object oPC, string sTemplate, string sCreatureName ) +{ + + // Only add new ones + if (IsKnownCreature(oPC,sTemplate)) + return; + + int num_creatures = GetPersistantLocalInt( oPC, "num_creatures" ); + persistant_array_create(oPC, "shift_choice"); + persistant_array_create(oPC, "shift_choice_name"); + persistant_array_set_string( oPC, "shift_choice", num_creatures, sTemplate ); + persistant_array_set_string( oPC, "shift_choice_name", num_creatures, sCreatureName );//added the line to store the name as well as the resref + SetPersistantLocalInt( oPC, "num_creatures", num_creatures+1 ); + +} + +int IsKnownCreature( object oPC, string sTemplate ) +{ +// object oMimicForms = GetItemPossessedBy( oPC, "sparkoflife" ); + int num_creatures = GetPersistantLocalInt( oPC, "num_creatures" ); + int i; + string cmp; + + for ( i=0; i nPCHD) + { + SendMessageToPC(oPC,"You need " + IntToString(nTHD-nPCHD) + " more character levels before you can take on that form." ); + iInvalid = 1; + } + + //if checks failed return false + if (iInvalid == 1) + { + //this way both of the texts come up if they are needed + //so you dont just get 1 if your need both + return FALSE; + } + //else if all checks past return true + return TRUE; + +} + +// Determine if the oCreature has the ability to cast spells +// Return values: TRUE or FALSE +int GetCanFormCast(object oCreature) +{ + int nTRacialType = MyPRCGetRacialType(oCreature); + + // Need to have hands, and the ability to speak + + switch (nTRacialType) + { + case RACIAL_TYPE_ABERRATION: + case RACIAL_TYPE_MAGICAL_BEAST: + case RACIAL_TYPE_VERMIN: + case RACIAL_TYPE_BEAST: + case RACIAL_TYPE_ANIMAL: + case RACIAL_TYPE_OOZE: + //case RACIAL_TYPE_PLANT: + // These forms can't cast spells + return FALSE; + break; + + case RACIAL_TYPE_SHAPECHANGER: + case RACIAL_TYPE_ELEMENTAL: + case RACIAL_TYPE_DRAGON: + case RACIAL_TYPE_OUTSIDER: + case RACIAL_TYPE_UNDEAD: + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_GIANT: + case RACIAL_TYPE_HUMANOID_MONSTROUS: + 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_ORC: + case RACIAL_TYPE_HUMANOID_GOBLINOID: + case RACIAL_TYPE_HUMANOID_REPTILIAN: + case RACIAL_TYPE_FEY: + break; + } + + return TRUE; +} + +// Determines if the oCreature is harmless enough to have +// special effects applied to the shifter +// Return values: TRUE or FALSE +int GetIsCreatureHarmless(object oCreature) +{ + string sCreatureName = GetName(oCreature); + + // looking for small < 1 CR creatures that nobody looks at twice + + if ((sCreatureName == "Chicken") || + (sCreatureName == "Falcon") || + (sCreatureName == "Hawk") || + (sCreatureName == "Raven") || + (sCreatureName == "Bat") || + (sCreatureName == "Dire Rat") || + (sCreatureName == "Will-O'-Wisp") || + (sCreatureName == "Rat") || + (GetChallengeRating(oCreature) < 1.0 )) + return TRUE; + else + return FALSE; +} + +int GetTrueForm(object oPC) +{ + int nRace = GetRacialType(OBJECT_SELF); + int nPCForm; + int iIsStored = GetPersistantLocalInt( oPC, "AppearanceIsStored" ); + int iStoredAppearance = GetPersistantLocalInt( oPC, "AppearanceStored" ); + + if (iIsStored == 6) + { + nPCForm = iStoredAppearance; + } + else + { + nPCForm = StringToInt(Get2DACache("racialtypes", "Appearance", GetRacialType(oPC))); + } + + return nPCForm; +} + + +// Transforms the oPC into the oTarget using the epic rules +// Assumes oTarget is already a valid target +int SetShiftEpic(object oPC, object oTarget) +{ + + SetLocalInt(oPC,"EpicShift",1); //this makes the setshift_2 script do the epic shifter stuff that used to be here + + SetShift(oPC, oTarget); + + return TRUE; +} + + +// Creates a temporary creature for the shifter to shift into +// Validates the shifter is able to become that creature based on level +// Return values: TRUE or FALSE +// the epic version of this script was rolled into this 1 with the +// addition of the iEpic peramiter +int SetShiftFromTemplateValidate(object oPC, string sTemplate, int iEpic) +{ + if (!CanShift(oPC)) + { + return FALSE; + } + int bRetValue = FALSE; + int in_list = IsKnownCreature(oPC, sTemplate); + + if (iEpic==TRUE) + { + if (!GetHasFeat(FEAT_PRESTIGE_SHIFTER_EGWSHAPE_1, oPC)) + return FALSE; + else + DecrementRemainingFeatUses(oPC,FEAT_PRESTIGE_SHIFTER_EGWSHAPE_1); //we are good to go with the shift + } + else + { + if (!GetHasFeat(FEAT_PRESTIGE_SHIFTER_GWSHAPE_1, oPC)) + return FALSE; + else + DecrementRemainingFeatUses(oPC,FEAT_PRESTIGE_SHIFTER_GWSHAPE_1); //we are good to go with the shift + } + if (!GetHasFeat(FEAT_PRESTIGE_SHIFTER_GWSHAPE_1, oPC)) //if your out of GWS + { + if (GetHasFeat(FEAT_WILD_SHAPE, oPC)) //and you have DWS left + { + if(GetLocalInt(oPC, "DWS") == 1) //and you wont to change then over to GWS + { + IncrementRemainingFeatUses(oPC,FEAT_PRESTIGE_SHIFTER_GWSHAPE_1); // +1 GWS + DecrementRemainingFeatUses(oPC,FEAT_WILD_SHAPE); //-1 DWS + } + } + } + int i=0; + object oLimbo=GetObjectByTag("Limbo",i); + location lLimbo; + while (i < 100) + { + if (GetIsObjectValid(oLimbo)) + { + if (GetName(oLimbo)=="Limbo") + { + i = 2000; + vector vLimbo = Vector(0.0f, 0.0f, 0.0f); + lLimbo = Location(oLimbo, vLimbo, 0.0f); + } + } + i++; + object oLimbo=GetObjectByTag("Limbo",i); + } + object oTarget; + if (i>=2000) + { + oTarget = CreateObject(OBJECT_TYPE_CREATURE,sTemplate,lLimbo); + } + else + { + oTarget = CreateObject(OBJECT_TYPE_CREATURE,sTemplate,GetLocation(oPC)); + } + + if (!GetIsObjectValid(oTarget)) + { + SendMessageToPC(oPC, "Not a valid creature."); + } + if ( !in_list ) + { + SendMessageToPC( oPC, "You have not mimiced this creature yet." ); + } + + // Make sure the PC can take on that form + if (GetValidShift(oPC, oTarget) && in_list ) + { + //get the appearance before changing it + SetLocalInt(oTarget,"Appearance",GetAppearanceType(oTarget)); + //set appearance to invis so it dont show up when scripts run thro + SetCreatureAppearanceType(oTarget,APPEARANCE_TYPE_INVISIBLE_HUMAN_MALE); + //set oTarget for deletion + SetLocalInt(oTarget,"pnp_shifter_deleteme",1); + //Shift the PC to it + bRetValue = TRUE; + if (iEpic == TRUE) + SetShiftEpic(oPC, oTarget); + else + SetShift(oPC, oTarget); + } + else //if we're not gona shift we need to get ride of the creature + { + // Remove the temporary creature + AssignCommand(oTarget,SetIsDestroyable(TRUE,FALSE,FALSE)); + SetPlotFlag(oTarget,FALSE); + SetImmortal(oTarget,FALSE); + DestroyObject(oTarget); + } + return bRetValue; +} + +// helper function to SetVisualTrueForm() for DelayCommand() to work on +void SetBodyPartTrueForm(object oPC, int i) +{ + int nBodyPartValue = GetPersistantLocalInt(oPC, "AppearanceStoredPart"+IntToString(i)); + if(GetCreatureBodyPart(i) != nBodyPartValue) // if the stored and current values are different + SetCreatureBodyPart(i, nBodyPartValue, oPC); +} + +//returns the PC to their original form +//purely visual +void SetVisualTrueForm(object oPC) +{ + if(GetPersistantLocalInt(oPC,"AppearanceIsStored") == 6) + { + SetCreatureAppearanceType(oPC, GetPersistantLocalInt(oPC,"AppearanceStored")); + SetPortraitId(oPC, GetPersistantLocalInt(oPC, "AppearanceStoredPortraitID")); + SetPortraitResRef(oPC, GetPersistantLocalString(oPC, "AppearanceStoredPortraitResRef")); + SetCreatureTailType(GetPersistantLocalInt(oPC, "AppearanceStoredTail"), oPC); + SetCreatureWingType(GetPersistantLocalInt(oPC, "AppearanceStoredWing"), oPC); + int i; + for(i=0;i<=20;i++) + { + DelayCommand(1.0, SetBodyPartTrueForm(oPC, i)); + } + } + else + //hasnt been previously stored + //use racial lookup + SetCreatureAppearanceType(oPC, GetTrueForm(oPC)); +} + + +// Transforms the oPC back to thier true form if they are shifted +void SetShiftTrueForm(object oPC) +{ + // Remove all the creature equipment and destroy it + object oHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + object oWeapCR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeapCL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeapCB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + + // Do not move or destroy the objects, it will crash the game + if (GetIsObjectValid(oHide)) + { + // Remove all the abilities of the object + ScrubPCSkin(oPC,oHide); + DeletePRCLocalInts(oHide); + AssignCommand(oPC, ActionEquipItem(oHide, INVENTORY_SLOT_CARMOUR)); //reequip the hide to get item props to update properly + } + + itemproperty ipUnarmed = ItemPropertyMonsterDamage(2); + + if (GetIsObjectValid(oWeapCR)) + { + //remove creature weapons + ClearCreatureItem(oPC, oWeapCR); + //AssignCommand(oPC,ActionUnequipItem(oWeapCR)); + } + if (GetIsObjectValid(oWeapCL)) + { + //remove creature weapons + ClearCreatureItem(oPC, oWeapCL); + //AssignCommand(oPC,ActionUnequipItem(oWeapCL)); + + } + if (GetIsObjectValid(oWeapCB)) + { + //remove creature weapons + ClearCreatureItem(oPC, oWeapCB); + //AssignCommand(oPC,ActionUnequipItem(oWeapCB)); + } + // if the did an epic form remove the special powers + object oEpicPowersItem = GetItemPossessedBy(oPC,"EpicShifterPowers"); + if (GetIsObjectValid(oEpicPowersItem)) + { + ClearCreatureItem(oPC, oEpicPowersItem); + //RemoveAllItemProperties(oEpicPowersItem); + RemoveAuraEffect( oPC ); + } + + + // Spell failure was done through an effect + // AC was done via an effect + // invis was done via an effect + // we will look for and remove them + effect eEff = GetFirstEffect(oPC); + while (GetIsEffectValid(eEff)) + { + int eDurType = GetEffectDurationType(eEff); + int eSubType = GetEffectSubType(eEff); + int eType = GetEffectType(eEff); + int eID = GetEffectSpellId(eEff); + object eCreator = GetEffectCreator(eEff); + //all three effects are permanent and supernatural and are created by spell id -1 and by the PC. + if ((eDurType == DURATION_TYPE_PERMANENT) && (eSubType == SUBTYPE_SUPERNATURAL) && (eID == -1) && (eCreator == oPC)) + { + switch (eType) + { + case EFFECT_TYPE_SPELL_FAILURE: + case EFFECT_TYPE_INVISIBILITY: + case EFFECT_TYPE_AC_INCREASE: + case EFFECT_TYPE_AC_DECREASE: + case EFFECT_TYPE_ATTACK_INCREASE: + case EFFECT_TYPE_ATTACK_DECREASE: + case EFFECT_TYPE_DAMAGE_INCREASE: + case EFFECT_TYPE_DAMAGE_DECREASE: + case EFFECT_TYPE_TEMPORARY_HITPOINTS: + RemoveEffect(oPC,eEff); + break; + } + } + if (eType == EFFECT_TYPE_POLYMORPH) + { + RemoveEffect(oPC,eEff); + } + eEff = GetNextEffect(oPC); + } + + // Change the PC appearance back to TRUE form + SetVisualTrueForm(oPC); + + // Set race back to unused + SetLocalInt(oPC, "RACIAL_TYPE", 0); + + // Reset shifted state + SetPersistantLocalInt(oPC, "nPCShifted", FALSE); + +} + + +void ClearCreatureItem(object oPC, object oTarget) +{ + AssignCommand(oPC, ActionUnequipItem(oTarget)); + DelayCommand(0.10, AssignCommand(oPC, DestroyObject(oTarget))); +} \ No newline at end of file diff --git a/src/include/pnp_shft_poly.nss b/src/include/pnp_shft_poly.nss new file mode 100644 index 0000000..ce6d860 --- /dev/null +++ b/src/include/pnp_shft_poly.nss @@ -0,0 +1,249 @@ +// Used to polymorph characters to lycanthrope shapes +// Merges Weapons, Armors, Items if told to by 2da. +// - object oPC: Player to Polymorph +// - int nPoly: POLYMORPH_TYPE_* Constant +void LycanthropePoly(object oPC, int nPoly); + +void DoDisguise(int nRace, object oTarget = OBJECT_SELF); +void ShifterCheck(object oPC); + +#include "pnp_shft_main" +#include "prc_inc_shifting" + +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +////////////////Begin Werewolf////////////////// + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly; + + ePoly = EffectPolymorph(nPoly); + ePoly = SupernaturalEffect(ePoly); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + if (GetIsObjectValid(oShield)) + { + if (GetBaseItemType(oShield) !=BASE_ITEM_LARGESHIELD && + GetBaseItemType(oShield) !=BASE_ITEM_SMALLSHIELD && + GetBaseItemType(oShield) !=BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + //check if a shifter and if shifted then unshift + ShifterCheck(oPC); + + ClearAllActions(); // prevents an exploit + + //Apply the VFX impact and effects + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + if (bWeapon) + { + IPWildShapeCopyItemProperties(oWeaponOld,oWeaponNewLeft, TRUE); + IPWildShapeCopyItemProperties(oWeaponOld,oWeaponNewRight, TRUE); + IPWildShapeCopyItemProperties(oWeaponOld,oWeaponNewBite, TRUE); + } + if (bArmor) + { + IPWildShapeCopyItemProperties(oShield,oArmorNew); + IPWildShapeCopyItemProperties(oHelmetOld,oArmorNew); + IPWildShapeCopyItemProperties(oArmorOld,oArmorNew); + } + if (bItems) + { + IPWildShapeCopyItemProperties(oRing1Old,oArmorNew); + IPWildShapeCopyItemProperties(oRing2Old,oArmorNew); + IPWildShapeCopyItemProperties(oAmuletOld,oArmorNew); + IPWildShapeCopyItemProperties(oCloakOld,oArmorNew); + IPWildShapeCopyItemProperties(oBootsOld,oArmorNew); + IPWildShapeCopyItemProperties(oBeltOld,oArmorNew); + } + +} + +////////////////End Werewolf////////////////// + +void ShifterCheck(object oPC) +{ + if (!GetIsPC(oPC)) + return; + //int iShifterLevels = GetLevelByClass(CLASS_TYPE_PNP_SHIFTER,oPC); + //if (iShifterLevels>0) + //{ + int iTemp = GetPersistantLocalInt(oPC,"nPCShifted"); + + if (iTemp) + { + DoDebug("ShifterCheck(): calling UnShift()"); + //SetShiftTrueForm(oPC); + UnShift(oPC, TRUE); + } + //} +} + + +//changes portrait, head, and appearance +//based on the target race with a degree of randomization. +void DoDisguise(int nRace, object oTarget = OBJECT_SELF) +{ + //store current appearance to be safe + StoreAppearance(oTarget); + int nAppearance; //appearance to change into + int nHeadMax; //max head ID, changed to random 1-max + int nGender = GetGender(oTarget); + int nPortraitMin;//minimum row in portraits.2da + int nPortraitMax;//maximum row in portraits.2da + switch(nRace) + { + case RACIAL_TYPE_DWARF: + nAppearance = APPEARANCE_TYPE_DWARF; + if(nGender == GENDER_MALE) + { nHeadMax = 10; nPortraitMin = 9; nPortraitMax = 17; } + else + { nHeadMax = 12; nPortraitMin = 1; nPortraitMax = 8; } + break; + case RACIAL_TYPE_ELF: + nAppearance = APPEARANCE_TYPE_ELF; + if(nGender == GENDER_MALE) + { nHeadMax = 10; nPortraitMin = 31; nPortraitMax = 40; } + else + { nHeadMax = 16; nPortraitMin = 18; nPortraitMax = 30; } + break; + case RACIAL_TYPE_HALFELF: + nAppearance = APPEARANCE_TYPE_HALF_ELF; + if(nGender == GENDER_MALE) + { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } + else + { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } + break; + case RACIAL_TYPE_HALFORC: + nAppearance = APPEARANCE_TYPE_HALF_ORC; + if(nGender == GENDER_MALE) + { nHeadMax = 11; nPortraitMin = 134; nPortraitMax = 139; } + else + { nHeadMax = 1; nPortraitMin = 130; nPortraitMax = 133; } + break; + case RACIAL_TYPE_HUMAN: + nAppearance = APPEARANCE_TYPE_HUMAN; + if(nGender == GENDER_MALE) + { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } + else + { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } + break; + case RACIAL_TYPE_HALFLING: + nAppearance = APPEARANCE_TYPE_HALFLING; + if(nGender == GENDER_MALE) + { nHeadMax = 8; nPortraitMin = 61; nPortraitMax = 66; } + else + { nHeadMax = 11; nPortraitMin = 54; nPortraitMax = 59; } + break; + case RACIAL_TYPE_GNOME: + nAppearance = APPEARANCE_TYPE_GNOME; + if(nGender == GENDER_MALE) + { nHeadMax = 11; nPortraitMin = 47; nPortraitMax = 53; } + else + { nHeadMax = 9; nPortraitMin = 41; nPortraitMax = 46; } + break; + default: //not a normal race, abort + return; + } + //change the appearance + SetCreatureAppearanceType(oTarget, nAppearance); + + //need to be delayed a bit otherwise you get "supergnome" and "exploded elf" effects + DelayCommand(1.1, SetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN, d2(), oTarget)); + DelayCommand(1.2, SetCreatureBodyPart(CREATURE_PART_LEFT_SHIN, d2(), oTarget)); + DelayCommand(1.3, SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, d2(), oTarget)); + DelayCommand(1.4, SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, d2(), oTarget)); + DelayCommand(1.5, SetCreatureBodyPart(CREATURE_PART_TORSO, d2(), oTarget)); + DelayCommand(1.6, SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, d2(), oTarget)); + DelayCommand(1.7, SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, d2(), oTarget)); + DelayCommand(1.8, SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, d2(), oTarget)); + DelayCommand(1.9, SetCreatureBodyPart(CREATURE_PART_LEFT_BICEP, d2(), oTarget)); + + //dont do these body parts, they dont have tattoos and weird things could happen + //SetCreatureBodyPart(CREATURE_PART_BELT, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_NECK, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_PELVIS, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, d2(), oTarget); + //randomise the head + DelayCommand(2.0, SetCreatureBodyPart(CREATURE_PART_HEAD, Random(nHeadMax)+1, oTarget)); + + //remove any wings/tails + SetCreatureWingType(CREATURE_WING_TYPE_NONE, oTarget); + SetCreatureTailType(CREATURE_TAIL_TYPE_NONE, oTarget); + + int nPortraitID = Random(nPortraitMax-nPortraitMin+1)+nPortraitMin; + string sPortraitResRef = Get2DACache("portraits", "BaseResRef", nPortraitID); + sPortraitResRef = GetStringLeft(sPortraitResRef, GetStringLength(sPortraitResRef)-1); //trim the trailing _ + SetPortraitResRef(oTarget, sPortraitResRef); + SetPortraitId(oTarget, nPortraitID); +} + +//utility functions to make sure characters that gain wings/tails permanently +//interact with the polymorph system by overwriting the default form + + +void DoWings(object oPC, int nWingType) +{ + //wing invalid, abort + if(nWingType <= 0) + return; + //already has wings, keep them + if(GetCreatureWingType(oPC) != CREATURE_WING_TYPE_NONE) + return; + //already has a default wings, keep them + if(GetPersistantLocalInt(oPC, "AppearanceStoredWing")) + return; + //if polymorphed or shifted, dont do the real change + if(!GetIsPolyMorphedOrShifted(oPC)) + SetCreatureWingType(nWingType, oPC); + //override any stored default appearance + SetPersistantLocalInt(oPC, "AppearanceStoredWing", nWingType); +} + +void DoTail(object oPC, int nTailType) +{ + //tail invalid, use current + if(nTailType == -1) + return; + //already has tail, keep it + if(GetCreatureTailType(oPC)) + return; + //already has a default tail, keep it + if(GetPersistantLocalInt(oPC, "AppearanceStoredTail")) + return; + //if polymorphed or shifted, dont do the real change + if(!GetIsPolyMorphedOrShifted(oPC)) + SetCreatureTailType(nTailType, oPC); + //override any stored default appearance + SetPersistantLocalInt(oPC, "AppearanceStoredTail", nTailType); +} \ No newline at end of file diff --git a/src/include/prc_add_spell_dc.nss b/src/include/prc_add_spell_dc.nss new file mode 100644 index 0000000..4cde6de --- /dev/null +++ b/src/include/prc_add_spell_dc.nss @@ -0,0 +1,788 @@ +// Get the DC to save against for a spell (10 + spell level + relevant ability +// bonus). This can be called by a creature or by an Area of Effect object. +// Takes into account PRC classes +int PRCGetSpellSaveDC(int nSpellID = -1, int nSchool = -1, object oCaster = OBJECT_SELF); + +// Use this function to get the adjustments to a spell or SLAs saving throw +// from the various class effects +// Update this function if any new classes change saving throws +int PRCGetSaveDC(object oTarget, object oCaster, int nSpellID = -1); + +//called just from above and from inc_epicspells +int GetChangesToSaveDC(object oTarget, object oCaster, int nSpellID, int nSchool); + +#include "prc_add_spl_pen" +// #include "prc_inc_spells" +// #include "prc_class_const" +// #include "prc_feat_const" +// #include "lookup_2da_spell" +// #include "prcsp_archmaginc" +// #include "prc_alterations" +// #include "prc_inc_racial" +#include "inc_newspellbook" + +int GetCorruptSpellFocus(int nSpellID, object oCaster) +{ + int nCorrupt = FALSE; + if(nSpellID == SPELL_ABSORB_STRENGTH + || nSpellID == SPELL_APOCALYPSE_FROM_THE_SKY + || nSpellID == SPELL_CLAWS_OF_THE_BEBILITH + || nSpellID == SPELL_DEATH_BY_THORNS + || nSpellID == SPELL_EVIL_WEATHER + || nSpellID == SPELL_FANGS_OF_THE_VAMPIRE_KING + || nSpellID == SPELL_LAHMS_FINGER_DARTS + || nSpellID == SPELL_POWER_LEECH + || nSpellID == SPELL_RAPTURE_OF_RUPTURE + || nSpellID == SPELL_RED_FESTER + || nSpellID == SPELL_ROTTING_CURSE_OF_URFESTRA + || nSpellID == SPELL_SEETHING_EYEBANE + || nSpellID == SPELL_TOUCH_OF_JUIBLEX) + nCorrupt = TRUE; + + if (GetHasFeat(FEAT_GREATER_CORRUPT_SPELL_FOCUS, oCaster) && nCorrupt) return 2; + else if (GetHasFeat(FEAT_CORRUPT_SPELL_FOCUS, oCaster) && nCorrupt) return 1; + + return 0; +} + +int GetHeartWarderDC(int spell_id, int nSchool, object oCaster) +{ + // Check the curent school + if(nSchool != SPELL_SCHOOL_ENCHANTMENT) + return 0; + + if(!GetHasFeat(FEAT_VOICE_SIREN, oCaster)) + return 0; + + // Bonus Requires Verbal Spells + string VS = GetStringLowerCase(Get2DACache("spells", "VS",spell_id)); + if(FindSubString(VS, "v") == -1) + return 0; + + // These feats provide greater bonuses or remove the Verbal requirement + if(PRCGetMetaMagicFeat(oCaster, FALSE) & METAMAGIC_SILENT + || GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oCaster) + || GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ENCHANTMENT, oCaster)) + return 0; + + return 2; +} + +//Elemental Savant DC boost based on elemental spell type. +int ElementalSavantDC(int spell_id, int nElement, object oCaster) +{ + int nDC = 0; + + // All Elemental Savants will have this feat + // when they first gain a DC bonus. + if(GetHasFeat(FEAT_ES_FOCUS_1, oCaster)) + { + // Any value that does not match one of the enumerated feats + int feat, nES; + nES = GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + // Specify the elemental type rather than lookup by class? + if(nElement & DESCRIPTOR_FIRE) + { + feat = FEAT_ES_FIRE; + } + else if(nElement & DESCRIPTOR_COLD) + { + feat = FEAT_ES_COLD; + } + else if(nElement & DESCRIPTOR_ELECTRICITY) + { + feat = FEAT_ES_ELEC; + } + else if(nElement & DESCRIPTOR_ACID) + { + feat = FEAT_ES_ACID; + } + + // Now determine the bonus + if(feat && GetHasFeat(feat, oCaster)) + nDC = (nES + 1) / 3; + } +// SendMessageToPC(GetFirstPC(), "Your Elemental Focus modifier is " + IntToString(nDC)); + return nDC; +} + +// This does other spell focus feats, starting with Spell Focus: Cold +int SpellFocus(int nSpellId, int nElement, object oCaster) +{ + int nDC = 0; + + // Specify the elemental type + if(nElement & DESCRIPTOR_COLD) + { + if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_COLD, oCaster)) + nDC += 2; + else if(GetHasFeat(FEAT_SPELL_FOCUS_COLD, oCaster)) + nDC += 1; + } + if (GetHasDescriptor(nSpellId, DESCRIPTOR_CHAOTIC) && GetHasFeat(FEAT_SPELL_FOCUS_CHAOS, oCaster)) nDC += 1; + if (GetHasDescriptor(nSpellId, DESCRIPTOR_EVIL) && GetHasFeat(FEAT_SPELL_FOCUS_EVIL, oCaster)) nDC += 1; + if (GetHasDescriptor(nSpellId, DESCRIPTOR_GOOD) && GetHasFeat(FEAT_SPELL_FOCUS_GOOD, oCaster)) nDC += 1; + if (GetHasDescriptor(nSpellId, DESCRIPTOR_LAWFUL) && GetHasFeat(FEAT_SPELL_FOCUS_LAWFUL, oCaster)) nDC += 1; + + return nDC; +} + +//Red Wizard DC boost based on spell school specialization +int RedWizardDC(int spell_id, int nSchool, object oCaster) +{ + int iRedWizard = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); + int nDC; + + if(iRedWizard) + { + int iRWSpec; + switch(nSchool) + { + case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break; + case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break; + case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break; + case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break; + case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break; + case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break; + case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break; + case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break; + } + + if(iRWSpec && GetHasFeat(iRWSpec, oCaster)) + nDC = iRedWizard / 2; + } +// SendMessageToPC(GetFirstPC(), "Your Spell Power modifier is " + IntToString(nDC)); + return nDC; +} + +//Red Wizards recieve a bonus against their specialist schools +// this is done by lowering the DC of spells cast against them +int RedWizardDCPenalty(int spell_id, int nSchool, object oTarget) +{ + int nDC; + int iRW = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oTarget); + if(iRW) + { + int iRWSpec; + switch(nSchool) + { + case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break; + case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break; + case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break; + case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break; + case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break; + case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break; + case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break; + case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break; + } + + if(iRWSpec && GetHasFeat(iRWSpec, oTarget)) + nDC -= iRW > 4 ? (iRW - 1) / 2 : (iRW + 1) / 2; + } + return nDC; +} + +int ShadowAdeptDCPenalty(int spell_id, int nSchool, object oTarget) +{ + int nDC; + int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oTarget); + if(iShadow) + { + if(nSchool == SPELL_SCHOOL_ENCHANTMENT + || nSchool == SPELL_SCHOOL_NECROMANCY + || nSchool == SPELL_SCHOOL_ILLUSION) + { + nDC -= (iShadow + 1) / 3; + } + //SendMessageToPC(GetFirstPC(), "Your Spell Save modifier is " + IntToString(nDC)); + } + return nDC; +} + +//Tattoo Focus DC boost based on spell school specialization +int TattooFocus(int spell_id, int nSchool, object oCaster) +{ + int nDC; + int iRWSpec; + switch(nSchool) + { + case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break; + case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break; + case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break; + case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break; + case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break; + case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break; + case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break; + case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break; + } + + if(iRWSpec && GetHasFeat(iRWSpec, oCaster)) + nDC = 1; + + return nDC; +} + +//:: Jaebrins get a +1 to Enchantment spells. +int JaebrinEnchant(int nSchool, object oCaster) +{ + int nDC; + + if(nSchool == SPELL_SCHOOL_ENCHANTMENT && GetRacialType(oCaster) == RACIAL_TYPE_JAEBRIN) + nDC = 1; + + return nDC; +} + +int ShadowWeaveDC(int spell_id, int nSchool, object oCaster) +{ + // Account for the Shadow Weave feat + int nDC = ShadowWeave(oCaster, spell_id, nSchool) == 1; + + // Account for Shadow Adept levels + int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + if(iShadow && nDC) + // Shadow Spell Power + nDC += iShadow / 3; + + return nDC; +} + +int KOTCSpellFocusVsDemons(object oTarget, object oCaster) +{ + if(GetLevelByClass(CLASS_TYPE_KNIGHT_CHALICE, oCaster) >= 1) + { + if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_OUTSIDER) + { + if(GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL) + { + return 2; + } + } + } + return 0; +} + +int BloodMagusBloodComponent(object oCaster) +{ + int nDC = 0; + if (GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster) > 0 && GetLocalInt(oCaster, "BloodComponent") == TRUE) + { + nDC = 1; + effect eSelfDamage = EffectDamage(1, DAMAGE_TYPE_MAGICAL); + // To make sure it doesn't cause a conc check + DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eSelfDamage, oCaster)); + } + return nDC; +} + +int RunecasterRunePowerDC(object oCaster) +{ + int nDC; + + if(GetHasSpellEffect(SPELL_RUNE_CHANT)) + { + int nClass = GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if (nClass >= 30) nDC = 10; + else if (nClass >= 27) nDC = 9; + else if (nClass >= 24) nDC = 8; + else if (nClass >= 21) nDC = 7; + else if (nClass >= 18) nDC = 6; + else if (nClass >= 15) nDC = 5; + else if (nClass >= 12) nDC = 4; + else if (nClass >= 9) nDC = 3; + else if (nClass >= 5) nDC = 2; + else if (nClass >= 2) nDC = 1; + } + return nDC; +} + + //Unheavened spell +int UnheavenedAdjustment(object oTarget, object oCaster) +{ + if(GetHasSpellEffect(SPELL_UNHEAVENED, oTarget)) + { + if((MyPRCGetRacialType(oCaster) == RACIAL_TYPE_OUTSIDER) && (GetAlignmentGoodEvil(oCaster) == ALIGNMENT_GOOD)) + { + return -4; + } + } + return 0; +} + +// Soul Eater's 10th level Soul Power ability. If they've drained in the last 24h, they get +2 to DCs +int SoulEaterSoulPower(object oCaster) +{ + return (GetLocalInt(oCaster, "PRC_SoulEater_HasDrained") && GetLevelByClass(CLASS_TYPE_SOUL_EATER, oCaster) >= 10) ? 2 : 0; +} + +//:: Saint Template gets a +2 DC on all spells, powers & abilites. +int SaintHolySpellPower(object oCaster) +{ + if(GetHasFeat(FEAT_TEMPLATE_SAINT_HOLY_POWER, oCaster)) + { + if (GetAlignmentGoodEvil(oCaster) == ALIGNMENT_GOOD) + { + return 2; + } + else + { + return 0; + } + } +//:: If it gets here, the caster does not have the feat + return 0; +} + +//Draconic Power's elemental boost to spell DCs +int DraconicPowerDC(int spell_id, int nElement, object oCaster) +{ + if(GetHasFeat(FEAT_DRACONIC_POWER, oCaster)) + { + // Compare heritage type and elemental type + if(nElement & DESCRIPTOR_FIRE) + { + if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BS, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_GD, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_RD, oCaster)) + return 1; + } + else if(nElement & DESCRIPTOR_COLD) + { + if(GetHasFeat(FEAT_DRACONIC_HERITAGE_CR, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_SR, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_TP, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_WH, oCaster)) + return 1; + } + else if(nElement & DESCRIPTOR_ELECTRICITY) + { + if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BL, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_BZ, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_SA, oCaster)) + return 1; + } + else if(nElement & DESCRIPTOR_ACID) + { + if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BK, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_CP, oCaster) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_GR, oCaster)) + return 1; + } + else if(nElement & DESCRIPTOR_SONIC) + { + if(GetHasFeat(FEAT_DRACONIC_HERITAGE_EM, oCaster)) + return 1; + } + } + + //if it gets here, the caster does not have the feat, or is a heritage type without a NWN element (e.g. Amethyst) + return 0; +} + +//Energy Draconc Aura's elemental boost to spell DCs +int EnergyAuraDC(int spell_id, int nElement, object oCaster) +{ + // Compare aura type and elemental type + if(nElement & DESCRIPTOR_FIRE) + return GetLocalInt(oCaster, "FireEnergyAura"); + + else if(nElement & DESCRIPTOR_COLD) + return GetLocalInt(oCaster, "ColdEnergyAura"); + + else if(nElement & DESCRIPTOR_ELECTRICITY) + return GetLocalInt(oCaster, "ElecEnergyAura"); + + else if(nElement & DESCRIPTOR_ACID) + return GetLocalInt(oCaster, "AcidEnergyAura"); + + //if it gets here, the caster is not in this type of Draconic Aura + return 0; +} + +//Spirit Folk get a better save vs elemental stuff +int SpiritFolkAdjustment(int spell_id, int nElement, object oTarget) +{ + if(nElement & DESCRIPTOR_FIRE && GetHasFeat(FEAT_BONUS_SEA, oTarget)) + { + return -2; + } + else if(nElement & DESCRIPTOR_COLD && GetHasFeat(FEAT_BONUS_RIVER, oTarget)) + { + return -2; + } + else if(nElement & DESCRIPTOR_ACID && GetHasFeat(FEAT_BONUS_BAMBOO, oTarget)) + { + return -2; + } + + //if it gets here, the target is not a Spirit Folk + return 0; +} + +//Angry Spell for Rage Mage class +int AngrySpell(int spell_id, int nSchool, object oCaster) +{ + int nDC; + + if(GetHasSpellEffect(SPELL_SPELL_RAGE, oCaster)) + { + if(nSchool == SPELL_SCHOOL_ABJURATION + || nSchool == SPELL_SCHOOL_CONJURATION + || nSchool == SPELL_SCHOOL_EVOCATION + || nSchool == SPELL_SCHOOL_NECROMANCY + || nSchool == SPELL_SCHOOL_TRANSMUTATION) + { + if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) >= 10) + nDC = 4; + else if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) >= 5) + nDC = 2; + } + } + + return nDC; +} + +int CloakedCastingDC(int spell_id, object oTarget, object oCaster) +{ + int nDC; + int iBeguiler = GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster); + + if(iBeguiler) + { + if(GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE)) + { + if(iBeguiler >= 14) + nDC = 2; + else if(iBeguiler >= 2) + nDC = 1; + } + } + + return nDC; +} + + // Wyrmbane Helm +int WyrmbaneHelmDC(object oTarget, object oCaster) +{ + // You get nothing if you aren't wielding the legacy item + object oWOL = GetItemPossessedBy(oCaster, "WOL_Wyrmbane"); + if(oWOL != GetItemInSlot(INVENTORY_SLOT_HEAD, oCaster)) return 0; + + if((MyPRCGetRacialType(oTarget) == RACIAL_TYPE_DRAGON)) + { + return 2; + } + return 0; +} + +// Arkamoi Strength From Magic +int StrengthFromMagic(object oCaster) +{ + if (GetRacialType(oCaster) != RACIAL_TYPE_ARKAMOI) + return 0; + + if (GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster))) + return GetLocalInt(oCaster, "StrengthFromMagic"); + + return 0; +} + +// Hobgoblin Warsoul Soul Tyrant +int SoulTyrant(object oCaster) +{ + if (GetRacialType(oCaster) != RACIAL_TYPE_HOBGOBLIN_WARSOUL) + return 0; + + if (GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster))) + return GetLocalInt(oCaster, "WarsoulTyrant"); + + return 0; +} + +int PRCGetSpellSaveDC(int nSpellID = -1, int nSchool = -1, object oCaster = OBJECT_SELF) +{ + if(nSpellID == -1) + nSpellID = PRCGetSpellId(); + if(nSchool == -1) + nSchool = GetSpellSchool(nSpellID); + + int nClass = PRCGetLastSpellCastClass(oCaster); + int nDC = 10; + + if(nClass == CLASS_TYPE_BARD) + nDC += StringToInt(Get2DACache("Spells", "Bard", nSpellID)); + else if(nClass == CLASS_TYPE_CLERIC || nClass == CLASS_TYPE_UR_PRIEST || nClass == CLASS_TYPE_OCULAR) + nDC += StringToInt(Get2DACache("Spells", "Cleric", nSpellID)); + else if(nClass == CLASS_TYPE_DRUID) + nDC += StringToInt(Get2DACache("Spells", "Druid", nSpellID)); + else if(nClass == CLASS_TYPE_RANGER) + nDC += StringToInt(Get2DACache("Spells", "Ranger", nSpellID)); + else if(nClass == CLASS_TYPE_PALADIN) + nDC += StringToInt(Get2DACache("Spells", "Paladin", nSpellID)); + else if (nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK) + nDC += StringToInt(Get2DACache("spells", "Cultist", nSpellID)); + else if (nClass == CLASS_TYPE_NENTYAR_HUNTER) + nDC += StringToInt(Get2DACache("spells", "Nentyar", nSpellID)); + else if (nClass == CLASS_TYPE_SHADOWLORD) + nDC += StringToInt(Get2DACache("spells", "Telflammar", nSpellID)); + else if (nClass == CLASS_TYPE_SLAYER_OF_DOMIEL) + nDC += StringToInt(Get2DACache("spells", "Domiel", nSpellID)); + else if (nClass == CLASS_TYPE_SOHEI) + nDC += StringToInt(Get2DACache("spells", "Sohei", nSpellID)); + else if (nClass == CLASS_TYPE_VASSAL) + nDC += StringToInt(Get2DACache("spells", "Bahamut", nSpellID)); + else if (nClass == CLASS_TYPE_BLACKGUARD) + nDC += StringToInt(Get2DACache("spells", "Blackguard", nSpellID)); + else if (nClass == CLASS_TYPE_KNIGHT_CHALICE) + nDC += StringToInt(Get2DACache("spells", "Chalice", nSpellID)); + else if (nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE) + nDC += StringToInt(Get2DACache("spells", "MiddleCircle", nSpellID)); + else if (nClass == CLASS_TYPE_SOLDIER_OF_LIGHT) + nDC += StringToInt(Get2DACache("spells", "SoLight", nSpellID)); + else if (nClass == CLASS_TYPE_BLIGHTER) + nDC += StringToInt(Get2DACache("spells", "Blighter", nSpellID)); + else if (nClass == CLASS_TYPE_HEALER) + nDC += StringToInt(Get2DACache("spells", "Healer", nSpellID)); + else if (nClass == CLASS_TYPE_SHAMAN) + nDC += StringToInt(Get2DACache("spells", "Shaman", nSpellID)); + else if(nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER) + nDC += StringToInt(Get2DACache("Spells", "Wiz_Sorc", nSpellID)); + else if(nClass != CLASS_TYPE_INVALID) + { + int nSpellbookID = RealSpellToSpellbookID(nClass, nSpellID); + string sFile = GetFileForClass(nClass); + nDC += StringToInt(Get2DACache(sFile, "Level", nSpellbookID)); + } + else + nDC += StringToInt(Get2DACache("Spells", "Innate", nSpellID)); + + // This is here because a Cleric casting a domain spell like Chain Lightning has a 0 in the cleric column, resulting in a DC of 10 + if (nDC == 10 && nClass == CLASS_TYPE_CLERIC) + nDC += StringToInt(Get2DACache("Spells", "Innate", nSpellID)); + + nDC += GetDCAbilityModForClass(nClass, oCaster); + + object oItem = GetSpellCastItem(); + + int nEpic = 6; + int nGreat = 4; + int nSF = 2; + + if (GetPRCSwitch(PRC_35_SPELL_FOCUS)) + { + nEpic = 3; + nGreat = 2; + nSF = 1; + } + + if(DEBUG && !GetIsObjectValid(oItem)) DoDebug("PRCGetSpellSaveDC oItem is OBJECT_INVALID"); + if(DEBUG) DoDebug("PRCGetSpellSaveDC oCaster "+GetName(oCaster)+", nSpell "+IntToString(nSpellID)+", nSchool "+IntToString(nSchool)+", nClass "+IntToString(nClass)+", oItem "+GetName(oItem)); + + if(!GetIsObjectValid(oItem) || (GetBaseItemType(oItem) == BASE_ITEM_MAGICSTAFF && GetPRCSwitch(PRC_STAFF_CASTER_LEVEL))) + { + if(nSchool == SPELL_SCHOOL_EVOCATION) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_EVOCATION, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_EVOCATION, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_EVOCATION, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_TRANSMUTATION) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_TRANSMUTATION, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_TRANSMUTATION, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_TRANSMUTATION, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_NECROMANCY) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_NECROMANCY, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_NECROMANCY, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_NECROMANCY, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_ILLUSION) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ILLUSION, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ILLUSION, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_ILLUSION, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_ABJURATION) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ABJURATION, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ABJURATION, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_ABJURATION, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_CONJURATION) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_CONJURATION, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_CONJURATION, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_CONJURATION, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_DIVINATION) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_DIVINATION, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_DIVINATION, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_DIVINATION, oCaster)) + nDC+=nSF; + } + else if(nSchool == SPELL_SCHOOL_ENCHANTMENT) + { + if(GetHasFeat(FEAT_EPIC_SPELL_FOCUS_ENCHANTMENT, oCaster)) + nDC+=nEpic; + else if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oCaster)) + nDC+=nGreat; + else if(GetHasFeat(FEAT_SPELL_FOCUS_ENCHANTMENT, oCaster)) + nDC+=nSF; + } + } + + return nDC; +} + +int PRCGetSaveDC(object oTarget, object oCaster, int nSpellID = -1) +{ + object oItem = GetSpellCastItem(); + if(nSpellID == -1) + nSpellID = PRCGetSpellId(); + int nSchool = GetSpellSchool(nSpellID); + int nDC; + // at this point, if it's still -1 then this is running on an AoE + if (nSpellID == -1) + { + // get the needed values off the AoE + nSpellID = GetLocalInt(OBJECT_SELF, "X2_AoE_SpellID"); + nDC = GetLocalInt(OBJECT_SELF, "X2_AoE_BaseSaveDC"); + nSchool = GetSpellSchool(nSpellID); + } + else // not persistent AoE script + { + //10+spelllevel+stat(cha default) + nDC = PRCGetSpellSaveDC(nSpellID, nSchool, oCaster); + } + + // For when you want to assign the caster DC + //this does not take feat/race/class into account, it is an absolute override + if (GetLocalInt(oCaster, PRC_DC_TOTAL_OVERRIDE) != 0) + { + nDC = GetLocalInt(oCaster, PRC_DC_TOTAL_OVERRIDE); + if(DEBUG) DoDebug("Forced-DC PRC_DC_TOTAL_OVERRIDE casting at DC " + IntToString(nDC)); + } + // For when you want to assign the caster DC + //this does take feat/race/class into account, it only overrides the baseDC + else if (GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE) != 0) + { + nDC = GetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE); + if(nDC == -1) + nDC = PRCGetSpellSaveDC(nSpellID, nSchool, oCaster); + + if(DEBUG) DoDebug("Forced Base-DC casting at DC " + IntToString(nDC)); + nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, nSchool); + } + else if(GetIsObjectValid(oItem) && !(GetBaseItemType(oItem) == BASE_ITEM_MAGICSTAFF && GetPRCSwitch(PRC_STAFF_CASTER_LEVEL))) + { + //code for getting new ip type + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_DC) + { + int nSubType = GetItemPropertySubType(ipTest); + nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType)); + if(nSubType == nSpellID) + { + nDC = GetItemPropertyCostTableValue (ipTest); + break;//end while + } + } + ipTest = GetNextItemProperty(oItem); + } + int nType = GetBaseItemType(oItem); + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) + { + if (GetHasFeat(FEAT_WAND_MASTERY, oCaster)) + nDC += 2; + } + } + else + nDC += GetChangesToSaveDC(oTarget, oCaster, nSpellID, nSchool); + + // Karsus's Heavy Magic ability + if(GetIsObjectValid(oItem) && GetHasSpellEffect(VESTIGE_KARSUS, oCaster) && GetLocalInt(oCaster, "ExploitVestige") != VESTIGE_KARSUS_HEAVY_MAGIC && (GetLevelByClass(CLASS_TYPE_BINDER, oCaster) || GetHasFeat(FEAT_PRACTICED_BINDER, oCaster))) + nDC += 2; + + //target-based adjustments go here + nDC += RedWizardDCPenalty(nSpellID, nSchool, oTarget); + nDC += ShadowAdeptDCPenalty(nSpellID, nSchool, oTarget); + + if (GetPRCSwitch(PRC_ACTIVATE_MAX_SPELL_DC_CAP)) + { + if (nDC > GetPRCSwitch(PRC_SET_MAX_SPELL_DC_CAP)) + { + nDC = GetPRCSwitch(PRC_SET_MAX_SPELL_DC_CAP); + } + } + + return nDC; + +} + +//called just from above and from inc_epicspells +int GetChangesToSaveDC(object oTarget, object oCaster, int nSpellID, int nSchool) +{ + int nDC; + int nElement = GetIsElementalSpell(nSpellID); + + if(nElement) + { + nDC += ElementalSavantDC(nSpellID, nElement, oCaster); + nDC += SpiritFolkAdjustment(nSpellID, nElement, oTarget); + nDC += SpellFocus(nSpellID, nElement, oCaster); + nDC += DraconicPowerDC(nSpellID, nElement, oCaster); + nDC += EnergyAuraDC(nSpellID, nElement, oCaster); + } + nDC += GetHeartWarderDC(nSpellID, nSchool, oCaster); + nDC += GetSpellPowerBonus(oCaster); + nDC += ShadowWeaveDC(nSpellID, nSchool, oCaster); + nDC += RedWizardDC(nSpellID, nSchool, oCaster); + nDC += TattooFocus(nSpellID, nSchool, oCaster); + nDC += KOTCSpellFocusVsDemons(oTarget, oCaster); + //nDC += BloodMagusBloodComponent(oCaster); + nDC += RunecasterRunePowerDC(oCaster); + nDC += UnheavenedAdjustment(oTarget, oCaster); + nDC += SoulEaterSoulPower(oCaster); + nDC += AngrySpell(nSpellID, nSchool, oCaster); + nDC += CloakedCastingDC(nSpellID, oTarget, oCaster); + nDC += GetCorruptSpellFocus(nSpellID, oCaster); + nDC += Soulcaster(oCaster, nSpellID); + nDC += WyrmbaneHelmDC(oTarget, oCaster); + nDC += StrengthFromMagic(oCaster); + nDC += SoulTyrant(oCaster); + nDC += SaintHolySpellPower(oCaster); + nDC += GetLocalInt(oCaster, PRC_DC_ADJUSTMENT);//this is for builder use + nDC += JaebrinEnchant(nSchool, oCaster); + return nDC; +} + +// Test main +//:: void main(){} diff --git a/src/include/prc_add_spl_pen.nss b/src/include/prc_add_spl_pen.nss new file mode 100644 index 0000000..e339ebd --- /dev/null +++ b/src/include/prc_add_spl_pen.nss @@ -0,0 +1,425 @@ +//:://///////////////////////////////////////////// +//:: Spells include: Spell Penetration +//:: prc_add_spl_pen +//:://///////////////////////////////////////////// +/** @file + Defines functions that may have something to do + with modifying a spell's caster level in regards + to Spell Resistance penetration. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int GetHeartWarderPene(int spell_id, object oCaster = OBJECT_SELF); + +int ElementalSavantSP(int spell_id, object oCaster = OBJECT_SELF); + +int RedWizardSP(int spell_id, int nSchool, object oCaster = OBJECT_SELF); + +int GetSpellPenetreFocusSchool(int nSchool, object oCaster = OBJECT_SELF); + +int GetSpellPowerBonus(object oCaster = OBJECT_SELF); + +int ShadowWeavePen(int spell_id, int nSchool, object oCaster = OBJECT_SELF); + +int KOTCSpellPenVsDemons(object oCaster, object oTarget); + +int RunecasterRunePowerSP(object oCaster); + +int MarshalDeterminedCaster(object oCaster); + +int DuskbladeSpellPower(object oCaster, object oTarget); + +int DraconicMagicPower(object oCaster); + +int TrueCastingSpell(object oCaster); + +string ChangedElementalType(int spell_id, object oCaster = OBJECT_SELF); + +// Use this function to get the adjustments to a spell or SLAs spell penetration +// from the various class effects +// Update this function if any new classes change spell pentration +int add_spl_pen(object oCaster = OBJECT_SELF); + +int SPGetPenetr(object oCaster = OBJECT_SELF); + +int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "prc_inc_spells" +//#include "prc_alterations" +//#include "prcsp_archmaginc" +//#include "prc_inc_racial" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +// +// Determine if a spell type is elemental +// +int IsSpellTypeElemental(string type) +{ + return type == "Acid" + || type == "Cold" + || type == "Electricity" + || type == "Fire" + || type == "Sonic"; +} + +int GetHeartWarderPene(int spell_id, object oCaster = OBJECT_SELF) +{ + // Guard Expensive Calculations + if(!GetHasFeat(FEAT_VOICE_SIREN, oCaster)) + return 0; + + // Bonus Requires Verbal Spells + string VS = GetStringLowerCase(Get2DACache("spells", "VS", spell_id)); + if(FindSubString(VS, "v") == -1) + return 0; + + // These feats provide greater bonuses or remove the Verbal requirement + if(PRCGetMetaMagicFeat(oCaster, FALSE) & METAMAGIC_SILENT + || GetHasFeat(FEAT_SPELL_PENETRATION, oCaster) + || GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oCaster) + || GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oCaster)) + return 0; + + return 2; +} + +// +// Calculate Elemental Savant Contributions +// +int ElementalSavantSP(int spell_id, object oCaster = OBJECT_SELF) +{ + // get spell elemental type + int element = GetIsElementalSpell(spell_id); + + //not an elemental spell + if(!element) + return 0; + + int nSP = 0; + + // All Elemental Savants will have this feat + // when they first gain a penetration bonus. + // Otherwise this would require checking ~4 items (class or specific feats) + if(GetHasFeat(FEAT_ES_PEN_1, oCaster)) + { + int feat, nES; + nES = GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + // Specify the elemental type rather than lookup by class? + if(element & DESCRIPTOR_FIRE) + { + feat = FEAT_ES_FIRE; + } + else if(element & DESCRIPTOR_COLD) + { + feat = FEAT_ES_COLD; + } + else if(element & DESCRIPTOR_ELECTRICITY) + { + feat = FEAT_ES_ELEC; + } + else if(element & DESCRIPTOR_ACID) + { + feat = FEAT_ES_ACID; + } + + // Now determine the bonus + if(feat && GetHasFeat(feat, oCaster)) + nSP = nES / 3; + } +// SendMessageToPC(GetFirstPC(), "Your Elemental Penetration modifier is " + IntToString(nSP)); + return nSP; +} + +//Red Wizard SP boost based on spell school specialization +int RedWizardSP(int spell_id, int nSchool, object oCaster = OBJECT_SELF) +{ + int iRedWizard = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); + int nSP; + + if(iRedWizard) + { + int iRWSpec; + switch(nSchool) + { + case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break; + case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break; + case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break; + case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break; + case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break; + case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break; + case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break; + case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break; + } + + if(iRWSpec && GetHasFeat(iRWSpec, oCaster)) + nSP = (iRedWizard / 2) + 1; + } +// SendMessageToPC(GetFirstPC(), "Your Spell Power modifier is " + IntToString(nSP)); + return nSP; +} + +int GetSpellPenetreFocusSchool(int nSchool, object oCaster = OBJECT_SELF) +{ + if(nSchool) + { + if(GetHasFeat(FEAT_FOCUSED_SPELL_PENETRATION_ABJURATION+nSchool-1, oCaster)) + return 4; + } + + return 0; +} + +int GetSpellPowerBonus(object oCaster = OBJECT_SELF) +{ + if(GetHasFeat(FEAT_SPELLPOWER_10, oCaster)) + return 10; + else if(GetHasFeat(FEAT_SPELLPOWER_8, oCaster)) + return 8; + else if(GetHasFeat(FEAT_SPELLPOWER_6, oCaster)) + return 6; + else if(GetHasFeat(FEAT_SPELLPOWER_4, oCaster)) + return 4; + else if(GetHasFeat(FEAT_SPELLPOWER_2, oCaster)) + return 2; + + return 0; +} + +// Shadow Weave Feat +// +1 caster level vs SR (school Ench,Illu,Necro) +int ShadowWeavePen(int spell_id, int nSchool, object oCaster = OBJECT_SELF) +{ + int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + int nSP; + + // Apply changes if the caster has level in Shadow Adept class + // and this spell is eligible for the spell penetration check increase + if (iShadow > 0 && ShadowWeave(oCaster, spell_id, nSchool) == 1) + // Shadow Spell Power + nSP = iShadow / 3; + + return nSP; +} + +int KOTCSpellPenVsDemons(object oCaster, object oTarget) +{ + if(GetLevelByClass(CLASS_TYPE_KNIGHT_CHALICE, oCaster) >= 1) + { + if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_OUTSIDER) + { + if(GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL) + { + return 2; + } + } + } + return 0; +} + +int RunecasterRunePowerSP(object oCaster) +{ + int nSP = 0; + + // casting from a rune + if(GetResRef(GetSpellCastItem()) == "prc_rune_1") + { + nSP = StringToInt(GetTag(GetSpellCastItem())); + } + // caster is runechanting + else if(GetHasSpellEffect(SPELL_RUNE_CHANT)) + { + int nClass = GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if (nClass >= 30) nSP = 10; + else if (nClass >= 27) nSP = 9; + else if (nClass >= 24) nSP = 8; + else if (nClass >= 21) nSP = 7; + else if (nClass >= 18) nSP = 6; + else if (nClass >= 15) nSP = 5; + else if (nClass >= 12) nSP = 4; + else if (nClass >= 9) nSP = 3; + else if (nClass >= 5) nSP = 2; + else if (nClass >= 2) nSP = 1; + } + + return nSP; +} + +int MarshalDeterminedCaster(object oCaster) +{ + return GetLocalInt(oCaster,"Marshal_DetCast"); +} + +int DuskbladeSpellPower(object oCaster, object oTarget) +{ + int nSP = 0; + if(GetLocalInt(oTarget, "DuskbladeSpellPower")) + { + int nClass = GetLevelByClass(CLASS_TYPE_DUSKBLADE, oCaster); + + if(nClass >= 38) nSP = 10; + else if(nClass >= 36) nSP = 9; + else if(nClass >= 31) nSP = 8; + else if(nClass >= 26) nSP = 7; + else if(nClass >= 21) nSP = 6; + else if(nClass >= 18) nSP = 5; + else if(nClass >= 16) nSP = 4; + else if(nClass >= 11) nSP = 3; + else if(nClass >= 6) nSP = 2; + } + + return nSP; +} + +int DraconicMagicPower(object oCaster) +{ + return GetLocalInt(oCaster,"MagicPowerAura"); +} + +int TrueCastingSpell(object oCaster) +{ + if(GetHasSpellEffect(SPELL_TRUE_CASTING, oCaster)) + return 10; + + return 0; +} + +// Beguilers of level 8+ gain +2 bonus to SR agianst enemis that are denided DEX bonus to AC +int CloakedCastingSR(object oCaster, object oTarget) +{ + if(GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster) >= 8) + { + if(GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE)) + return 2; + } + + return 0; +} + +int PenetratingBlast(object oCaster, object oTarget) +{ + if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET")) + { + if(GetLocalInt(oCaster, "BlastEssence") == INVOKE_PENETRATING_BLAST) + return 4; + } + return 0; +} + +int add_spl_pen(object oCaster = OBJECT_SELF) +{ + object oTarget = PRCGetSpellTargetObject(); + int spell_id = PRCGetSpellId(); + int nSchool = GetSpellSchool(spell_id); + + int nSP = ElementalSavantSP(spell_id, oCaster); + nSP += GetHeartWarderPene(spell_id, oCaster); + nSP += RedWizardSP(spell_id, nSchool, oCaster); + nSP += GetSpellPowerBonus(oCaster); + nSP += GetSpellPenetreFocusSchool(nSchool, oCaster); + nSP += ShadowWeavePen(spell_id, nSchool, oCaster); + nSP += RunecasterRunePowerSP(oCaster); + nSP += MarshalDeterminedCaster(oCaster); + nSP += DraconicMagicPower(oCaster); + nSP += TrueCastingSpell(oCaster); + nSP += GetEssentiaInvestedFeat(oCaster, FEAT_SOULTOUCHED_SPELLCASTING); + if(GetIsObjectValid(oTarget)) + { + nSP += CloakedCastingSR(oCaster, oTarget); + nSP += PenetratingBlast(oCaster, oTarget); + nSP += KOTCSpellPenVsDemons(oCaster, oTarget); + nSP += DuskbladeSpellPower(oCaster, oTarget); + } + + return nSP; +} + +// +// This function converts elemental types as needed +// +string ChangedElementalType(int spell_id, object oCaster = OBJECT_SELF) +{ + // Lookup the spell type + string spellType = Get2DACache("spells", "ImmunityType", spell_id);//lookup_spell_type(spell_id); + + // Check if an override is set + string sType = GetLocalString(oCaster, "archmage_mastery_elements_name"); + + // If so, check if the spell qualifies for a change + if (sType == "" || !IsSpellTypeElemental(spellType)) + sType = spellType; + + return sType; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +// +// Get the Spell Penetration Bonuses +// +int SPGetPenetr(object oCaster = OBJECT_SELF) +{ + int nPenetr = 0; + + // This is a deliberate optimization attempt. + // The first feat determines if the others even need + // to be referenced. + if(GetHasFeat(FEAT_SPELL_PENETRATION, oCaster)) + { + nPenetr += 2; + if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oCaster)) + nPenetr += 4; + else if (GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oCaster)) + nPenetr += 2; + } + + // Check for additional improvements + nPenetr += add_spl_pen(oCaster); + + return nPenetr; +} + +// +// Interface for specific AOE requirements +// TODO: Determine who or what removes the cached local var (bug?) +// TODO: Try and remove this function completely? It does 2 things the +// above function doesnt: Effective Caster Level and Cache +// +int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0) +{ + // Check the cache + int nPenetr = GetLocalInt(OBJECT_SELF, "nPenetre"); + + // Compute the result + if (!nPenetr) { + nPenetr = (nCasterLvl) ? nCasterLvl : PRCGetCasterLevel(oCaster); + + // Factor in Penetration Bonuses + nPenetr += SPGetPenetr(oCaster); + + // Who removed this? + SetLocalInt(OBJECT_SELF,"nPenetre",nPenetr); + } + + return nPenetr; +} + +// Test main +//void main(){} diff --git a/src/include/prc_allow_const.nss b/src/include/prc_allow_const.nss new file mode 100644 index 0000000..b2b2b6d --- /dev/null +++ b/src/include/prc_allow_const.nss @@ -0,0 +1,296 @@ +// Base Classes +// BW +const string ALLOW_CLASS_BARBARIAN = "PRC_AllowBarbarian"; +const string ALLOW_CLASS_BARD = "PRC_AllowBard"; +const string ALLOW_CLASS_CLERIC = "PRC_AllowCleric"; +const string ALLOW_CLASS_DRUID = "PRC_AllowDruid"; +const string ALLOW_CLASS_FIGHTER = "PRC_AllowFighter"; +const string ALLOW_CLASS_MONK = "PRC_AllowMonk"; +const string ALLOW_CLASS_PALADIN = "PRC_AllowPaladin"; +const string ALLOW_CLASS_RANGER = "PRC_AllowRanger"; +const string ALLOW_CLASS_ROGUE = "PRC_AllowRogue"; +const string ALLOW_CLASS_SORCERER = "PRC_AllowSorcerer"; +const string ALLOW_CLASS_WIZARD = "PRC_AllowWizard"; + +//: Racial Classes +const string ALLOW_CLASS_ABERRATION = "PRC_AllowAberration"; +const string ALLOW_CLASS_ANIMAL = "PRC_AllowAnimal"; +const string ALLOW_CLASS_BEAST = "PRC_AllowBeast"; +const string ALLOW_CLASS_CONSTRUCT = "PRC_AllowConstruct"; +const string ALLOW_CLASS_DRAGON = "PRC_AllowDragon"; +const string ALLOW_CLASS_ELEMENTAL = "PRC_AllowEle"; +const string ALLOW_CLASS_FEY = "PRC_AllowFey"; +const string ALLOW_CLASS_GIANT = "PRC_AllowGiant"; +const string ALLOW_CLASS_HUMANOID = "PRC_AllowHumanoid"; +const string ALLOW_CLASS_MAGICAL_BEAST = "PRC_AllowMagicalBeast"; +const string ALLOW_CLASS_MON_HUMANOID = "PRC_AllowMonstrous"; +const string ALLOW_CLASS_OOZE = "PRC_AllowOoze"; +const string ALLOW_CLASS_OUTSIDER = "PRC_AllowOutsider"; +const string ALLOW_CLASS_PLANT = "PRC_AllowPlant"; +const string ALLOW_CLASS_SHAPECHANGER = "PRC_AllowShapechanger"; +const string ALLOW_CLASS_UNDEAD = "PRC_AllowUndead"; +const string ALLOW_CLASS_VERMIN = "PRC_AllowVermin"; + +// PRC +const string ALLOW_CLASS_ANTIPALADIN = "PRC_AllowAntiPal"; +const string ALLOW_CLASS_ARTIFICER = "PRC_AllowArtificer"; +const string ALLOW_CLASS_BINDER = "PRC_AllowBinder"; +const string ALLOW_CLASS_CRUSADER = "PRC_AllowCrusader"; +const string ALLOW_CLASS_DRAGONFIRE_ADEPT = "PRC_AllowDFAdept"; +const string ALLOW_CLASS_DRAGON_SHAMAN = "PRC_AllowDragSham"; +const string ALLOW_CLASS_DREAD_NECROMANCER = "PRC_AllowDNecro"; +const string ALLOW_CLASS_DUSKBLADE = "PRC_AllowDuskblade"; +const string ALLOW_CLASS_FACTOTUM = "PRC_AllowFactotum"; +const string ALLOW_CLASS_FAVOURED_SOUL = "PRC_AllowFavouredSoul"; +const string ALLOW_CLASS_HEALER = "PRC_AllowHealer"; +const string ALLOW_CLASS_HEXBLADE = "PRC_AllowHexblade"; +const string ALLOW_CLASS_INCARNATE = "PRC_AllowIncarnate"; +const string ALLOW_CLASS_KNIGHT = "PRC_AllowKnight"; +const string ALLOW_CLASS_MARSHAL = "PRC_AllowMarshal"; +const string ALLOW_CLASS_NINJA = "PRC_AllowNinjaCA"; +const string ALLOW_CLASS_PSION = "PRC_AllowPsion"; +const string ALLOW_CLASS_PSYCHIC_ROGUE = "PRC_AllowPsyRog"; +const string ALLOW_CLASS_PSYCHIC_WARRIOR = "PRC_AllowPsyWar"; +const string ALLOW_CLASS_SAMURAI = "PRC_AllowSamurai"; +const string ALLOW_CLASS_SAMURAI_CW = "PRC_AllowCWSam"; +const string ALLOW_CLASS_SCOUT = "PRC_AllowScout"; +const string ALLOW_CLASS_SHADOWCASTER = "PRC_AllowShadowcaster"; +const string ALLOW_CLASS_SHAMAN = "PRC_AllowShaman"; +const string ALLOW_CLASS_SOHEI = "PRC_AllowSohei"; +const string ALLOW_CLASS_SOULBORN = "PRC_AllowSoulborn"; +const string ALLOW_CLASS_SOULKNIFE = "PRC_AllowSoulKnife"; +const string ALLOW_CLASS_SWASHBUCKLER = "PRC_AllowSwash"; +const string ALLOW_CLASS_SWORDSAGE = "PRC_AllowSwordsage"; +const string ALLOW_CLASS_TOTEMIST = "PRC_AllowTotemist"; +const string ALLOW_CLASS_TRUENAMER = "PRC_AllowTruenamer"; +const string ALLOW_CLASS_WARBLADE = "PRC_AllowWarblade"; +const string ALLOW_CLASS_WARLOCK = "PRC_AllowWarlock"; +const string ALLOW_CLASS_WARMAGE = "PRC_AllowWarmage"; +const string ALLOW_CLASS_WILDER = "PRC_AllowWilder"; + + +// Prestige Classes +// BW +const string ALLOW_SHADOWDANCER = "X1_AllowShadow"; +const string ALLOW_HARPER = "X1_AllowHarper"; +const string ALLOW_CLASS_ARCANE_ARCHER = "X1_AllowArcher"; +const string ALLOW_ASSASSIN = "X1_AllowAsasin"; +const string ALLOW_BLACKGUARD = "X1_AllowBlkGrd"; +const string ALLOW_DIVINE_CHAMPION = "X2_AllowDivcha"; +const string ALLOW_WEAPON_MASTER = "X2_AllowWM"; +const string ALLOW_PALE_MASTER = "X2_AllowPalema"; +const string ALLOW_SHIFTER = "X2_AllowShiftr"; +const string ALLOW_DWARVEN_DEFENDER = "X1_AllowDwDef"; +const string ALLOW_DRAGON_DISCIPLE = "X1_AllowDrDis"; + +// PRC +const string ALLOW_CLASS_ABJURANT_CHAMPION = "PRC_AllowAbjCha"; +const string ALLOW_CLASS_ACOLYTE = "PRC_AllowAcolyte"; +const string ALLOW_CLASS_ACOLYTE_EGO = "PRC_AllowEgo"; +const string ALLOW_CLASS_ALAGHAR = "PRC_AllowAlag"; +const string ALLOW_CLASS_ALIENIST = "PRC_AllowAlien"; +const string ALLOW_CLASS_ARCANE_DUELIST = "PRC_AllowArcDuel"; +const string ALLOW_CLASS_ARCHMAGE = "PRC_AllowArchmage"; +const string ALLOW_CLASS_ARCTRICK = "PRC_AllowArcTrick"; +const string ALLOW_CLASS_BAELNORN = "PRC_AllowBaeln"; +const string ALLOW_CLASS_BATTLERAGER = "PRC_AllowBRage"; +const string ALLOW_CLASS_BATTLESMITH = "PRC_AllowBattlesmith"; +const string ALLOW_CLASS_BEREFT = "PRC_AllowBereft"; +const string ALLOW_CLASS_BFZ = "PRC_AllowBFZ"; +const string ALLOW_CLASS_BLACK_BLOOD_CULTIST = "PRC_AllowBBC"; +const string ALLOW_CLASS_BLADESINGER = "PRC_AllowBlades"; +const string ALLOW_CLASS_BLIGHTLORD = "PRC_AllowBlightlord"; +const string ALLOW_CLASS_BLIGHTER = "PRC_AllowBlighter"; +const string ALLOW_CLASS_BLOOD_MAGUS = "PRC_AllowBLMagus"; +const string ALLOW_CLASS_BLOODCLAW_MASTER = "PRC_AllowBloodclaw"; +const string ALLOW_CLASS_BONDED_SUMMONNER = "PRC_AllowBonded"; +const string ALLOW_CLASS_BRIMSTONE_SPEAKER = "PRC_AllowBrimstone"; +const string ALLOW_CLASS_CELEBRANT_SHARESS = "PRC_AllowCelebrant"; +const string ALLOW_CLASS_CEREBREMANCER = "PRC_AllowCereb"; +const string ALLOW_CLASS_CHAMPION_BANE = "PRC_AllowChaBane"; +const string ALLOW_CLASS_CHAMPION_CORELLON = "PRC_AllowCoC"; +const string ALLOW_CLASS_CHILD_OF_NIGHT = "PRC_AllowChildNight"; +const string ALLOW_CLASS_COMBAT_MEDIC = "PRC_AllowCbtMed"; +const string ALLOW_CLASS_CONTEMPLATIVE = "PRC_AllowContemp"; +const string ALLOW_CLASS_CRINTI = "PRC_AllowCrinti"; +const string ALLOW_CLASS_CULTIST_SHATTERED_PEAK = "PRC_AllowCultist"; +const string ALLOW_CLASS_DEEPSTONE_SENTINEL = "PRC_AllowDeepSt"; +const string ALLOW_CLASS_DIABOLIST = "PRC_AllowDiablo"; +const string ALLOW_CLASS_DIAMOND_DRAGON = "PRC_AllowDiaDra"; +const string ALLOW_CLASS_DIRGESINGER = "PRC_AllowDirge"; +const string ALLOW_CLASS_DISCIPLE_OF_MEPH = "PRC_AllowDiscmep"; +const string ALLOW_CLASS_DISC_ASMODEUS = "PRC_AllowAsmodeus"; +const string ALLOW_CLASS_DISC_BAALZEBUL = "PRC_AllowBaal"; +const string ALLOW_CLASS_DISPATER = "PRC_AllowDisp"; +const string ALLOW_CLASS_DRAGON_DEVOTEE = "PRC_AllowDragonDevotee"; +const string ALLOW_CLASS_DRAGONHEART_MAGE = "PRC_AllowDragonheart"; +const string ALLOW_CLASS_DRAGONSONG_LYRIST = "PRC_AllowDraSong"; +const string ALLOW_CLASS_DROW_JUDICATOR = "PRC_AllowDrowJud"; +const string ALLOW_CLASS_DRUNKEN_MASTER = "PRC_AllowDrnkn"; +const string ALLOW_CLASS_DUELIST = "PRC_AllowDuel"; +const string ALLOW_CLASS_ELDRITCH_DISCIPLE = "PRC_AllowEDisc"; +const string ALLOW_CLASS_ELDRITCH_KNIGHT = "PRC_AllowEldknight"; +const string ALLOW_CLASS_ELDRITCH_THEURGE = "PRC_AllowETheurg"; +const string ALLOW_CLASS_ENLIGHTENED_FIST = "PRC_AllowEnlFist"; +const string ALLOW_CLASS_ELEMENTAL_SAVANT = "PRC_AllowElSav"; +const string ALLOW_CLASS_ETERNAL_BLADE = "PRC_AllowETBL"; +const string ALLOW_CLASS_EYE_OF_GRUUMSH = "PRC_AllowEoG"; +const string ALLOW_CLASS_FISTRAZIEL = "PRC_AllowFistRaz"; +const string ALLOW_CLASS_FIST_OF_DAL_QUOR = "PRC_AllowDalQuor"; +const string ALLOW_CLASS_FIST_OF_ZUOKEN = "PRC_AllowFoZ"; +const string ALLOW_CLASS_FMM = "PRC_AllowFMM"; +const string ALLOW_CLASS_FOCHULAN_LYRIST = "PRC_AllowFocLyr"; +const string ALLOW_CLASS_FOE_HUNTER = "PRC_AllowFH"; +const string ALLOW_CLASS_FORESTMASTER = "PRC_AllowForMast"; +const string ALLOW_CLASS_FORSAKER = "PRC_AllowForsaker"; +const string ALLOW_CLASS_FRE_BERSERKER = "PRC_AllowFrebzk"; +const string ALLOW_CLASS_FROST_MAGE = "PRC_AllowFrostMage"; +const string ALLOW_CLASS_FROST_RAGER = "PRC_AllowFrostRager"; +const string ALLOW_CLASS_GHOST_FACED_KILLER = "PRC_AllowGhostFacedKiller"; +const string ALLOW_CLASS_HAND_WINGED_MASTERS = "PRC_AllowHotWM"; +const string ALLOW_CLASS_HARPERMAGE = "PRC_AllowHarperM"; +const string ALLOW_CLASS_HATHRAN = "PRC_AllowHath"; +const string ALLOW_CLASS_HAVOC_MAGE = "PRC_AllowHavocM"; +const string ALLOW_CLASS_HEARTWARDER = "PRC_AllowHeartW"; +const string ALLOW_CLASS_HELLFIRE_WARLOCK = "PRC_AllowHFWar"; +const string ALLOW_CLASS_HENSHIN_MYSTIC = "PRC_AllowHnshn"; +const string ALLOW_CLASS_HEXTOR = "PRC_AllowHextor"; +const string ALLOW_CLASS_HIEROPHANT = "PRC_AllowHiero"; +const string ALLOW_CLASS_HOSPITALER = "PRC_AllowHosp"; +const string ALLOW_CLASS_IAIJUTSU_MASTER = "PRC_AllowIaij"; +const string ALLOW_CLASS_INCANDESCENT_CHAMPION = "PRC_AllowIncandescent"; +const string ALLOW_CLASS_INCARNUM_BLADE = "PRC_AllowIBlade"; +const string ALLOW_CLASS_INITIATE_DRACONIC = "PRC_AllowIniDra"; +const string ALLOW_CLASS_IRON_MIND = "PRC_AllowIronMind"; +const string ALLOW_CLASS_IRONSOUL_FORGEMASTER = "PRC_AllowIronsoul"; +const string ALLOW_CLASS_JADE_PHOENIX_MAGE = "PRC_AllowJPM"; +const string ALLOW_CLASS_JUSTICE_WEALD_AND_WOE = "PRC_AllowJustWW"; +const string ALLOW_CLASS_KNIGHT_CHALICE = "PRC_AllowKnghtCh"; +const string ALLOW_CLASS_KNIGHT_MIDDLECIRCLE = "PRC_AllowKotMC"; +const string ALLOW_CLASS_KNIGHT_WEAVE = "PRC_AllowKnightWeave"; +const string ALLOW_CLASS_LASHER = "PRC_AllowLash"; +const string ALLOW_CLASS_LEGENDARY_DREADNOUGHT = "PRC_AllowLgDr"; +const string ALLOW_CLASS_LICH = "PRC_AllowLich"; +const string ALLOW_CLASS_MAESTER = "PRC_AllowMaester"; +const string ALLOW_CLASS_MAGEKILLER = "PRC_AllowMageK"; +const string ALLOW_CLASS_MANATARMS = "PRC_AllowManAt"; +const string ALLOW_CLASS_MASTER_ALCHEMIST = "PRC_AllowMstAlc"; +const string ALLOW_CLASS_MASTER_HARPER = "PRC_AllowMaster"; +const string ALLOW_CLASS_MASTER_OF_NINE = "PRC_AllowMoNine"; +const string ALLOW_CLASS_MASTER_OF_SHADOW = "PRC_AllowMasterShadow"; +const string ALLOW_CLASS_MASTER_OF_SHROUDS = "PRC_AllowShrouds"; +const string ALLOW_CLASS_MIGHTY_CONTENDER_KORD = "PRC_AllowKord"; +const string ALLOW_CLASS_MINSTREL = "PRC_AllowMinstrel"; +const string ALLOW_CLASS_MORNINGLORD_LATHANDER = "PRC_AllowMorninglord"; +const string ALLOW_CLASS_MYSTIC_THEURGE = "PRC_AllowMysticTheurge"; +const string ALLOW_CLASS_NECROCARNUM_ACOLYTE = "PRC_AllowNecrocarnum"; +const string ALLOW_CLASS_NENTYAR_HUNTER = "PRC_AllowNentyar"; +const string ALLOW_CLASS_NIGHTSHADE = "PRC_AllowNights"; +const string ALLOW_CLASS_NINJA_SPY = "PRC_AllowNinja"; +const string ALLOW_CLASS_NOCTUMANCER = "PRC_AllowNoctumancer"; +const string ALLOW_CLASS_OCULAR = "PRC_AllowOccAd"; +const string ALLOW_CLASS_OLLAM = "PRC_AllowOllam"; +const string ALLOW_CLASS_OOZEMASTER = "PRC_AllowUzi"; +const string ALLOW_CLASS_ORCUS = "PRC_AllowOrcus"; +const string ALLOW_CLASS_ORC_WARLORD = "PRC_AllowOrcWar"; +const string ALLOW_CLASS_ORDER_BOW_INNATE = "PRC_AllowOOTBI"; +const string ALLOW_CLASS_PEERLESS = "PRC_AllowPArcher"; +const string ALLOW_CLASS_PNP_SHIFTER = "PRC_AllowPNPSfr"; +const string ALLOW_CLASS_PSYCHIC_THEURGE = "PRC_AllowPsychic"; +const string ALLOW_CLASS_PURPLE_DRAGON_KNIGHT = "PRC_AllowPDK"; +const string ALLOW_CLASS_PYROKINETICIST = "PRC_AllowPyro"; +const string ALLOW_CLASS_RAGE_MAGE = "PRC_AllowRageMage"; +const string ALLOW_CLASS_RAVAGER = "PRC_Allow_Rava"; +const string ALLOW_CLASS_REAPING_MAULER = "PRC_AllowReapMauler"; +const string ALLOW_CLASS_RED_AVENGER = "PRC_AllowRedavng"; +const string ALLOW_CLASS_RED_WIZARD = "PRC_AllowRedWiz"; +const string ALLOW_CLASS_RUBY_KNIGHT_VINDICATOR = "PRC_AllowRubyKnight"; +const string ALLOW_CLASS_RUNECASTER = "PRC_AllowRunecaster"; +const string ALLOW_CLASS_RUNESCARRED = "PRC_Allow_Rune"; +const string ALLOW_CLASS_SACREDFIST = "PRC_AllowSacFist"; +const string ALLOW_CLASS_SACREDPURIFIER = "PRC_AllowPurifier"; +const string ALLOW_CLASS_SANCTIFIED_MIND = "PRC_AllowSancMind"; +const string ALLOW_CLASS_SAPPHIE_HIERARCH = "PRC_AllowSapphire"; +const string ALLOW_CLASS_SCION_DANTALION = "PRC_AllowScion"; +const string ALLOW_CLASS_SERENE_GUARDIAN = "PRC_AllowSerGuard"; +const string ALLOW_CLASS_SHADOWBLADE = "PRC_AllowShdBld"; +const string ALLOW_CLASS_SHADOWMIND = "PRC_AllowShadowmind"; +const string ALLOW_CLASS_SHADOWLORD = "PRC_AllowShaLow"; +const string ALLOW_CLASS_SHADOW_ADEPT = "PRC_AllowShadAde"; +const string ALLOW_CLASS_SHADOW_SUN_NINJA = "PRC_AllowSSN"; +const string ALLOW_CLASS_SHADOWBANE_INQUISITOR = "PRC_AllowShadowInq"; +const string ALLOW_CLASS_SHADOWBANE_STALKER = "PRC_AllowShadowStalk"; +const string ALLOW_CLASS_SHADOWSMITH = "PRC_AllowShadowsmith"; +const string ALLOW_CLASS_SHADOW_THIEF = "PRC_AllowShadowThief"; +const string ALLOW_CLASS_SHINING_BLADE = "PRC_AllowSBHeir"; +const string ALLOW_CLASS_SHOU = "PRC_AllowShou"; +const string ALLOW_CLASS_SKULLCLAN_HUNTER = "PRC_AllowSkullClan"; +const string ALLOW_CLASS_SLAYER_OF_DOMIEL = "PRC_AllowDomiel"; +const string ALLOW_CLASS_SOLDIER_OF_LIGHT = "PRC_AllowSoLight"; +const string ALLOW_CLASS_SOULCASTER = "PRC_AllowSoulcaster"; +const string ALLOW_CLASS_SOUL_EATER = "PRC_AllowSoulEater"; +const string ALLOW_CLASS_SPELLDANCER = "PRC_AllowSpelldancer"; +const string ALLOW_CLASS_SPELLFIRE_CHANNELER = "PRC_Allow_Spellf"; +const string ALLOW_CLASS_SPELLSWORD = "PRC_AllowSpellS"; +const string ALLOW_CLASS_SPINEMELD_WARRIOR = "PRC_AllowSpinemeld"; +const string ALLOW_CLASS_STORMLORD = "PRC_AllowStormL"; +const string ALLOW_CLASS_SUEL_ARCHANAMACH = "PRC_AllowSuel"; +const string ALLOW_CLASS_SWIFT_WING = "PRC_AllowSwiftW"; +const string ALLOW_CLASS_TALON_OF_TIAMAT = "PRC_TalonOfTiamat"; +const string ALLOW_CLASS_TEMPEST = "PRC_AllowTempest"; +const string ALLOW_CLASS_TEMPUS = "PRC_AllowTempus"; +const string ALLOW_CLASS_TENEBROUS_APOSTATE = "PRC_AllowTenebrous"; +const string ALLOW_CLASS_THAYAN_KNIGHT = "PRC_AllowThayKt"; +const string ALLOW_CLASS_THRALL_OF_GRAZZT = "PRC_AllowTOG"; +const string ALLOW_CLASS_THRALLHERD = "PRC_AllowThrallherd"; +const string ALLOW_CLASS_TOTEMRAGER = "PRC_AllowTotemRager"; +const string ALLOW_CLASS_TRUENECRO = "PRC_AllowTNecro"; +const string ALLOW_CLASS_UMBRAL_DISCIPLE = "PRC_AllowUmbral"; +const string ALLOW_CLASS_UNSEEN_SEER = "PRC_AllowUnseenSeer"; +const string ALLOW_CLASS_UR_PRIEST = "PRC_AllowUrPriest"; +const string ALLOW_CLASS_VASSAL = "PRC_AllowVassal"; +const string ALLOW_CLASS_VIGILANT = "PRC_AllowVigil"; +const string ALLOW_CLASS_VIRTUOSO = "PRC_AllowVirtuoso"; +const string ALLOW_CLASS_WARCHIEF = "PRC_AllowWarchief"; +const string ALLOW_CLASS_WARFORGED_JUGGERNAUT = "PRC_AllowJuggernaut"; +const string ALLOW_CLASS_WARMIND = "PRC_AllowWarmind"; +const string ALLOW_CLASS_WARPRIEST = "PRC_AllowWarPrst"; +const string ALLOW_CLASS_WARSLING_SNIPER = "PRC_AllowWarsling"; +const string ALLOW_CLASS_WAYFARER_GUIDE = "PRC_AllowWayfarer"; +const string ALLOW_CLASS_WAR_WIZARD_OF_CORMYR = "PRC_AllowWWoC"; +const string ALLOW_CLASS_WEREWOLF = "PRC_AllowWWolf"; +const string ALLOW_CLASS_WITCHBORN_BINDER = "PRC_AllowWitchborn"; +const string ALLOW_CLASS_WILD_MAGE = "PRC_AllowWildMage"; + + + +/*AL_SAVAGE = 205; +const string ALLOW_CLASS_WARLORD = 206; +const string ALLOW_CLASS_SCOUT = 207; +const string ALLOW_CLASS_DRAGON_SLAYER = 208; +const string ALLOW_CLASS_HOLY_CRUSADER = 209; +const string ALLOW_CLASS_GREY_WANDERER = 210; +const string ALLOW_CLASS_PROTECTOR = 211; +const string ALLOW_CLASS_SUMMONER = 212; +const string ALLOW_CLASS_ASTRAL_ADEPT = 213; +const string ALLOW_CLASS_DEMON_MASTER = 214; +const string ALLOW_CLASS_AURAMANCER = 215; +const string ALLOW_CLASS_ABJURER = 217; +const string ALLOW_CLASS_PUPPET_MASTER = 218 ; +const string ALLOW_CLASS_XENOWIZARD = 219; +const string ALLOW_CLASS_NATURALIST = 220; +const string ALLOW_CLASS_GEOMANCER = 221; + +const string ALLOW_CLASS_PROPHET = 223 ; +const string ALLOW_CLASS_SHAMAN = 224; +const string ALLOW_CLASS_WITCH_DOCTOR = 225; +const string ALLOW_CLASS_BATTLEMAGE = 226; +const string ALLOW_CLASS_MYSTICAL_KNIGHT = 227; +const string ALLOW_CLASS_ARCANE_THEOLOGIST = 22; +const string ALLOW_CLASS_CRYSTAL_MASTER = 229; +const string ALLOW_CLASS_ARCANE_ASSASSIN = 230; +const string ALLOW_CLASS_ARCANE_SWORDSMAN = 231; + +const string ALLOW_CLASS_VAMPIRE = 132; +*/ + diff --git a/src/include/prc_alterations.nss b/src/include/prc_alterations.nss new file mode 100644 index 0000000..9f31f26 --- /dev/null +++ b/src/include/prc_alterations.nss @@ -0,0 +1,158 @@ +//:://///////////////////////////////////////////// +//:: Include nexus +//:: prc_alterations +//:://///////////////////////////////////////////// +/* + This is the original include file for the PRC Spell Engine. + + Various spells, components and designs within this system have + been contributed by many individuals within and without the PRC. + + + These days, it serves to gather links to almost all the PRC + includes to one file. Should probably get sorted out someday, + since this slows compilation. On the other hand, it may be + necessary, since the custom compiler can't seem to handle + the most twisted include loops. + Related TODO to any C++ experts: Add #DEFINE support to nwnnsscomp + + Also, this file contains misceallenous functions that haven't + got a better home. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +//return a location that PCs will never be able to access +location PRC_GetLimbo(); + +//int GetSkill(object oObject, int nSkill, int bSynergy = FALSE, int bSize = FALSE, int bAbilityMod = TRUE, int bEffect = TRUE, int bArmor = TRUE, int bShield = TRUE, int bFeat = TRUE); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// const int ERROR_CODE_5_FIX_YET_ANOTHER_TIME = 1; + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +// Generic includes + +#include "inc_abil_damage" + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +//return a location that PCs will never be able to access +location PRC_GetLimbo() +{ + int i = 0; + location lLimbo; + + while (1) + { + object oLimbo = GetObjectByTag("Limbo", i++); + + if (oLimbo == OBJECT_INVALID) { + PrintString("PRC ERROR: no Limbo area! (did you import the latest PRC .ERF file?)"); + return lLimbo; + } + + if (GetName(oLimbo) == "Limbo" && GetArea(oLimbo) == OBJECT_INVALID) + { + vector vLimbo = Vector(0.0f, 0.0f, 0.0f); + lLimbo = Location(oLimbo, vLimbo, 0.0f); + } + } + return lLimbo; //never reached +} + +//Also serves as a store of all item creation feats +int GetItemCreationFeatCount() +{ + return(GetHasFeat(FEAT_CRAFT_WONDROUS) + + GetHasFeat(FEAT_CRAFT_STAFF) + + GetHasFeat(FEAT_CRAFT_ARMS_ARMOR) + + GetHasFeat(FEAT_FORGE_RING) + + GetHasFeat(FEAT_CRAFT_ROD) + + GetHasFeat(FEAT_CRAFT_CONSTRUCT) + + GetHasFeat(FEAT_SCRIBE_SCROLL) + + GetHasFeat(FEAT_BREW_POTION) + + GetHasFeat(FEAT_CRAFT_WAND) + + GetHasFeat(FEAT_ATTUNE_GEM) + + GetHasFeat(FEAT_CRAFT_SKULL_TALISMAN) + + GetHasFeat(FEAT_INSCRIBE_RUNE) + //+ GetHasFeat(?) + ); +} + +// Returns the IP_CONST_DAMAGESOAK_*_HP constant that does the given +// amount of damage reduction +int GetDamageSoakConstant(int nDamRed) +{ + switch(nDamRed) + { + case 1: return IP_CONST_DAMAGESOAK_1_HP; + case 2: return IP_CONST_DAMAGESOAK_2_HP; + case 3: return IP_CONST_DAMAGESOAK_3_HP; + case 4: return IP_CONST_DAMAGESOAK_4_HP; + case 5: return IP_CONST_DAMAGESOAK_5_HP; + case 6: return IP_CONST_DAMAGESOAK_6_HP; + case 7: return IP_CONST_DAMAGESOAK_7_HP; + case 8: return IP_CONST_DAMAGESOAK_8_HP; + case 9: return IP_CONST_DAMAGESOAK_9_HP; + case 10: return IP_CONST_DAMAGESOAK_10_HP; + case 11: return IP_CONST_DAMAGESOAK_11_HP; + case 12: return IP_CONST_DAMAGESOAK_12_HP; + case 13: return IP_CONST_DAMAGESOAK_13_HP; + case 14: return IP_CONST_DAMAGESOAK_14_HP; + case 15: return IP_CONST_DAMAGESOAK_15_HP; + case 16: return IP_CONST_DAMAGESOAK_16_HP; + case 17: return IP_CONST_DAMAGESOAK_17_HP; + case 18: return IP_CONST_DAMAGESOAK_18_HP; + case 19: return IP_CONST_DAMAGESOAK_19_HP; + case 20: return IP_CONST_DAMAGESOAK_20_HP; + case 21: return IP_CONST_DAMAGESOAK_21_HP; + case 22: return IP_CONST_DAMAGESOAK_22_HP; + case 23: return IP_CONST_DAMAGESOAK_23_HP; + case 24: return IP_CONST_DAMAGESOAK_24_HP; + case 25: return IP_CONST_DAMAGESOAK_25_HP; + case 26: return IP_CONST_DAMAGESOAK_26_HP; + case 27: return IP_CONST_DAMAGESOAK_27_HP; + case 28: return IP_CONST_DAMAGESOAK_28_HP; + case 29: return IP_CONST_DAMAGESOAK_29_HP; + case 30: return IP_CONST_DAMAGESOAK_30_HP; + case 31: return IP_CONST_DAMAGESOAK_31_HP; + case 32: return IP_CONST_DAMAGESOAK_32_HP; + case 33: return IP_CONST_DAMAGESOAK_33_HP; + case 34: return IP_CONST_DAMAGESOAK_34_HP; + case 35: return IP_CONST_DAMAGESOAK_35_HP; + case 36: return IP_CONST_DAMAGESOAK_36_HP; + case 37: return IP_CONST_DAMAGESOAK_37_HP; + case 38: return IP_CONST_DAMAGESOAK_38_HP; + case 39: return IP_CONST_DAMAGESOAK_39_HP; + case 40: return IP_CONST_DAMAGESOAK_40_HP; + case 41: return IP_CONST_DAMAGESOAK_41_HP; + case 42: return IP_CONST_DAMAGESOAK_42_HP; + case 43: return IP_CONST_DAMAGESOAK_43_HP; + case 44: return IP_CONST_DAMAGESOAK_44_HP; + case 45: return IP_CONST_DAMAGESOAK_45_HP; + case 46: return IP_CONST_DAMAGESOAK_46_HP; + case 47: return IP_CONST_DAMAGESOAK_47_HP; + case 48: return IP_CONST_DAMAGESOAK_48_HP; + case 49: return IP_CONST_DAMAGESOAK_49_HP; + case 50: return IP_CONST_DAMAGESOAK_50_HP; + + + + default: + WriteTimestampedLogEntry("Erroneous value for nDamRed in GetDamageReductionConstant: " + IntToString(nDamRed)); + } + + return -1; +} + +//:: void main(){} \ No newline at end of file diff --git a/src/include/prc_ccc_const.nss b/src/include/prc_ccc_const.nss new file mode 100644 index 0000000..31ff130 --- /dev/null +++ b/src/include/prc_ccc_const.nss @@ -0,0 +1,85 @@ +/** + * prc_ccc_const + * + * contains the constants + */ + +/** + * Constants for determining whether a PC goes through the convoCC + */ + +const int CONVOCC_ENTER_BOOT_PC = 0; +const int CONVOCC_ENTER_NEW_PC = 1; +const int CONVOCC_ENTER_RETURNING_PC = 2; + + +/** + * Constants for each stage in the convoCC + */ + +const int STAGE_INTRODUCTION = 0; +const int STAGE_GENDER = 1; +const int STAGE_GENDER_CHECK = 2; +const int STAGE_RACE = 3; +const int STAGE_RACE_CHECK = 4; +const int STAGE_CLASS = 5; +const int STAGE_CLASS_CHECK = 6; +const int STAGE_ALIGNMENT = 7; +const int STAGE_ALIGNMENT_CHECK = 8; +const int STAGE_ABILITY = 9; +const int STAGE_ABILITY_CHECK = 10; +const int STAGE_SKILL = 11; +const int STAGE_SKILL_CHECK = 12; +const int STAGE_FEAT = 13; +const int STAGE_FEAT_CHECK = 14; +const int STAGE_BONUS_FEAT = 15; +const int STAGE_BONUS_FEAT_CHECK = 16; +const int STAGE_WIZ_SCHOOL = 17; +const int STAGE_WIZ_SCHOOL_CHECK = 18; +const int STAGE_SPELLS_0 = 19; +const int STAGE_SPELLS_1 = 20; +const int STAGE_SPELLS_CHECK = 21; +const int STAGE_FAMILIAR = 22; +const int STAGE_FAMILIAR_CHECK = 23; +const int STAGE_DOMAIN = 24; +const int STAGE_DOMAIN_CHECK1 = 25; +const int STAGE_DOMAIN_CHECK2 = 26; +const int STAGE_APPEARANCE = 27; +const int STAGE_APPEARANCE_CHECK = 28; +const int STAGE_PORTRAIT = 29; +const int STAGE_PORTRAIT_CHECK = 30; +const int STAGE_SOUNDSET = 31; +const int STAGE_SOUNDSET_CHECK = 32; +const int STAGE_HEAD = 33; +const int STAGE_HEAD_CHECK = 34; +const int STAGE_TATTOO = 35; +const int STAGE_TATTOO_CHECK = 36; +const int STAGE_WINGS = 37; +const int STAGE_WINGS_CHECK = 38; +const int STAGE_TAIL = 39; +const int STAGE_TAIL_CHECK = 40; +const int STAGE_SKIN_COLOUR = 41; +const int STAGE_SKIN_COLOUR_CHOICE = 42; +const int STAGE_SKIN_COLOUR_CHECK = 43; +const int STAGE_HAIR_COLOUR = 44; +const int STAGE_HAIR_COLOUR_CHOICE = 45; +const int STAGE_HAIR_COLOUR_CHECK = 46; +const int STAGE_TATTOO1_COLOUR = 47; +const int STAGE_TATTOO1_COLOUR_CHOICE = 48; +const int STAGE_TATTOO1_COLOUR_CHECK = 49; +const int STAGE_TATTOO2_COLOUR = 50; +const int STAGE_TATTOO2_COLOUR_CHOICE = 51; +const int STAGE_TATTOO2_COLOUR_CHECK = 52; + +const int FINAL_STAGE = 99; + +/** + * constants used in the convoCC that aren't convo stages + */ + +// brownie model in the CEP +const int APPEARANCE_TYPE_CEP_BROWNIE = 1002; + +// wemic model in the CEP +const int APPEARANCE_TYPE_CEP_WEMIC = 1000; + diff --git a/src/include/prc_ccc_readme.nss b/src/include/prc_ccc_readme.nss new file mode 100644 index 0000000..bef8336 --- /dev/null +++ b/src/include/prc_ccc_readme.nss @@ -0,0 +1,192 @@ +/* +Conversation Character Creator v1.4 + +This is a conversation driven in-game 100% serverside character creator allowing +the use of custom content on a serverside game from level 1 (including new races +and new base classes ). It is also more secure than conventional character creation +methods + + + +You Need: + +PRC (current version is 2.2d, but this should work with any +http://nwnprc.netgamers.co.uk/ +or +http://nwvault.ign.com/Files/hakpacks/data/1071643329920.shtml +or +http://nwvault.ign.com/Files/hakpacks/data/1082946089000.shtml + +NWNX 2 +http://nwvault.ign.com/Files/other/data/1046636009723.shtml + +NWNX-Leto +http://prdownloads.sourceforge.net/leto/nwnxleto-build-03%2B18.rar?download + + + +This is an in-game conversation driven character creator. This is designed for +servervault so that custom content is used at character creation. To work +properly however, several things need to be done. + +NWNX2 must be instlled and used to run the module ( http://nwvault.ign.com/Files/ +other/...636009723.shtml ) See the readme included with NWNX2 for instructions, +its very easy. + +The NWNX_ODBC plugin included with NWNX2 should be installed and setup. See the +readme included with NWNX2 for instructions, this is quite straightforward but +trickier than installing NWNX2. In particular, make sure that the aps_onload +script is setup and that the database can be read to and from using the demo module +included in NWNX2. The ConvoCC should work with Access or MySQL databases. + +To enable the database system for 2da caching, you must set a local int on the +module named "USE_2DA_DB_CACHE" to 1. + +In addition to using NWNX2 for database storage, the cache can also be stored as +object in the bioware database. This cache is only updated when a character is +created, and is only restored when the module is loaded. This is to keep lag to +a minimum. To enable this, add RestoreCache(); to your OnModuleLoad script just +after void main() and add #include "prc_ccc_cache" above void main() + +NWNX-Leto plugin build 18 must be installed ( http://sourceforge.net/projects/leto/ ) +See the readme included with NWNX-Leto for instructions, its very easy. + +THIS IS A VERY IMPORTANT NOTE Inside the module, in inc_letoscript, the constant +NWN_DIR must point to your servers hard disk location where NWN is installed. +After this, the scripts must be recompiled. THIS IS A VERY IMPORTANT NOTE + +The player start should be placed in an area with no access to the rest of the +module. Then you should add code to jump the player to a starting waypoint in +the mod_cliententer script. There is an example commented out, if you delete +the comments ( // at the start of the line) then you can simply place a waypoint +tagged "WP_RealStart" where you want players to be jumped to. + +You must connect the mod_cliententer script to the modules on client enter event. +You must connect the mod_clientexit script to the modules on client exit event. + +Since much of the data is read directly from 2da's, this can be very slow, +especially for feats and spells where an enourmous amount of data is used. To +speed this process up, all 2da reads are cached via NWNX_ODBC to form a +persistant cache. This is a single tabled named "prccache". This means +that the first time a character is created it is much slower than subsequent +times will be. Also, whenever the PRC updates, or your 2da files change, you +should delete the table from the DB. In addition you must delete the bioware +database named "ConvoCC" when your 2das are updated. + +If you wish to expand it to include more custom content (for example CEP portraits), +you need to change the constants in inc_fileends to define the last lines of the +relevant 2das. You will also need to recompile all the scripts. + + +You may be able to use FastFrenchs NWNX development, I havn't tested it +particularly, but there is no reason why not. I have also only tested it with an +MS Access database, though it should work with MySQL too. + + +In addition, there is now a few optional extras. These are toggled by local int +variables set on the module. +PRC_CONVOCC_AVARIEL_WINGS Avariel characters have bird wings +PRC_CONVOCC_FEYRI_WINGS Fey'ri characters have bat wings +PRC_CONVOCC_FEYRI_TAIL Fey'ri characters have a demonic tail +PRC_CONVOCC_DROW_ENFORCE_GENDER Force Drow characters to be of the correct gender for + their race +PRC_CONVOCC_GENSAI_ENFORCE_DOMAINS Force Gensai clerics to select the relevant elemental + domain as one of their feats +PRC_CONVOCC_ENFORCE_BLOOD_OF_THE_WARLORD Makes Blood of the Warlord only avliable to orcish characters +PRC_CONVOCC_ENFORCE_FEAT_NIMBUSLIGHT Enforces the "moral" feats +PRC_CONVOCC_ENFORCE_FEAT_HOLYRADIANCE +PRC_CONVOCC_ENFORCE_FEAT_SERVHEAVEN +PRC_CONVOCC_ENFORCE_FEAT_SAC_VOW +PRC_CONVOCC_ENFORCE_FEAT_VOW_OBED +PRC_CONVOCC_ENFORCE_FEAT_THRALL_TO_DEMON +PRC_CONVOCC_ENFORCE_FEAT_DISCIPLE_OF_DARKNESS +PRC_CONVOCC_ENFORCE_FEAT_LICHLOVED +PRC_CONVOCC_ENFORCE_FEAT_EVIL_BRANDS +PRC_CONVOCC_ENFORCE_FEAT_VILE_WILL_DEFORM +PRC_CONVOCC_ENFORCE_FEAT_VILE_DEFORM_OBESE +PRC_CONVOCC_ENFORCE_FEAT_VILE_DEFORM_GAUNT +PRC_CONVOCC_RAKSHASHA_FEMALE_APPEARANCE Female rakshasha use the female rakshasha model +PRC_CONVOCC_DRIDER_FEMALE_APPEARANCE Female drider use the female drider model +PRC_CONVOCC_DISALLOW_CUSTOMISE_WINGS Stops players changing their wings +PRC_CONVOCC_DISALLOW_CUSTOMISE_TAIL Stops players changing their tail +PRC_CONVOCC_DISALLOW_CUSTOMISE_MODEL Stops players changing their model at all +PRC_CONVOCC_USE_RACIAL_APPEARANCES Players can only change their model / portrait / soundset +PRC_CONVOCC_USE_RACIAL_PORTRAIT to alternatives of the same race. If you have extra +PRC_CONVOCC_USE_RACIAL_SOUNDSET content (e.g. from CEP) you must add them to + SetupRacialAppearances or SetupRacialPortraits or + SetupRacialSoundsets in prc_ccc_inc_e in order for + them to be shown on the list. +PRC_CONVOCC_ONLY_PLAYER_VOICESETS Players can only select from the player voicesets + NPC voicesets are not complete, so wont play sounds + for many things such as emotes. +PRC_CONVOCC_RESTRICT_VOICESETS_BY_SEX Only allows players to select voiceset of the same gender +PRC_CONVOCC_FORCE_KEEP_VOICESET Skips the select a voiceset step entirely, and players + have to keep their current voiceset +PRC_CONVOCC_ALLOW_TO_KEEP_PORTRAIT Allow players to keep their exisiting portrait + The ConvoCC cannot allow players to select custom + portriats, so the only way for players to have them + is to select them in the bioware character creator + and then select to keep them in the ConvoCC. +PRC_CONVOCC_FORCE_KEEP_PORTRAIT Skips the select a portrait step entirely, and players + have to keep their current portrait +PRC_CONVOCC_RESTRICT_PORTRAIT_BY_SEX Only allow players to select portraits of the same gender. + Most of the NPC portraits do not have a gender so are also + removed. +PRC_CONVOCC_ENABLE_RACIAL_HITDICE This option give players the ability to start with racial + hit dice for some of the more powerful races. These are + defined in ECL.2da For these races, players do not pick + a class in the ConvoCC but instead select 1 or more levels + in a racial class (such as monsterous humanoid, or outsider). + This is not a complete ECL system, it mearly gives players + the racial hit dice component. It does not make any measure + of the Level Adjustment component. For example, a pixie has + no racial hit dice, but has a +4 level adjustment. +PRC_CONVOCC_ALLOW_HIDDEN_SKIN_COLOURS These enable players to select the hidden skin, hair, +PRC_CONVOCC_ALLOW_HIDDEN_HAIR_COLOURS and tattoo colours (metalics, matt black, matt white). +PRC_CONVOCC_ALLOW_HIDDEN_TATTOO_COLOURS +PRC_CONVOCC_ALLOW_SKILL_POINT_ROLLOVER This option allows players to keep their skillpoints + from one level to the next, if they want to. +PRC_CONVOCC_USE_XP_FOR_NEW_CHAR This will identify new characters based on X which is + the same as v1.3 but less secure. +PRC_CONVOCC_ENCRYPTION_KEY This is the key used to encrypt characters names if + USE_XP_FOR_NEW_CHAR is false in order to identify + returning characters. It should be in the range 1-100. + If USE_XP_FOR_NEW_CHAR is true, then returning + characters will be encrypted too, so once everone has + logged on at least once, USE_XP_FOR_NEW_CHAR can be + set to false for greater security. + + +Change log +v1.4 + Changed several systems to the more general systems the PRC will use in 2.3 + Fixed alertness not being given as a racial/bonus feat + Fixed cross class skills not dissapering at 1 skill point remaining + Fixed clerical domain feats not being given correctly + Fixed bonus feats not being given correctly + Fixed quick to master feat selection not being reset correctly + Added model selection + Added wing selection + Added tail selection + Added skin colour selection + Added hair colour selection + Added preview for character customizations (model, soundset, colors, etc) + Added builder options for lots of things + Added skill point storage +v1.3 + Fixed a nasty bug 1.2 introduced where all characters were given elven radial feats and appearance +v1.2 + Fixed another bug with user and character names with unusual characters in + Fixed bugs with MySQL database + Added BiowareDB caching (at character creation and module load only) + Added confirmation stages + Added "keep existing" options to portrait and soundset +v1.1 + Fixed bug with usernames with spaces in them + Fixed bug with cleric second domain scrolling + Fixed bug with missing scripts in erf + Added portrait selection + Added soundset selection +v1.0 + First Release +*/ diff --git a/src/include/prc_class_const.nss b/src/include/prc_class_const.nss new file mode 100644 index 0000000..286b574 --- /dev/null +++ b/src/include/prc_class_const.nss @@ -0,0 +1,241 @@ +//:: Class constants + +const int CLASS_TYPE_PLANT = 164; + +const int CLASS_TYPE_PRC_EYE_OF_GRUUMSH = 39; +const int CLASS_TYPE_SHOU = 40; + +const int CLASS_TYPE_UR_PRIEST = 42; +const int CLASS_TYPE_BINDER = 43; +const int CLASS_TYPE_ANIMA_MAGE = 44; +const int CLASS_TYPE_KNIGHT_SACRED_SEAL = 45; +const int CLASS_TYPE_SCION_DANTALION = 46; +const int CLASS_TYPE_TENEBROUS_APOSTATE = 47; +const int CLASS_TYPE_REAPING_MAULER = 48; +const int CLASS_TYPE_SERENE_GUARDIAN = 49; +const int CLASS_TYPE_SACREDPURIFIER = 50; +const int CLASS_TYPE_OCULAR = 51; +const int CLASS_TYPE_BATTLERAGER = 52; +const int CLASS_TYPE_MYSTIC_THEURGE = 53; +const int CLASS_TYPE_NINJA_SPY = 54; +const int CLASS_TYPE_SAMURAI = 55; +const int CLASS_TYPE_WARPRIEST = 56; +const int CLASS_TYPE_SPELLFIRE = 57; +const int CLASS_TYPE_VIRTUOSO = 58; +const int CLASS_TYPE_MARSHAL = 59; +const int CLASS_TYPE_SWASHBUCKLER = 60; +const int CLASS_TYPE_HEXBLADE = 61; +const int CLASS_TYPE_DUSKBLADE = 62; +const int CLASS_TYPE_SCOUT = 63; +const int CLASS_TYPE_HEALER = 64; +const int CLASS_TYPE_MAGEKILLER = 65; +const int CLASS_TYPE_HARPERMAGE = 66; +const int CLASS_TYPE_SPELLSWORD = 67; +const int CLASS_TYPE_ACOLYTE = 68; +const int CLASS_TYPE_UNSEEN_SEER = 69; +const int CLASS_TYPE_ELDRITCH_KNIGHT = 70; +const int CLASS_TYPE_ELEMENTAL_SAVANT = 71; +const int CLASS_TYPE_FACTOTUM = 72; +const int CLASS_TYPE_CELEBRANT_SHARESS = 73; +const int CLASS_TYPE_CULTIST_SHATTERED_PEAK = 74; +const int CLASS_TYPE_FORSAKER = 75; +const int CLASS_TYPE_INCARNATE = 76; +const int CLASS_TYPE_SOULBORN = 77; +const int CLASS_TYPE_TOTEMIST = 78; +const int CLASS_TYPE_BEGUILER = 79; +const int CLASS_TYPE_DUELIST = 80; +const int CLASS_TYPE_HIEROPHANT = 81; +const int CLASS_TYPE_RED_AVENGER = 82; +const int CLASS_TYPE_KNIGHT_CHALICE = 83; +const int CLASS_TYPE_HATHRAN = 84; +const int CLASS_TYPE_IRONSOUL_FORGEMASTER = 85; +const int CLASS_TYPE_STORMLORD = 86; +const int CLASS_TYPE_HEARTWARDER = 87; +const int CLASS_TYPE_FISTRAZIEL = 88; +const int CLASS_TYPE_VASSAL = 89; +const int CLASS_TYPE_LICH = 90; +const int CLASS_TYPE_PNP_SHIFTER = 91; +const int CLASS_TYPE_COC = 92; +const int CLASS_TYPE_SUBLIME_CHORD = 93; +const int CLASS_TYPE_ARTIFICER = 94; +const int CLASS_TYPE_ARCANE_DUELIST = 95; +const int CLASS_TYPE_FMM = 96; +const int CLASS_TYPE_WILD_MAGE = 97; +const int CLASS_TYPE_SHADOWSMITH = 98; +const int CLASS_TYPE_ABJURANT_CHAMPION = 99; +const int CLASS_TYPE_ARCHMAGE = 100; +const int CLASS_TYPE_OOZEMASTER = 101; +const int CLASS_TYPE_PSYCHIC_ROGUE = 102; +const int CLASS_TYPE_SPELLDANCER = 103; +const int CLASS_TYPE_KNIGHT_WEAVE = 104; +const int CLASS_TYPE_JUDICATOR = 105; +const int CLASS_TYPE_SHADOWBANE_INQUISITOR = 106; +const int CLASS_TYPE_SHADOWBANE_STALKER = 107; +const int CLASS_TYPE_WAYFARER_GUIDE = 108; +const int CLASS_TYPE_UMBRAL_DISCIPLE = 109; +const int CLASS_TYPE_ALIENIST = 110; +const int CLASS_TYPE_BLACK_BLOOD_CULTIST = 111; +const int CLASS_TYPE_WARLOCK = 112; +const int CLASS_TYPE_FOCHLUCAN_LYRIST = 113; +const int CLASS_TYPE_DRAGONSONG_LYRIST = 114; +const int CLASS_TYPE_SPINEMELD_WARRIOR = 115; +const int CLASS_TYPE_NIGHTSHADE = 116; +const int CLASS_TYPE_SHADOW_ADEPT = 117; +const int CLASS_TYPE_SOLDIER_OF_LIGHT = 118; +const int CLASS_TYPE_SAPPHIRE_HIERARCH = 119; +const int CLASS_TYPE_SHADOWLORD = 120; +const int CLASS_TYPE_BONDED_SUMMONNER = 121; +const int CLASS_TYPE_INITIATE_DRACONIC = 122; +const int CLASS_TYPE_TEMPUS = 123; +const int CLASS_TYPE_BLADESINGER = 124; +const int CLASS_TYPE_SOULCASTER = 125; +const int CLASS_TYPE_SACREDFIST = 126; +const int CLASS_TYPE_LEGENDARY_DREADNOUGHT = 127; +const int CLASS_TYPE_DISC_BAALZEBUL = 128; +const int CLASS_TYPE_MIGHTY_CONTENDER_KORD = 129; +const int CLASS_TYPE_IAIJUTSU_MASTER = 130; +const int CLASS_TYPE_DISPATER = 131; +const int CLASS_TYPE_CW_SAMURAI = 132; +const int CLASS_TYPE_RAVAGER = 133; +const int CLASS_TYPE_RUNESCARRED = 134; +const int CLASS_TYPE_BLIGHTLORD = 135; +const int CLASS_TYPE_SHADOWCASTER = 136; +const int CLASS_TYPE_CHILD_OF_NIGHT = 137; +const int CLASS_TYPE_MASTER_OF_SHADOW = 138; +const int CLASS_TYPE_NOCTUMANCER = 139; +const int CLASS_TYPE_TOTEM_RAGER = 140; +const int CLASS_TYPE_NINJA = 141; +const int CLASS_TYPE_SHADOWBLADE = 142; +const int CLASS_TYPE_DRAGON_SHAMAN = 143; +const int CLASS_TYPE_DRAGONFIRE_ADEPT = 144; +const int CLASS_TYPE_PSION = 145; +const int CLASS_TYPE_PSYWAR = 146; +const int CLASS_TYPE_SOULKNIFE = 147; +const int CLASS_TYPE_WILDER = 148; +const int CLASS_TYPE_THAYAN_KNIGHT = 149; +const int CLASS_TYPE_RED_WIZARD = 150; +const int CLASS_TYPE_TRUENECRO = 151; +const int CLASS_TYPE_ARCTRICK = 152; +const int CLASS_TYPE_BLOOD_MAGUS = 153; +const int CLASS_TYPE_DIABOLIST = 154; +const int CLASS_TYPE_HEXTOR = 155; +const int CLASS_TYPE_INCANDESCENT_CHAMPION = 156; +const int CLASS_TYPE_JUSTICEWW = 157; +const int CLASS_TYPE_ACOLYTE_EGO = 158; +const int CLASS_TYPE_PEERLESS = 159; +const int CLASS_TYPE_LASHER = 160; +const int CLASS_TYPE_ORDER_BOW_INITIATE = 161; +const int CLASS_TYPE_HELLFIRE_WARLOCK = 162; +const int CLASS_TYPE_ORCUS = 163; +//:: Plant = 164 +const int CLASS_TYPE_BFZ = 165; +const int CLASS_TYPE_SHINING_BLADE = 166; +const int CLASS_TYPE_KNIGHT_MIDDLECIRCLE = 167; +const int CLASS_TYPE_MAESTER = 168; +const int CLASS_TYPE_COMBAT_MEDIC = 169; +const int CLASS_TYPE_OLLAM = 170; +const int CLASS_TYPE_HALFLING_WARSLINGER = 171; +const int CLASS_TYPE_SPIRIT_SHAMAN = 172; +const int CLASS_TYPE_WEREWOLF = 173; +const int CLASS_TYPE_HOSPITALER = 174; +const int CLASS_TYPE_MASTER_OF_SHROUDS = 175; +const int CLASS_TYPE_MASTER_HARPER = 176; +const int CLASS_TYPE_FRE_BERSERKER = 177; +const int CLASS_TYPE_TEMPEST = 178; +const int CLASS_TYPE_FOE_HUNTER = 179; +//:: Free = 180 +const int CLASS_TYPE_ORC_WARLORD = 181; +const int CLASS_TYPE_THRALL_OF_GRAZZT_A = 182; +const int CLASS_TYPE_NECROCARNATE = 183; +const int CLASS_TYPE_ELDRITCH_DISCIPLE = 184; +const int CLASS_TYPE_ELDRITCH_THEURGE = 185; +const int CLASS_TYPE_GHOST_FACED_KILLER = 186; +const int CLASS_TYPE_DREAD_NECROMANCER = 187; +const int CLASS_TYPE_ULTIMATE_MAGUS = 188; +const int CLASS_TYPE_FORESTMASTER = 189; +const int CLASS_TYPE_ARCHIVIST = 190; +const int CLASS_TYPE_DEEPSTONE_SENTINEL = 191; +const int CLASS_TYPE_JADE_PHOENIX_MAGE = 192; +const int CLASS_TYPE_BLOODCLAW_MASTER = 193; +const int CLASS_TYPE_RUBY_VINDICATOR = 194; +const int CLASS_TYPE_MASTER_OF_NINE = 195; +const int CLASS_TYPE_ETERNAL_BLADE = 196; +const int CLASS_TYPE_SHADOW_SUN_NINJA = 197; +const int CLASS_TYPE_WITCHBORN_BINDER = 198; +const int CLASS_TYPE_BAELNORN = 199; +const int CLASS_TYPE_DISCIPLE_OF_MEPH = 200; +const int CLASS_TYPE_SOUL_EATER = 201; +const int CLASS_TYPE_HENSHIN_MYSTIC = 202; +const int CLASS_TYPE_DRUNKEN_MASTER = 203; +const int CLASS_TYPE_ENLIGHTENEDFIST = 204; +const int CLASS_TYPE_MORNINGLORD = 205; +const int CLASS_TYPE_INCARNUM_BLADE = 206; +const int CLASS_TYPE_SHAMAN = 207; +const int CLASS_TYPE_PYROKINETICIST = 208; +const int CLASS_TYPE_SHADOWMIND = 209; +const int CLASS_TYPE_PSYCHIC_THEURGE = 210; +const int CLASS_TYPE_CEREBREMANCER = 211; +const int CLASS_TYPE_THRALLHERD = 212; +const int CLASS_TYPE_FIST_OF_ZUOKEN = 213; +const int CLASS_TYPE_HAVOC_MAGE = 214; +const int CLASS_TYPE_CONTEMPLATIVE = 215; +const int CLASS_TYPE_RUNECASTER = 216; +const int CLASS_TYPE_WARCHIEF = 217; +const int CLASS_TYPE_WARMIND = 218; +const int CLASS_TYPE_IRONMIND = 219; +const int CLASS_TYPE_SANCTIFIED_MIND = 220; +const int CLASS_TYPE_SLAYER_OF_DOMIEL = 221; +const int CLASS_TYPE_DISCIPLE_OF_ASMODEUS = 222; +const int CLASS_TYPE_DIRGESINGER = 223; +const int CLASS_TYPE_SUEL_ARCHANAMACH = 224; +const int CLASS_TYPE_FAVOURED_SOUL = 225; +const int CLASS_TYPE_WAR_WIZARD_OF_CORMYR = 226; +const int CLASS_TYPE_SKULLCLAN_HUNTER = 227; +const int CLASS_TYPE_TRUENAMER = 228; +const int CLASS_TYPE_MASTER_ALCHEMIST = 229; +const int CLASS_TYPE_BEREFT = 230; +const int CLASS_TYPE_BRIMSTONE_SPEAKER = 231; +const int CLASS_TYPE_SHUGENJA = 232; +const int CLASS_TYPE_SOHEI = 233; +const int CLASS_TYPE_CRUSADER = 234; +const int CLASS_TYPE_SWORDSAGE = 235; +const int CLASS_TYPE_WARBLADE = 236; +const int CLASS_TYPE_WARMAGE = 237; +const int CLASS_TYPE_KNIGHT = 238; +const int CLASS_TYPE_FIST_DAL_QUOR = 239; +const int CLASS_TYPE_HANDOTWM = 240; +const int CLASS_TYPE_TALON_OF_TIAMAT = 241; +const int CLASS_TYPE_DRAGON_DEVOTEE = 242; +const int CLASS_TYPE_FROST_MAGE = 243; +const int CLASS_TYPE_WARFORGED_JUGGERNAUT = 244; +const int CLASS_TYPE_BATTLESMITH = 245; +const int CLASS_TYPE_NENTYAR_HUNTER = 246; +const int CLASS_TYPE_BLIGHTER = 247; +const int CLASS_TYPE_RAGE_MAGE = 248; +const int CLASS_TYPE_DRAGONHEART_MAGE = 249; +const int CLASS_TYPE_SWIFT_WING = 250; +const int CLASS_TYPE_DIAMOND_DRAGON = 251; +const int CLASS_TYPE_FROSTRAGER = 252; +const int CLASS_TYPE_CRINTI_SHADOW_MARAUDER = 253; +const int CLASS_TYPE_SHADOW_THIEF_AMN = 254; + +const int CLASS_TYPE_ANTI_PALADIN = -1; +const int CLASS_TYPE_NIGHTSTALKER = -1; //Just here to make things compile until it gets stripped out +const int CLASS_TYPE_MINSTREL_EDGE = -1; +const int CLASS_TYPE_BRAWLER = -1; +const int CLASS_TYPE_THRALL_OF_GRAZZT_D = -1; +const int CLASS_TYPE_MASTER_HARPER_DIV = -1; +const int CLASS_TYPE_ULTIMATE_RANGER = -1; +const int CLASS_TYPE_MANATARMS = -1; +const int CLASS_TYPE_BOWMAN = -1; +const int CLASS_TYPE_VIGILANT = -1; +const int CLASS_TYPE_ARCANE_HIEROPHANT = -1; +const int CLASS_TYPE_ALAGHAR = -1; +const int CLASS_TYPE_BLARCHER = -1; +const int CLASS_TYPE_WITCH = -1; +const int CLASS_TYPE_TEMPLAR = -1; +const int CLASS_TYPE_MYSTIC = -1; +const int CLASS_TYPE_NOBLE = -1; + + +//void main (){} \ No newline at end of file diff --git a/src/include/prc_compan_inc.nss b/src/include/prc_compan_inc.nss new file mode 100644 index 0000000..bf96c34 --- /dev/null +++ b/src/include/prc_compan_inc.nss @@ -0,0 +1,171 @@ +const int PRC_COMP_APPEARANCE_TYPE_BEHOLDER_EYEBALL_150 = 482; +const int PRC_COMP_APPEARANCE_TYPE_BEHOLDER_125 = 483; +const int PRC_COMP_APPEARANCE_TYPE_BEHOLDER_MAGE_125 = 484; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BLACK_75 = 485; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BLACK_125 = 486; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BRASS_75 = 487; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BRASS_125 = 488; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_COPPER_75 = 489; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_COPPER_125 = 490; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_SILVER_75 = 491; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_SILVER_125 = 492; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BRONZE_75 = 493; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BRONZE_125 = 494; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_GOLD_75 = 495; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_GOLD_125 = 496; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BLUE_75 = 497; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_BLUE_125 = 498; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_GREEN_75 = 499; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_GREEN_125 = 500; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_RED_75 = 501; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_RED_125 = 502; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_WHITE_75 = 503; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_WHITE_125 = 504; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_SHADOW_75 = 505; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_SHADOW_125 = 506; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_PRISM_75 = 507; +const int PRC_COMP_APPEARANCE_TYPE_DRAGON_PRISM_125 = 508; +const int PRC_COMP_APPEARANCE_TYPE_BEETLE_SLICER_125 = 509; +const int PRC_COMP_APPEARANCE_TYPE_BEETLE_STAG_125 = 510; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_AIR_125 = 511; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_AIR_150 = 512; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER_125 = 513; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER_150 = 514; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_EARTH_125 = 515; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_EARTH_150 = 516; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER_125=517; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER_150=518; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_FIRE_125 = 519; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_FIRE_150 = 520; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER_125= 521; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER_150= 522; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_WATER_125 = 523; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_WATER_150 = 524; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER_125=525; +const int PRC_COMP_APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER_150=526; +const int PRC_COMP_APPEARANCE_TYPE_MUMMY_COMMON_125 = 527; +const int PRC_COMP_APPEARANCE_TYPE_SKELETON_COMMON_125 = 528; +const int PRC_COMP_APPEARANCE_TYPE_SKELETON_COMMON_150 = 529; +const int PRC_COMP_APPEARANCE_TYPE_SHIELD_GUARDIAN_125 = 530; +const int PRC_COMP_APPEARANCE_TYPE_GOLEM_CLAY_125 = 531; +const int PRC_COMP_APPEARANCE_TYPE_MOHRG_125 = 532; +const int PRC_COMP_APPEARANCE_TYPE_ZOMBIE_125 = 533; +const int PRC_COMP_APPEARANCE_TYPE_ZOMBIE_150 = 534; +const int PRC_COMP_APPEARANCE_TYPE_GARGOYLE_125 = 535; +const int PRC_COMP_APPEARANCE_TYPE_SKELETAL_DEVOURER_125 = 536; +const int PRC_COMP_APPEARANCE_TYPE_PENGUIN_150 = 537; +const int PRC_COMP_APPEARANCE_TYPE_PENGUIN_200 = 538; +const int PRC_COMP_APPEARANCE_TYPE_PENGUIN_300 = 539; +const int PRC_COMP_APPEARANCE_TYPE_PENGUIN_400 = 540; +const int PRC_COMP_APPEARANCE_TYPE_PENGUIN_500 = 541; +const int PRC_COMP_APPEARANCE_TYPE_BEETLE_STAG_50 = 542; +const int PRC_COMP_APPEARANCE_TYPE_BEETLE_STAG_25 = 543; +const int PRC_COMP_APPEARANCE_TYPE_BEETLE_STINK_50 = 544; +const int PRC_COMP_APPEARANCE_TYPE_BEETLE_STINK_25 = 545; +const int PRC_COMP_APPEARANCE_TYPE_BAT_50 = 546; +const int PRC_COMP_APPEARANCE_TYPE_BAT_60 = 547; +const int PRC_COMP_APPEARANCE_TYPE_BAT_70 = 548; +const int PRC_COMP_APPEARANCE_TYPE_BAT_80 = 549; +const int PRC_COMP_APPEARANCE_TYPE_BAT_90 = 550; +const int PRC_COMP_APPEARANCE_TYPE_BAT_125 = 551; +const int PRC_COMP_APPEARANCE_TYPE_CAT_COUGAR_40 = 552; +const int PRC_COMP_APPEARANCE_TYPE_CAT_COUGAR_50 = 553; +const int PRC_COMP_APPEARANCE_TYPE_CAT_COUGAR_75 = 554; +const int PRC_COMP_APPEARANCE_TYPE_CAT_LION_40 = 555; +const int PRC_COMP_APPEARANCE_TYPE_CAT_LION_50 = 556; +const int PRC_COMP_APPEARANCE_TYPE_CAT_LION_75 = 557; +const int PRC_COMP_APPEARANCE_TYPE_CAT_PANTHER_40 = 558; +const int PRC_COMP_APPEARANCE_TYPE_CAT_PANTHER_50 = 559; +const int PRC_COMP_APPEARANCE_TYPE_CAT_PANTHER_75 = 560; +const int PRC_COMP_APPEARANCE_TYPE_RAT_50 = 561; +const int PRC_COMP_APPEARANCE_TYPE_RAT_75 = 562; +const int PRC_COMP_APPEARANCE_TYPE_SKELETON_COMMON_50 = 563; +const int PRC_COMP_APPEARANCE_TYPE_SKELETON_COMMON_75 = 564; +const int PRC_COMP_APPEARANCE_TYPE_ZOMBIE_50 = 565; +const int PRC_COMP_APPEARANCE_TYPE_ZOMBIE_75 = 566; +const int PRC_COMP_APPEARANCE_TYPE_GOLEM_IRON_125 = 567; +const int PRC_COMP_APPEARANCE_TYPE_GOLEM_STONE_125 = 568; +const int PRC_COMP_APPEARANCE_TYPE_BAT_150 = 569; +const int PRC_COMP_APPEARANCE_TYPE_BAT_200 = 570; +const int PRC_COMP_APPEARANCE_TYPE_OGRE_DLA = 1535; +const int PRC_COMP_APPEARANCE_TYPE_MONODRONE = 1529; +const int PRC_COMP_APPEARANCE_TYPE_OSYLUTH_C = 1503; +const int PRC_COMP_APPEARANCE_TYPE_OSYLUTH_B = 1502; +const int PRC_COMP_APPEARANCE_TYPE_OSYLUTH_A = 1501; +const int PRC_COMP_APPEARANCE_TYPE_MAUG_COMMANDER = 1500; +const int PRC_COMP_APPEARANCE_TYPE_MAUG_LIEUTENANT = 1499; +const int PRC_COMP_APPEARANCE_TYPE_MAUG = 1498; +const int PRC_COMP_APPEARANCE_TYPE_TREANT = 1492; +const int PRC_COMP_APPEARANCE_TYPE_GLABREZU = 1418; +const int PRC_COMP_APPEARANCE_TYPE_HARLAT = 990; +const int PRC_COMP_APPEARANCE_TYPE_MASTERIUS = 991; +const int PRC_COMP_APPEARANCE_TYPE_BEHOLDER_GZORB = 993; +const int PRC_COMP_APPEARANCE_TYPE_HAGTHA = 994; +const int PRC_COMP_APPEARANCE_TYPE_WEREBOAR = 996; +const int PRC_COMP_APPEARANCE_TYPE_ANTOINE = 997; +const int PRC_COMP_APPEARANCE_TYPE_MAGGRIS = 998; +const int PRC_COMP_APPEARANCE_TYPE_HALASTER = 999; + +const int PRC_COMP_POLYMORPH_TYPE_PENGUIN_150 = 151; +const int PRC_COMP_POLYMORPH_TYPE_PENGUIN_200 = 152; +const int PRC_COMP_POLYMORPH_TYPE_PENGUIN_300 = 153; +const int PRC_COMP_POLYMORPH_TYPE_PENGUIN_400 = 154; +const int PRC_COMP_POLYMORPH_TYPE_PENGUIN_500 = 155; + +const int PRC_COMP_WING_TYPE_ERINYES = 30; +const int PRC_COMP_WING_TYPE_BIRD_RED = 31; +const int PRC_COMP_WING_TYPE_BIRD_DARK = 32; +const int PRC_COMP_WING_TYPE_BIRD_BLUE = 33; +const int PRC_COMP_WING_TYPE_DRAGON_BLACK = 34; +const int PRC_COMP_WING_TYPE_DRAGON_BLUE = 35; +const int PRC_COMP_WING_TYPE_DRAGON_BRASS = 36; +const int PRC_COMP_WING_TYPE_DRAGON_BRONZE = 37; +const int PRC_COMP_WING_TYPE_DRAGON_COPPER = 38; +const int PRC_COMP_WING_TYPE_DRAGON_GOLD = 39; +const int PRC_COMP_WING_TYPE_DRAGON_GREEN = 40; +const int PRC_COMP_WING_TYPE_DRAGON_SILVER = 41; +const int PRC_COMP_WING_TYPE_DRAGON_WHITE = 42; +const int PRC_COMP_WING_TYPE_BIRD_KENKU = 43; +const int PRC_COMP_WING_TYPE_HALFDRAGON_GOLD = 44; +const int PRC_COMP_WING_TYPE_HALFDRAGON_SILVER = 45; +const int PRC_COMP_WING_TYPE_ANGEL_ARMORED = 46; +const int PRC_COMP_WING_TYPE_ANGEL_FALLEN = 47; +const int PRC_COMP_WING_TYPE_RAVEN = 48; +const int PRC_COMP_WING_TYPE_MEPHISTO = 49; +const int PRC_COMP_WING_TYPE_DRAGON_SHADOW = 50; +const int PRC_COMP_WING_TYPE_DRAGON_DRACOLICH = 51; +const int PRC_COMP_WING_TYPE_DRAGON_PRISMATIC = 52; + +const int PRC_COMP_TAIL_TYPE_CAT = 130; +const int PRC_COMP_TAIL_TYPE_MAGE_TAIL = 131; +const int PRC_COMP_TAIL_TYPE_MAGE_TAIL_2 = 132; +const int PRC_COMP_TAIL_TYPE_WAR_TAIL = 133; +const int PRC_COMP_TAIL_TYPE_LIZARD_RED = 134; +const int PRC_COMP_TAIL_TYPE_LIZARD_BLACK = 135; +const int PRC_COMP_TAIL_TYPE_LIZARD_BLUE = 136; +const int PRC_COMP_TAIL_TYPE_LIZARD_WHITE = 137; +const int PRC_COMP_TAIL_TYPE_LIZARD_PRISMATIC = 138; +const int PRC_COMP_TAIL_TYPE_LIZARD_SHADOW = 139; +const int PRC_COMP_TAIL_TYPE_LIZARD_BRASS = 140; +const int PRC_COMP_TAIL_TYPE_LIZARD_BRONZE = 141; +const int PRC_COMP_TAIL_TYPE_LIZARD_COPPER = 142; +const int PRC_COMP_TAIL_TYPE_LIZARD_GOLD = 143; +const int PRC_COMP_TAIL_TYPE_LIZARD_SILVER = 144; +const int PRC_COMP_TAIL_TYPE_GARGOYLE = 145; +const int PRC_COMP_TAIL_TYPE_MINOTAUR = 146; +const int PRC_COMP_TAIL_TYPE_WERECAT = 147; +const int PRC_COMP_TAIL_TYPE_BULETTER = 148; +const int PRC_COMP_TAIL_TYPE_RAVEN = 149; +const int PRC_COMP_TAIL_TYPE_FALCON = 150; +const int PRC_COMP_TAIL_TYPE_MANTICORE = 151; +const int PRC_COMP_TAIL_TYPE_HALFLIZARD_SILVER = 152; +const int PRC_COMP_TAIL_TYPE_HALFLIZARD_GOLD = 153; +const int PRC_COMP_TAIL_TYPE_LEOPARD = 154; +const int PRC_COMP_TAIL_TYPE_LEOPARD_SNOW = 155; +const int PRC_COMP_TAIL_TYPE_TIGER = 156; +const int PRC_COMP_TAIL_TYPE_TIGER_WHITE = 157; +const int PRC_COMP_TAIL_TYPE_FOX = 158; +const int PRC_COMP_TAIL_TYPE_WOLF = 159; +const int PRC_COMP_TAIL_TYPE_WOLF_SHAGGY = 160; +const int PRC_COMP_TAIL_TYPE_LIZARD_DYNAMIC = 161; +const int PRC_COMP_TAIL_TYPE_DEVIL_DYNAMIC = 162; \ No newline at end of file diff --git a/src/include/prc_craft_inc.nss b/src/include/prc_craft_inc.nss new file mode 100644 index 0000000..8d0c398 --- /dev/null +++ b/src/include/prc_craft_inc.nss @@ -0,0 +1,2953 @@ +/* + prc_craft_include + + Include file for forge scripts, currently restricted to equipable items + + By: Flaming_Sword + Created: Jul 12, 2006 + Modified: Nov 5, 2007 + + GetItemPropertySubType() returns 0 or 65535, not -1 + on no subtype as in Lexicon + + Some hardcoded functions for itemprops to avoid looping through + the 2das multiple times: + + Get2DALineFromItemprop() + DisallowType() + PrereqSpecialHandling() + PropSpecialHandling() + +*/ + +itemproperty ConstructIP(int nType, int nSubTypeValue = 0, int nCostTableValue = 0, int nParam1Value = 0); + +//Partly ripped off the lexicon :P +int GetItemBaseAC(object oItem); + +int GetItemArmourCheckPenalty(object oItem); + +int GetCraftingFeat(object oItem); + +string GetItemPropertyString(itemproperty ip); + +struct ipstruct GetIpStructFromString(string sIp); + +#include "psi_inc_psifunc" + +//#include "prc_inc_spells" //Most core functions are + //accessd through psi_inc_core +//#include "prc_inc_listener" +#include "prc_inc_chat" +#include "prc_x2_craft" + +const int NUM_MAX_PROPERTIES = 200; +const int NUM_MAX_SUBTYPES = 256; +//const int NUM_MAX_FEAT_SUBTYPES = 16384; //because iprp_feats is frickin' huge +const int NUM_MAX_FEAT_SUBTYPES = 397; //because the above screwed the game + +//const int NUM_MAX_SPELL_SUBTYPES = 540; //restricted to bioware spells + // to avoid crashes +const int NUM_MAX_SPELL_SUBTYPES = 1172; //new value for list skipping + +const int PRC_CRAFT_SIMPLE_WEAPON = 1; +const int PRC_CRAFT_MARTIAL_WEAPON = 2; +const int PRC_CRAFT_EXOTIC_WEAPON = 3; + +const int PRC_CRAFT_MATERIAL_METAL = 1; +const int PRC_CRAFT_MATERIAL_WOOD = 2; +const int PRC_CRAFT_MATERIAL_LEATHER = 3; +const int PRC_CRAFT_MATERIAL_CLOTH = 4; + +const string PRC_CRAFT_UID_SUFFIX = "_UID_PRC"; +const string PRC_CRAFT_STORAGE_CHEST = "PRC_CRAFT_STORAGE_CHEST"; +const string PRC_CRAFT_TEMPORARY_CHEST = "PRC_CRAFT_TEMPORARY_CHEST"; +const string PRC_CRAFT_ITEMPROP_ARRAY = "PRC_CRAFT_ITEMPROP_ARRAY"; + +const int PRC_CRAFT_FLAG_NONE = 0; +const int PRC_CRAFT_FLAG_MASTERWORK = 1; +const int PRC_CRAFT_FLAG_ADAMANTINE = 2; +const int PRC_CRAFT_FLAG_DARKWOOD = 4; +const int PRC_CRAFT_FLAG_DRAGONHIDE = 8; +const int PRC_CRAFT_FLAG_MITHRAL = 16; +const int PRC_CRAFT_FLAG_COLD_IRON = 32; //not implemented +const int PRC_CRAFT_FLAG_ALCHEMICAL_SILVER = 64; //not implemented +const int PRC_CRAFT_FLAG_MUNDANE_CRYSTAL = 128; +const int PRC_CRAFT_FLAG_DEEP_CRYSTAL = 256; + +const int PRC_CRAFT_ITEM_TYPE_WEAPON = 1; +const int PRC_CRAFT_ITEM_TYPE_ARMOUR = 2; +const int PRC_CRAFT_ITEM_TYPE_SHIELD = 3; +const int PRC_CRAFT_ITEM_TYPE_AMMO = 4; +const int PRC_CRAFT_ITEM_TYPE_MISC = 5; +const int PRC_CRAFT_ITEM_TYPE_CASTSPELL = 6; +const int PRC_CRAFT_ITEM_TYPE_SPECIAL = 7; + +const string PRC_CRAFT_SPECIAL_BANE = "PRC_CRAFT_SPECIAL_BANE"; +const string PRC_CRAFT_SPECIAL_BANE_RACE = "PRC_CRAFT_SPECIAL_BANE_RACE"; + +const string PRC_CRAFT_LISTEN = "PRC_CRAFT_LISTEN"; + +const string PRC_CRAFT_APPEARANCE_ARRAY = "PRC_CRAFT_APPEARANCE_ARRAY"; +const string PRC_IP_ARRAY = "PRC_IP_ARRAY"; + +const int PRC_CRAFT_LISTEN_SETNAME = 1; +const int PRC_CRAFT_LISTEN_SETAPPEARANCE = 2; +/* +const int PRC_CRAFT_LISTEN_SETNAME = 1; +const int PRC_CRAFT_LISTEN_SETNAME = 1; +const int PRC_CRAFT_LISTEN_SETNAME = 1; +const int PRC_CRAFT_LISTEN_SETNAME = 1; +const int PRC_CRAFT_LISTEN_SETNAME = 1; +*/ + +const string PRC_CRAFT_TOKEN = "PRC_CRAFT_TOKEN"; + +const int CRAFT_COST_TYPE_INVALID = 0; +const int CRAFT_COST_TYPE_MARKET = 1; +const int CRAFT_COST_TYPE_CRAFTING = 2; +const int CRAFT_COST_TYPE_XP = 3; + +struct itemvars +{ + object item; + int enhancement; + int additionalcost; + int epic; +}; + +struct ipstruct +{ + int type; + int subtype; + int costtablevalue; + int param1value; +}; + +struct golemhds +{ + int base; + int max1; + int max2; +}; + +object GetCraftChest() +{ + return GetObjectByTag(PRC_CRAFT_STORAGE_CHEST); +} + +object GetTempCraftChest() +{ + return GetObjectByTag(PRC_CRAFT_TEMPORARY_CHEST); +} + +int GetCraftingSkill(object oItem) +{ + int nType = StringToInt(Get2DACache("prc_craft_gen_it", "Type", GetBaseItemType(oItem))); + if((nType == PRC_CRAFT_ITEM_TYPE_WEAPON) || + (nType == PRC_CRAFT_ITEM_TYPE_AMMO) + ) + return SKILL_CRAFT_WEAPON; + if((nType == PRC_CRAFT_ITEM_TYPE_ARMOUR) || + (nType == PRC_CRAFT_ITEM_TYPE_SHIELD) + ) + return SKILL_CRAFT_ARMOR; + return SKILL_CRAFT_GENERAL; +} + +string GetMaterialString(int nType) +{ + string sType = IntToString(nType); + int nLen = GetStringLength(sType); + switch(nLen) + { + case 1: sType = "0" + sType; + case 2: sType = "0" + sType; break; + } + return sType; +} + +//Will replace first 3 chars of item's tag with material flags +string GetNewItemTag(object oItem, int nType) +{ + string sTag = GetTag(oItem); + return GetMaterialString(nType) + GetStringRight(sTag, GetStringLength(sTag) - 3); +} + +int GetArmourCheckPenaltyReduction(object oItem) +{ + int nBase = GetBaseItemType(oItem); + int nBonus = 0; + if(((nBase == BASE_ITEM_ARMOR) || + (nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD)) + ) + { + int nMaterial = StringToInt(GetStringLeft(GetTag(oItem), 3)); + int nACPenalty = GetItemArmourCheckPenalty(oItem); + if(nMaterial & PRC_CRAFT_FLAG_MASTERWORK) + { + nBonus = PRCMin(1, nACPenalty); + } + if(nMaterial & PRC_CRAFT_FLAG_DARKWOOD) + { + nBonus = PRCMin(2, nACPenalty); + } + if(nMaterial & PRC_CRAFT_FLAG_MITHRAL) + { + nBonus = PRCMin(3, nACPenalty); + } + } + return nBonus; +} + +int SkillHasACPenalty(int nSkill) +{ + return StringToInt(Get2DACache("skills", "ArmorCheckPenalty", nSkill)); +} + +//Returns -1 if itemprop is not in the list, -2 if similar and should disallow type, +// hardcoded to avoid looping through 2das +int Get2DALineFromItemprop(string sFile, itemproperty ip, object oItem) +{ //it's either hardcoding or large numbers of 2da reads + int nType = GetItemPropertyType(ip); + int nSubType = GetItemPropertySubType(ip); + int nCostTableValue = GetItemPropertyCostTableValue(ip); + int nParam1Value = GetItemPropertyParam1Value(ip); + int nBase = GetBaseItemType(oItem); + if(sFile == "craft_armour") + { + switch(nType) + { + case ITEM_PROPERTY_AC_BONUS: + { + return (nCostTableValue - 1); + break; + } + case ITEM_PROPERTY_BONUS_FEAT: + { + if(nSubType == 201) return 24; + break; + } + case ITEM_PROPERTY_CAST_SPELL: + { + switch(nSubType) + { + case IP_CONST_CASTSPELL_CONTROL_UNDEAD_13: return 63; break; + case IP_CONST_CASTSPELL_ETHEREALNESS_18: return 33; break; + case 928: return (GetItemPropertyCostTableValue(ip) == IP_CONST_CASTSPELL_NUMUSES_1_USE_PER_DAY) ? 43 : 44; break; //spell turning + } + break; + } + case ITEM_PROPERTY_DAMAGE_REDUCTION: + { + if(nSubType == IP_CONST_DAMAGEREDUCTION_1) + { + switch(nCostTableValue) + { + case IP_CONST_DAMAGESOAK_5_HP: return 38; break; + case IP_CONST_DAMAGESOAK_10_HP: return 39; break; + case IP_CONST_DAMAGESOAK_15_HP: return 40; break; + default: return -2; break; + } + } + else if(nSubType == IP_CONST_DAMAGEREDUCTION_6) + { + switch(nCostTableValue) + { + case IP_CONST_DAMAGESOAK_5_HP: return 41; break; + case IP_CONST_DAMAGESOAK_10_HP: return 42; break; + default: return -2; break; + } + } + else return -2; + break; + } + case ITEM_PROPERTY_DAMAGE_RESISTANCE: + { + int nBaseValue = -1; + switch(nSubType) + { + case IP_CONST_DAMAGETYPE_ACID: nBaseValue = 20; break; + case IP_CONST_DAMAGETYPE_COLD: nBaseValue = 25; break; + case IP_CONST_DAMAGETYPE_ELECTRICAL: nBaseValue = 29; break; + case IP_CONST_DAMAGETYPE_FIRE: nBaseValue = 34; break; + case IP_CONST_DAMAGETYPE_SONIC: nBaseValue = 51; break; + } + if(nBaseValue != -1) + { + switch(nCostTableValue) + { + case IP_CONST_DAMAGERESIST_10: return nBaseValue; break; + case IP_CONST_DAMAGERESIST_20: return nBaseValue + 1; break; + case IP_CONST_DAMAGERESIST_30: return nBaseValue + 2; break; + case IP_CONST_DAMAGERESIST_50: return nBaseValue + 3; break; + default: return -2; break; + } + } + else return -2; + break; + } + case ITEM_PROPERTY_SPELL_RESISTANCE: + { + if((nCostTableValue >= 27) && (nCostTableValue <= 34)) return (nCostTableValue + 28); + else return -2; + break; + } + case ITEM_PROPERTY_SKILL_BONUS: + { + if(nSubType == SKILL_HIDE) + { + nCostTableValue -= GetArmourCheckPenaltyReduction(oItem); + switch(nCostTableValue) + { + case 5: return 49; break; + case 10: return 50; break; + case 15: return 51; break; + default: return -1; break; + } + } + else if(nSubType == SKILL_MOVE_SILENTLY) + { + nCostTableValue -= GetArmourCheckPenaltyReduction(oItem); + switch(nCostTableValue) + { + case 5: return 52; break; + case 10: return 53; break; + case 15: return 54; break; + default: return -1; break; + } + } + else + return -2; + break; + } + } + } + else if(sFile == "craft_weapon") + { + switch(nType) + { + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + { + return (nCostTableValue - 1); + break; + } + case ITEM_PROPERTY_DAMAGE_BONUS: + { + int bAmmo = StringToInt(Get2DACache("prc_craft_gen_it", "Type", GetBaseItemType(oItem))) == PRC_CRAFT_ITEM_TYPE_AMMO; + if(bAmmo && nSubType == ((nBase == BASE_ITEM_BULLET) ? DAMAGE_TYPE_BLUDGEONING : DAMAGE_TYPE_PIERCING)) + return (nCostTableValue - 1); + if(nSubType == IP_CONST_DAMAGETYPE_ACID) + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_1d6) return 20; //Corrosive + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 21; //Acidic blast + else return -2; + } + else if(nSubType == IP_CONST_DAMAGETYPE_FIRE) + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_1d6) return 30; //Flaming + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 31; //Flaming blast + else return -2; + } + else if(nSubType == IP_CONST_DAMAGETYPE_COLD) + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_1d6) return 32; //Frost + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 33; //Icy Blast + else return -2; + } + else if(nSubType == IP_CONST_DAMAGETYPE_ELECTRICAL) + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_1d6) return 37; //Shock + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 38; // Lightning Blast + else return -2; + } + else if(nSubType == IP_CONST_DAMAGETYPE_SONIC) + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_1d4) return 39; //Screaming + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 40; //Sonic Blast + else return -2; + } + else if(nSubType == IP_CONST_DAMAGETYPE_MAGICAL) + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_1d4) return 45; //Psychokinetic + else return -2; + } + + else return -2; + break; + } + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + { + switch(nSubType) + { + case IP_CONST_ALIGNMENTGROUP_LAWFUL: + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_2d6) return 22; // Anarchic + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 23; // Anarchic Power + else return -2; + break; + } + case IP_CONST_ALIGNMENTGROUP_CHAOTIC: + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_2d6) return 24; // Axiomatic + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 25; // Axiomatic Power + else return -2; + break; + } + case IP_CONST_ALIGNMENTGROUP_EVIL: + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_2d6) return 34; //Holy + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 35; // Holy Power + else return -2; + break; + } + case IP_CONST_ALIGNMENTGROUP_GOOD: + { + if(nCostTableValue == IP_CONST_DAMAGEBONUS_2d6) return 41; //Unholy + else if(nCostTableValue == IP_CONST_DAMAGEBONUS_3d6) return 42; //Unholy Power + else return -2; + break; + } + default: return -2; break; + } + break; + } + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + { + switch(nCostTableValue) + { + case IP_CONST_DAMAGEBONUS_2d6: return 26; break; //Bane + case IP_CONST_DAMAGEBONUS_4d6: return 27; break; //Dread + default: return -2; break; + } + break; + } + case ITEM_PROPERTY_KEEN: return 36; break; //Keen + case ITEM_PROPERTY_ON_HIT_PROPERTIES: + { + switch(nSubType) + { + case IP_CONST_ONHIT_SLAYRACE: + { + if(nParam1Value == IP_CONST_RACIALTYPE_UNDEAD) + { + if(nCostTableValue == IP_CONST_ONHIT_SAVEDC_14) return 28; //Disruption + else if(nCostTableValue == 21) return 29; //Mighty Disruption + else return -2; + } + break; + } + case IP_CONST_ONHIT_VORPAL: return 43; break; //Vorpal + case IP_CONST_ONHIT_WOUNDING: return 44; break; //Wounding + } + break; + } + } + } + return -1; +} + +//Hardcoded properties to disallow, avoids many loops through 2das +void DisallowType(object oItem, string sFile, itemproperty ip) +{ + int i; + int nType = GetItemPropertyType(ip); + int nSubType = GetItemPropertySubType(ip); + int nCostTableValue = GetItemPropertyCostTableValue(ip); + int nParam1Value = GetItemPropertyParam1Value(ip); + if(sFile == "craft_armour") + { + switch(nType) + { + /* + case ITEM_PROPERTY_AC_BONUS: + { + } + case ITEM_PROPERTY_BONUS_FEAT: + { + } + case ITEM_PROPERTY_CAST_SPELL: + { + } + */ + case ITEM_PROPERTY_DAMAGE_REDUCTION: + { + for(i = 38; i <= 42; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + break; + } + case ITEM_PROPERTY_DAMAGE_RESISTANCE: + { + for(i = 20; i <= 23; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + for(i = 25; i <= 32; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + for(i = 34; i <= 37; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + for(i = 51; i <= 54; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + break; + } + case ITEM_PROPERTY_SPELL_RESISTANCE: + { + for(i = 55; i <= 62; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + break; + } + case ITEM_PROPERTY_SKILL_BONUS: + { + for(i = 45; i <= 50; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + break; + } + } + } + else if(sFile == "craft_weapon") + { + switch(nType) + { + /* + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + { + } + */ + case ITEM_PROPERTY_DAMAGE_BONUS: + { + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 20, 0); + for(i = 29; i <= 32; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + for(i = 36; i <= 38; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + break; + } + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + { + for(i = 22; i <= 25; i++) + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 34, 0); + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 35, 0); + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 41, 0); + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 42, 0); + break; + } + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + { + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 25, 0); + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 26, 0); + break; + } + //case ITEM_PROPERTY_KEEN: array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 36, 0); break; + case ITEM_PROPERTY_ON_HIT_PROPERTIES: + { + switch(nSubType) + { + case IP_CONST_ONHIT_SLAYRACE: + { + if(nParam1Value == IP_CONST_RACIALTYPE_UNDEAD) + { + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 28, 0); + array_set_int(oItem, PRC_CRAFT_ITEMPROP_ARRAY, 29, 0); + } + break; + } + /* + case IP_CONST_ONHIT_VORPAL: return 43; break; + case IP_CONST_ONHIT_WOUNDING: return 44; break; + */ + } + break; + } + } + } +} + +//hardcoding of some prereqs +int PrereqSpecialHandling(string sFile, object oItem, int nLine) +{ + int nTemp; + int nBase = GetBaseItemType(oItem); + if(StringToInt(Get2DACache(sFile, "Special", nLine))) + { + if(sFile == "craft_armour") + { //nothing here yet + } + else if(sFile == "craft_weapon") + { + int nDamageType = StringToInt(Get2DACache("baseitems", "WeaponType", nBase)); + int bRangedType = StringToInt(Get2DACache("baseitems", "RangedWeapon", nBase)); + int bRanged = bRangedType;// && (bRangedType != nBase); + switch(nLine) + { + case 28: + case 29: + { + return (!bRanged && ((nDamageType == 2) || (nDamageType == 5))); + break; + } + case 36: + { + return (!bRanged && (nDamageType != 2)); + break; + } + case 43: + { + return (!bRanged && ((nDamageType == 3) || (nDamageType == 4))); + break; + } + } + } + else if(sFile == "craft_wondrous") + { + } + else if(sFile == "craft_golem") + { + } + } + if(sFile == "craft_wondrous") + return nBase == StringToInt(Get2DACache(sFile, "BaseItem", nLine)); + return TRUE; +} + +//Checks and decrements spells based on property to add +int CheckCraftingSpells(object oPC, string sFile, int nLine, int bDecrement = FALSE) +{ + if(GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC)) return TRUE; //artificers roll UMD checks during crafting time + //if(GetLevelByClass(CLASS_TYPE_BATTLESMITH, oPC)) return TRUE; + if(nLine == -1) return FALSE; + string sTemp = Get2DACache(sFile, "Spells", nLine); + if(sTemp == "") + return TRUE; //no prereqs, always true + int nSpellPattern = 0; + int nSpell1, nSpell2, nSpell3, nSpellOR1, nSpellOR2, nSpellOR3; + int bOR = FALSE; + string sSub; + int nLength = GetStringLength(sTemp); + int nPosition; + int nTemp; + int i; + + for(i = 0; i < 7; i++) + { + nPosition = FindSubString(sTemp, "_"); + sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + if(sSub != "*") + { + nTemp = StringToInt(sSub); + nSpellPattern += FloatToInt(pow(2.0, IntToFloat(i))); + switch(i) + { + case 0: + { + nSpell1 = nTemp; + break; + } + case 1: + { + nSpell2 = nTemp; + break; + } + case 2: + { + nSpell3 = nTemp; + break; + } + case 3: + { + nSpellOR1 = nTemp; + break; + } + case 4: + { + nSpellOR2 = nTemp; + break; + } + case 6: + { + nSpellOR3 = nTemp; + break; + } + } + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + + if (nSpellPattern) + { + // ... Existing code for nSpellPattern & 1 + + if (nSpellPattern & 2) + { + if (!PRCGetHasSpell(nSpell2, oPC)) + { + if (!CheckImbueItem(oPC, nSpell2)) + return FALSE; + } + } + + if (nSpellPattern & 4) + { + if (!PRCGetHasSpell(nSpell3, oPC)) + { + if (!CheckImbueItem(oPC, nSpell3)) + return FALSE; + } + } + + if (nSpellPattern & 8) + { + if (!PRCGetHasSpell(nSpellOR1, oPC)) + { + if (!CheckImbueItem(oPC, nSpellOR1)) + { + if (nSpellPattern & 16) + { + if (!PRCGetHasSpell(nSpellOR2, oPC)) + { + // Warlocks don't get two bites at things. + // if (!CheckImbueItem(oPC, nSpellOR2)) + return FALSE; + } + } + else if (nSpellPattern & 32) // Check for nSpellOR3 + { + if (!PRCGetHasSpell(nSpellOR3, oPC)) + { + // Handle nSpellOR3 missing + return FALSE; + } + } + else + return FALSE; + } + } + } + else if (nSpellPattern & 16) + { + if (!PRCGetHasSpell(nSpellOR2, oPC)) + { + if (!CheckImbueItem(oPC, nSpellOR2)) + return FALSE; + } + // Check for nSpellOR3 + else if (nSpellPattern & 32) + { + if (!PRCGetHasSpell(nSpellOR3, oPC)) + { + // Handle nSpellOR3 missing + return FALSE; + } + } + } + else if (nSpellPattern & 32) // Check for nSpellOR3 alone + { + if (!PRCGetHasSpell(nSpellOR3, oPC)) + { + // Handle nSpellOR3 missing + return FALSE; + } + } + + // ... Existing code for decrementing spells + + } + + return TRUE; +} +/* if(nSpellPattern) + { + if(nSpellPattern & 1) + { + if(sFile == "craft_wondrous") + { + switch(nLine) + { + case 85: + { + bOR = (PRCGetHasSpell(SPELL_DETECT_UNDEAD, oPC) && + PRCGetHasSpell(SPELL_FIREBALL, oPC) && + PRCGetHasSpell(SPELL_FLAME_WEAPON, oPC) && + PRCGetHasSpell(SPELL_LIGHT, oPC) && + PRCGetHasSpell(SPELL_PRISMATIC_SPRAY, oPC) && + PRCGetHasSpell(SPELL_PROTECTION_FROM_ELEMENTS, oPC) && + PRCGetHasSpell(SPELL_WALL_OF_FIRE, oPC)); + if(GetHasFeat(FEAT_IMBUE_ITEM) && bOR == FALSE) + bOR = GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 16) && + GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 18) && + GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 17) && + GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 16) && + GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 22) && + GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 18) && + GetIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, 19); + if(bDecrement) + { + PRCDecrementRemainingSpellUses(oPC, SPELL_DETECT_UNDEAD); + PRCDecrementRemainingSpellUses(oPC, SPELL_FIREBALL); + PRCDecrementRemainingSpellUses(oPC, SPELL_FLAME_WEAPON); + PRCDecrementRemainingSpellUses(oPC, SPELL_LIGHT); + PRCDecrementRemainingSpellUses(oPC, SPELL_PRISMATIC_SPRAY); + PRCDecrementRemainingSpellUses(oPC, SPELL_PROTECTION_FROM_ELEMENTS); + PRCDecrementRemainingSpellUses(oPC, SPELL_WALL_OF_FIRE); + } + return bOR; + break; + } + } + } + if(!PRCGetHasSpell(nSpell1, oPC)) + { + if(!CheckImbueItem(oPC, nSpell1)) + return FALSE; + } + } + if(nSpellPattern & 2) + { + if(!PRCGetHasSpell(nSpell2, oPC)) + { + if(!CheckImbueItem(oPC, nSpell2)) + return FALSE; + } + } + if(nSpellPattern & 4) + { + if(!PRCGetHasSpell(nSpell3, oPC)) + { + if(!CheckImbueItem(oPC, nSpell3)) + return FALSE; + } + } + if(nSpellPattern & 8) + { + if(!PRCGetHasSpell(nSpellOR1, oPC)) + { + if(!CheckImbueItem(oPC, nSpellOR1)) + { + if(nSpellPattern & 16) + { + if(!PRCGetHasSpell(nSpellOR2, oPC)) + { + // Warlocks don't get two bites at things. + //if(!CheckImbueItem(oPC, nSpellOR2)) + return FALSE; + } + } + else + return FALSE; + } + } + } + else if(nSpellPattern & 16) + { + if(!PRCGetHasSpell(nSpellOR2, oPC)) + { + if(!CheckImbueItem(oPC, nSpellOR2)) + return FALSE; + } + } + if(bDecrement) + { + if(nSpellPattern & 1) + PRCDecrementRemainingSpellUses(oPC, nSpell1); + if(nSpellPattern & 2) + PRCDecrementRemainingSpellUses(oPC, nSpell2); + if(nSpellPattern & 4) + PRCDecrementRemainingSpellUses(oPC, nSpell3); + if(nSpellPattern & 8) + PRCDecrementRemainingSpellUses(oPC, (bOR) ? nSpellOR2 : nSpellOR1); + else if(nSpellPattern & 16) + PRCDecrementRemainingSpellUses(oPC, nSpellOR2); + } + } + return TRUE; +} */ + +//Checks and decrements power points based on property to add +int CheckCraftingPowerPoints(object oPC, string sFile, int nLine, int bDecrement = FALSE) +{ + if(nLine == -1) return FALSE; + string sTemp = Get2DACache(sFile, "Spells", nLine); + if(sTemp == "") + return TRUE; //no prereqs, always true + int nSpellPattern = 0; + int nSpell1, nSpell2, nSpell3, nSpellOR1, nSpellOR2, nSpellOR3; + int bOR = FALSE; + string sSub; + int nLength = GetStringLength(sTemp); + int nPosition; + int nTemp; + int i; + int nLoss = 0; + + for(i = 0; i < 6; i++) + { + nPosition = FindSubString(sTemp, "_"); + sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + if(sSub != "*") + { + nTemp = StringToInt(sSub); + nSpellPattern += FloatToInt(pow(2.0, IntToFloat(i))); + switch(i) + { + case 0: + { + nSpell1 = nTemp; + break; + } + case 1: + { + nSpell2 = nTemp; + break; + } + case 2: + { + nSpell3 = nTemp; + break; + } + case 3: + { + nSpellOR1 = nTemp; + break; + } + case 4: + { + nSpellOR2 = nTemp; + break; + } + case 6: + { + nSpellOR3 = nTemp; + break; + } + } + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + if(nSpellPattern) + { + if(nSpellPattern & 1) + { + if(GetHasPower(nSpell1, oPC)) + nLoss += (StringToInt(lookup_spell_innate(nSpell1)) * 2 - 1); + else + return FALSE; + } + if(nSpellPattern & 2) + { + if(GetHasPower(nSpell2, oPC)) + nLoss += (StringToInt(lookup_spell_innate(nSpell2)) * 2 - 1); + else + return FALSE; + } + if(nSpellPattern & 4) + { + if(GetHasPower(nSpell3, oPC)) + nLoss += (StringToInt(lookup_spell_innate(nSpell3)) * 2 - 1); + else + return FALSE; + } + if(nSpellPattern & 8) + { + if(GetHasPower(nSpellOR1, oPC)) + nLoss += (StringToInt(lookup_spell_innate(nSpellOR1)) * 2 - 1); + else if(nSpellPattern & 16) + { + if(GetHasPower(nSpellOR2, oPC)) + nLoss += (StringToInt(lookup_spell_innate(nSpellOR2)) * 2 - 1); + else + return FALSE; + } + else + return FALSE; + } + else if(nSpellPattern & 16) + { + if(GetHasPower(nSpellOR2, oPC)) + nLoss += (StringToInt(lookup_spell_innate(nSpellOR2)) * 2 - 1); + else + return FALSE; + } + } + if(GetCurrentPowerPoints(oPC) < nLoss) + return FALSE; + + if(bDecrement) LosePowerPoints(oPC, nLoss, TRUE); + + return TRUE; +} + +int CheckGolemPrereq(object oPC, int nLine, int bEpic) +{ + if(GetLocalInt(oPC, PRC_CRAFT_TOKEN)) + return TRUE; + int bBreak = FALSE; + int nLevel; + int j = 0; + //replace the arti level check later when PrCs are added + int nCasterLevel = PRCMax(PRCMax(PRCMax(GetLevelByTypeArcane(oPC), GetLevelByTypeDivine(oPC)), GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC) + 2), GetLocalInt(oPC, "InvokerLevel")); + int nManifesterLevel = GetManifesterLevel(oPC); + int nTemp, nLength, nPosition; + int bArtificer = (GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC) > 0); + string sFile = "craft_golem"; + string sPropertyType = Get2DACache(sFile, "CasterType", nLine); + string sTemp, sSub; + int nDC = StringToInt(Get2DACache(sFile, "DC", nLine)); + if(sPropertyType == "M") + nLevel = nCasterLevel; + else if(sPropertyType == "P") + nLevel = nManifesterLevel; + else + nLevel = PRCMax(nCasterLevel, nManifesterLevel); + if(!bEpic && Get2DACache(sFile, "Epic", nLine) == "1") + return FALSE; + else if(nLevel < StringToInt(Get2DACache(sFile, "Level", nLine))) + return FALSE; + else + { + if( + (sPropertyType == "M") && + !CheckCraftingSpells(oPC, sFile, nLine) + ) + return FALSE; + if( + (sPropertyType == "P") && + !CheckCraftingPowerPoints(oPC, sFile, nLine) + ) + return FALSE; + sTemp = Get2DACache(sFile, "Skills", nLine); + if(sTemp == "") + bBreak = TRUE; + nLength = GetStringLength(sTemp); + for(j = 0; j < 2; j++) + { + if(bBreak) + break; + nPosition = FindSubString(sTemp, "_"); + sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + if(sSub == "*") + nTemp = -1; + else + nTemp = StringToInt(sSub); + if(nTemp != -1 && !bArtificer) + { + if(!GetPRCIsSkillSuccessful(oPC, nTemp, nDC)) + return FALSE; + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + sTemp = Get2DACache(sFile, "Skills", nLine); + } + return TRUE; +} + +int CheckPrereq(object oPC, int nLine, int bEpic, string sFile, struct itemvars strTemp) +{ + if(GetLocalInt(oPC, PRC_CRAFT_TOKEN)) + return TRUE; + int bBreak = FALSE; + int nLevel; + int j = 0; + //replace the arti level check later when PrCs are added + int nCasterLevel = PRCMax(PRCMax(PRCMax(GetLevelByTypeArcane(oPC), GetLevelByTypeDivine(oPC)), GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC) + 2), GetLocalInt(oPC, "InvokerLevel")); + nCasterLevel += GetLevelByClass(CLASS_TYPE_BATTLESMITH) * 3; + nCasterLevel += GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER) * 3; + int nManifesterLevel = GetManifesterLevel(oPC); + int nTemp, nLength, nPosition; + int bArtificer = (GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC) > 0); + string sPropertyType = Get2DACache(sFile, "PropertyType", nLine); + string sTemp, sSub; + if(sPropertyType == "M") + nLevel = nCasterLevel; + else if(sPropertyType == "P") + nLevel = nManifesterLevel; + else + nLevel = PRCMax(nCasterLevel, nManifesterLevel); + + if (DEBUG) DoDebug("CheckPrereq: "+GetName(oPC)+" nLevel "+IntToString(nLevel)+" PropType "+sPropertyType+" Epic "+IntToString(bEpic)+" sFile "+sFile); + + if(!bEpic && Get2DACache(sFile, "Epic", nLine) == "1") + { + if (DEBUG) DoDebug("CheckPrereq Failed Epic"); + return FALSE; + } + else if(nLevel < StringToInt(Get2DACache(sFile, "Level", nLine))) + { + if (DEBUG) DoDebug("CheckPrereq Failed Level"); + return FALSE; + } + else if(!bEpic && ((StringToInt(Get2DACache(sFile, "Enhancement", nLine)) + strTemp.enhancement) > 10)) + { + if (DEBUG) DoDebug("CheckPrereq Failed Enhancement"); + return FALSE; + } + else + { + if( + (sPropertyType == "M") && !CheckCraftingSpells(oPC, sFile, nLine)) + { + if (DEBUG) DoDebug("CheckPrereq Failed Crafting Spells"); + return FALSE; + } + if((sPropertyType == "P") && !CheckCraftingPowerPoints(oPC, sFile, nLine)) + { + if (DEBUG) DoDebug("CheckPrereq Failed Crafting Powers"); + return FALSE; + } + + sTemp = Get2DACache(sFile, "PrereqMisc", nLine); + if(sTemp == "") + bBreak = TRUE; + nLength = GetStringLength(sTemp); + for(j = 0; j < 5; j++) + { + if(bBreak) + break; + nPosition = FindSubString(sTemp, "_"); + sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + if(sSub == "*") + nTemp = -1; + else + nTemp = StringToInt(sSub); + switch(j) + { + case 0: + { + if(nTemp != -1 && MyPRCGetRacialType(oPC) != nTemp && !bArtificer) + { + if (DEBUG) DoDebug("CheckPrereq Failed Racial Type"); + return FALSE; + } + break; + } + case 1: + { + if(nTemp != -1 && !GetHasFeat(nTemp, oPC)) //artificer can't emulate feat requirements + { + if (DEBUG) DoDebug("CheckPrereq Failed Feat"); + return FALSE; + } + break; + } + case 2: + { + if(((sSub == "G" && GetAlignmentGoodEvil(oPC) != ALIGNMENT_GOOD) || + (sSub == "E" && GetAlignmentGoodEvil(oPC) != ALIGNMENT_EVIL) || + (sSub == "N" && GetAlignmentGoodEvil(oPC) != ALIGNMENT_NEUTRAL)) && + !bArtificer) + { + if (DEBUG) DoDebug("CheckPrereq Failed Alignment G/E"); + return FALSE; + } + break; + } + case 3: + { + if(((sSub == "L" && GetAlignmentLawChaos(oPC) != ALIGNMENT_LAWFUL) || + (sSub == "C" && GetAlignmentLawChaos(oPC) != ALIGNMENT_CHAOTIC) || + (sSub == "N" && GetAlignmentLawChaos(oPC) != ALIGNMENT_NEUTRAL)) && + !bArtificer) + { + if (DEBUG) DoDebug("CheckPrereq Failed Alignment L/C"); + return FALSE; + } + break; + } + case 4: + { + if(nTemp != -1 && !GetLevelByClass(nTemp, oPC) && !bArtificer) + { + if (DEBUG) DoDebug("CheckPrereq Failed Class"); + return FALSE; + } + break; + } + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + sTemp = Get2DACache(sFile, "Skill", nLine); + if(sTemp != "" && (GetSkillRank(StringToInt(sTemp), oPC) < StringToInt(Get2DACache(sFile, "SkillRanks", nLine)))) + { + if (DEBUG) DoDebug("CheckPrereq Failed Skill"); + return FALSE; + } + } + return TRUE; +} + +//Returns a struct containing enhancement and additional cost values, don't bother with array when bSet == 0 +struct itemvars GetItemVars(object oPC, object oItem, string sFile, int bEpic = 0, int bSet = 0) +{ + struct itemvars strTemp; + int i, bBreak, nTemp; + int j, k, bEnhanced, count; + int nEnhancement; + int nSpellPattern; + int nSpell1, nSpell2, nSpell3, nSpellOR1, nSpellOR2; + int nCasterLevel = PRCMax(GetLevelByTypeArcane(oPC), GetLevelByTypeDivine(oPC)); + int nManifesterLevel = GetManifesterLevel(oPC); + int nLevel; + int nFileEnd = PRCGetFileEnd(sFile); + int nRace = MyPRCGetRacialType(oPC); + int nFeat = GetCraftingFeat(oItem); + int bArmsArmour = nFeat == FEAT_CRAFT_ARMS_ARMOR; + string sPropertyType; + strTemp.item = oItem; + string sSub; + int nLength; + int nPosition; + if(bSet) + { + if(array_exists(oPC, PRC_CRAFT_ITEMPROP_ARRAY)) + array_delete(oPC, PRC_CRAFT_ITEMPROP_ARRAY); + array_create(oPC, PRC_CRAFT_ITEMPROP_ARRAY); + //Setup + for(i = 0; i <= nFileEnd; i++) + { + if(!GetPRCSwitch("PRC_CRAFT_DISABLE_" + sFile + "_" + IntToString(i)) && PrereqSpecialHandling(sFile, oItem, i)) + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, i, 1); + } + if(bArmsArmour) + { + int nBase = GetBaseItemType(oItem); + int bRangedType = StringToInt(Get2DACache("baseitems", "RangedWeapon", nBase)); + if(bRangedType && (bRangedType != nBase)) + { //disallowed because ranged weapons can't have onhit + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 22, 0); + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 24, 0); + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 26, 0); + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 34, 0); + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 40, 0); + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 42, 0); + } + } + } + if(bArmsArmour) + { + itemproperty ip = GetFirstItemProperty(oItem); + if(DEBUG) DoDebug("GetItemVars: " + GetName(oItem) + ", before itemprop loop"); + //Checking itemprops + count = 0; + while(GetIsItemPropertyValid(ip)) + { //assumes no duplicated enhancement itemprops + k = Get2DALineFromItemprop(sFile, ip, oItem); //is causing TMI with armour with skill props + count++; + if(DEBUG) DoDebug("GetItemVars: itemprop number " + IntToString(count) + + " " + IntToString(GetItemPropertyType(ip)) + + " " + IntToString(GetItemPropertySubType(ip)) + + " " + IntToString(GetItemPropertyCostTableValue(ip)) + + " " + IntToString(GetItemPropertyParam1Value(ip)) + ); + + if(k >= 0) + { + if(k < 20) bEnhanced = TRUE; + if(bSet) + { + for(j = StringToInt(Get2DACache(sFile, "ReplaceLast", k)); j >= 0; j--) + { + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, k - j, 0); + } + } + nEnhancement = StringToInt(Get2DACache(sFile, "Enhancement", k)); + strTemp.enhancement += nEnhancement; + if(nEnhancement > 5) strTemp.epic = TRUE; + strTemp.additionalcost += StringToInt(Get2DACache(sFile, "AdditionalCost", k)); + + if(DEBUG) + { + sPropertyType = GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", k))); + if(sPropertyType != "") + DoDebug("GetItemVars: " + sPropertyType); + } + } + else if(bSet && k == -2) + { + DisallowType(oPC, sFile, ip); + } + + ip = GetNextItemProperty(oItem); + } + if(strTemp.enhancement > 10) strTemp.epic = TRUE; + if(DEBUG) DoDebug("GetItemVars: " + GetName(oItem) + ", after itemprop loop"); + } + else + { + strTemp.enhancement = 0; + strTemp.additionalcost = 0; + strTemp.epic = FALSE; + } + + if(DEBUG) + { + DoDebug("GetItemVars: " + GetName(oItem) + + ", Enhancement: " + IntToString(strTemp.enhancement) + + ", AdditionalCost: " + IntToString(strTemp.additionalcost)); + } + if(!bSet) return strTemp; //don't bother with array + + if(!bEpic && strTemp.epic) + { //attempting to craft epic item without epic crafting feat, fails + for(i = 0; i <= nFileEnd; i++) + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + return strTemp; + } + if(!bEnhanced && bArmsArmour) + { //no enhancement value, cannot add more itemprops, stop right there + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, 0, 1); + for(i = 1; i <= nFileEnd; i++) + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + return strTemp; + } + string sTemp; + //Checking available spells, epic flag, caster level + if(!GetLocalInt(oPC, PRC_CRAFT_TOKEN)) + { + //moved check to confirmation stage + } + else if(GetPRCSwitch(PRC_DISABLE_CRAFT_EPIC)) + { //disabling epic crafting at npc facilities + for(i = 0; i <= nFileEnd; i++) + { //will skip over properties already disallowed + if(array_get_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, i) && Get2DACache(sFile, "Epic", i) == "1") + { + array_set_int(oPC, PRC_CRAFT_ITEMPROP_ARRAY, i, 0); + } + } + } + return strTemp; +} + +//Returns an int depending on the weapon type +// returns 0 if not a weapon +int GetWeaponType(int nBaseItem) +{ + switch(nBaseItem) + { + case BASE_ITEM_BASTARDSWORD: + case BASE_ITEM_TWOBLADEDSWORD: + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_KAMA: + case BASE_ITEM_KATANA: + case BASE_ITEM_KUKRI: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_SHURIKEN: + case BASE_ITEM_DWARVENWARAXE: + case BASE_ITEM_WHIP: + case BASE_ITEM_ELVEN_LIGHTBLADE: + case BASE_ITEM_ELVEN_THINBLADE: + case BASE_ITEM_ELVEN_COURTBLADE: + case BASE_ITEM_DOUBLE_SCIMITAR: + case BASE_ITEM_NUNCHAKU: + case BASE_ITEM_SAI: + case BASE_ITEM_EAGLE_CLAW: + return PRC_CRAFT_EXOTIC_WEAPON; + break; + + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_BATTLEAXE: + case BASE_ITEM_LIGHTFLAIL: + case BASE_ITEM_WARHAMMER: + case BASE_ITEM_LONGBOW: + case BASE_ITEM_HALBERD: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_HANDAXE: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SCIMITAR: + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_TRIDENT: + case BASE_ITEM_HEAVY_PICK: + case BASE_ITEM_LIGHT_PICK: + case BASE_ITEM_FALCHION: + case BASE_ITEM_SAP: + case BASE_ITEM_MAUL: + return PRC_CRAFT_MARTIAL_WEAPON; + break; + + case BASE_ITEM_LIGHTMACE: + case BASE_ITEM_DART: + case BASE_ITEM_MORNINGSTAR: + case BASE_ITEM_SHORTSPEAR: + case BASE_ITEM_SICKLE: + case BASE_ITEM_SLING: + case BASE_ITEM_DAGGER: + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + case BASE_ITEM_CLUB: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_KATAR: + case BASE_ITEM_HEAVY_MACE: + case BASE_ITEM_GOAD: + return PRC_CRAFT_SIMPLE_WEAPON; + break; + + default: return 0; break; + } + return 0; +} + +void ApplyBonusToStatBasedChecks(object oItem, int nStat, int nBonus) +{ + int i; + string sSkills = "skills"; + string sFilter; + switch(nStat) + { + case ABILITY_STRENGTH: sFilter = "STR"; break; + case ABILITY_DEXTERITY: sFilter = "DEX"; break; + case ABILITY_CONSTITUTION: sFilter = "CON"; break; + case ABILITY_INTELLIGENCE: sFilter = "INT"; break; + case ABILITY_WISDOM: sFilter = "WIS"; break; + case ABILITY_CHARISMA: sFilter = "CHA"; break; + } + for(i = 0; i <= PRCGetFileEnd(sSkills); i++) + { + if(Get2DACache(sSkills, "KeyAbility", i) == sFilter) + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(i, nBonus), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } +} + +//Hardcoding of some adjustments +itemproperty PropSpecialHandling(object oItem, string sFile, int nLine, int nIndex) +{ + itemproperty ip; + int nTemp; + string sEntry = Get2DACache(sFile, "IP" + IntToString(nIndex), nLine); + if(DEBUG) DoDebug("PropSpecialHandling(object, " + sFile + ", " + IntToString(nLine) + ", " + IntToString(nIndex) + ")"); + if(DEBUG) DoDebug("Get2DACache: IP" + IntToString(nIndex) + ", " + sEntry); + struct ipstruct iptemp = GetIpStructFromString(sEntry); + string sTemp; + + int nBase = GetBaseItemType(oItem); + if(StringToInt(Get2DACache(sFile, "Special", nLine))) + { + if(sFile == "craft_armour") + { + if(iptemp.type == ITEM_PROPERTY_SKILL_BONUS && SkillHasACPenalty(iptemp.subtype)) + iptemp.costtablevalue += GetArmourCheckPenaltyReduction(oItem); + } + else if(sFile == "craft_weapon") + { + switch(nLine) + { + case 26: + case 27: + { + nTemp = GetLocalInt(GetItemPossessor(oItem), PRC_CRAFT_SPECIAL_BANE_RACE); + if(nIndex == 1) + { + iptemp.subtype = nTemp; + nTemp = StringToInt(Get2DACache("baseitems", "WeaponType", nBase)); + switch(nTemp) + { + case 1: iptemp.param1value = IP_CONST_DAMAGETYPE_PIERCING; break; + case 2: + case 5: iptemp.param1value = IP_CONST_DAMAGETYPE_BLUDGEONING; break; + case 3: + case 4: iptemp.param1value = IP_CONST_DAMAGETYPE_SLASHING; break; + } + } + else if(nIndex == 2) + { + iptemp.subtype = nTemp; + iptemp.costtablevalue += IPGetWeaponEnhancementBonus(oItem); + if(iptemp.costtablevalue > 20) + iptemp.costtablevalue = 20; + } + else if(nIndex == 3) + iptemp.param1value = nTemp; + break; + } + } + if(iptemp.type == ITEM_PROPERTY_ENHANCEMENT_BONUS && + StringToInt(Get2DACache("prc_craft_gen_it", "Type", nBase)) == PRC_CRAFT_ITEM_TYPE_AMMO) + { + iptemp.type = ITEM_PROPERTY_DAMAGE_BONUS; + iptemp.subtype = (nBase == BASE_ITEM_BULLET) ? DAMAGE_TYPE_BLUDGEONING : DAMAGE_TYPE_PIERCING; + } + } + } + if(DEBUG) DoDebug("Reconstructed: IP" + IntToString(nIndex) + ", " + IntToString(iptemp.type)+"_"+IntToString(iptemp.subtype)+"_"+IntToString(iptemp.costtablevalue)+"_"+IntToString(iptemp.param1value)); + return ConstructIP(iptemp.type, iptemp.subtype, iptemp.costtablevalue, iptemp.param1value); +} + +void ApplyItemProps(object oItem, string sFile, int nLine) +{ + int i; + itemproperty ip; + if(StringToInt(Get2DACache(sFile, "Special", nLine))) + { + if(sFile == "craft_wondrous") + { + switch(nLine) + { + case 43: + { + ApplyBonusToStatBasedChecks(oItem, ABILITY_CHARISMA, 3); + SetName(oItem, GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", nLine)))); + return; + break; + } + case 85: //helm of brilliance + case 91: //necklace of fireballs + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: SetItemCharges(oItem, 50); break; + case 108: IPSafeAddItemProperty(oItem, ItemPropertyLimitUseBySAlign(IP_CONST_ALIGNMENT_TN), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; + case 116: + { + object oCopy = CopyObject(oItem, GetLocation(oItem), GetItemPossessor(oItem), "prc_turnphyl"); + DestroyObject(oItem); + oItem = oCopy; + } + } + } + } + i = 1; //FUGLY HACK: i doesn't initialise properly in for loop + for(i = 1; i <= 6; i++) + { + if(DEBUG) DoDebug("ApplyItemProps: i = " + IntToString(i)); + ip = PropSpecialHandling(oItem, sFile, nLine, i); + if(GetIsItemPropertyValid(ip)) + { + if(DEBUG) DoDebug(GetItemPropertyString(ip)); + IPSafeAddItemProperty(oItem, ip, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } + else + break; //no more itemprops, no gaps, assuming no errors + } + if(sFile != "craft_weapon" && sFile != "craft_armour") + SetName(oItem, GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", nLine)))); +} + +//Partly ripped off the lexicon :P +int GetItemBaseAC(object oItem) +{ + int nAC = -1; + int nBase = GetBaseItemType(oItem); + int bID = GetIdentified(oItem); + if(bID) SetIdentified(oItem, FALSE); + + if(nBase == BASE_ITEM_ARMOR) + { + switch(GetGoldPieceValue(oItem)) + { + case 1: nAC = 0; break; // None + case 5: nAC = 1; break; // Padded + case 10: nAC = 2; break; // Leather + case 15: nAC = 3; break; // Studded Leather / Hide + case 100: nAC = 4; break; // Chain Shirt / Scale Mail + case 150: nAC = 5; break; // Chainmail / Breastplate + case 200: nAC = 6; break; // Splint Mail / Banded Mail + case 600: nAC = 7; break; // Half-Plate + case 1500: nAC = 8; break; // Full Plate + } + } + else if(nBase == BASE_ITEM_SMALLSHIELD) + nAC = 1; + else if(nBase == BASE_ITEM_LARGESHIELD) + nAC = 2; + else if(nBase == BASE_ITEM_TOWERSHIELD) + nAC = 3; + + if(bID) SetIdentified(oItem, TRUE); + return nAC; +} + +int GetItemArmourCheckPenalty(object oItem) +{ + int nBase = GetBaseItemType(oItem); + int nPenalty = 0; + if(nBase == BASE_ITEM_SMALLSHIELD) + nPenalty = 1; + else if(nBase == BASE_ITEM_LARGESHIELD) + nPenalty = 2; + else if(nBase == BASE_ITEM_TOWERSHIELD) + nPenalty = 10; + else if(nBase == BASE_ITEM_ARMOR) + { + switch(GetItemBaseAC(oItem)) + { + case 3: nPenalty = 1; break; + case 4: nPenalty = 2; break; + case 5: nPenalty = 5; break; + case 6: nPenalty = 7; break; + case 7: nPenalty = 7; break; + case 8: nPenalty = 8; break; + } + } + return nPenalty; +} + +string GetCrafting2DA(object oItem) +{ + int nBase = GetBaseItemType(oItem); + int nMaterial = StringToInt(GetStringLeft(GetTag(oItem), 3)); + if(((nBase == BASE_ITEM_ARMOR) || + (nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD)) + ) + { + if((GetItemBaseAC(oItem) == 0) && !(nMaterial & PRC_CRAFT_FLAG_MASTERWORK)) return "craft_wondrous"; + return "craft_armour"; + } + + if(GetWeaponType(nBase) || + (nBase == BASE_ITEM_ARROW) || + (nBase == BASE_ITEM_BOLT) || + (nBase == BASE_ITEM_BULLET) + ) + return "craft_weapon"; + + if(nBase == BASE_ITEM_RING) return "craft_ring"; + + if(((nBase == BASE_ITEM_HELMET) || + (nBase == BASE_ITEM_AMULET) || + (nBase == BASE_ITEM_BELT) || + (nBase == BASE_ITEM_BOOTS) || + (nBase == BASE_ITEM_GLOVES) || + (nBase == BASE_ITEM_BRACER) || + (nBase == BASE_ITEM_CLOAK)) + ) + return "craft_wondrous"; + + //restrict to castspell itemprops? + /* + if(nBase == BASE_ITEM_MAGICROD) return FEAT_CRAFT_ROD; + if(nBase == BASE_ITEM_MAGICSTAFF) return FEAT_CRAFT_STAFF; + if(nBase == BASE_ITEM_MAGICWAND) return FEAT_CRAFT_WAND; + */ + + //bioware crafting, castspell itemprops only + return ""; +} + +int GetCraftingFeat(object oItem) +{ + int nBase = GetBaseItemType(oItem); + int nMaterial = StringToInt(GetStringLeft(GetTag(oItem), 3)); + if(((nBase == BASE_ITEM_ARMOR) || + (nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD)) || + (GetWeaponType(nBase) || + (nBase == BASE_ITEM_ARROW) || + (nBase == BASE_ITEM_BOLT) || + (nBase == BASE_ITEM_BULLET) + ) + ) + { + if((GetItemBaseAC(oItem) == 0) && !(nMaterial & PRC_CRAFT_FLAG_MASTERWORK)) return FEAT_CRAFT_WONDROUS; + return FEAT_CRAFT_ARMS_ARMOR; + } + + if(nBase == BASE_ITEM_RING) return FEAT_FORGE_RING; + + //routing bioware feats through this convo + if((nBase == BASE_ITEM_MAGICROD) || + (nBase == BASE_ITEM_CRAFTED_ROD) + ) + return FEAT_CRAFT_ROD; + if((nBase == BASE_ITEM_MAGICSTAFF) || + (nBase == BASE_ITEM_CRAFTED_STAFF) + ) + return FEAT_CRAFT_STAFF; + if((nBase == BASE_ITEM_MAGICWAND) || + (nBase == BASE_ITEM_BLANK_WAND) + ) + return FEAT_CRAFT_WAND; + if(nBase == BASE_ITEM_BLANK_POTION) return FEAT_BREW_POTION; + if(nBase == BASE_ITEM_BLANK_SCROLL) return FEAT_SCRIBE_SCROLL; + + if(((nBase == BASE_ITEM_HELMET) || + (nBase == BASE_ITEM_AMULET) || + (nBase == BASE_ITEM_BELT) || + (nBase == BASE_ITEM_BOOTS) || + (nBase == BASE_ITEM_GLOVES) || + (nBase == BASE_ITEM_BRACER) || + (nBase == BASE_ITEM_CLOAK)) + ) + return FEAT_CRAFT_WONDROUS; + + return -1; +} + +int GetEpicCraftingFeat(int nFeat) +{ + switch(nFeat) + { + case FEAT_CRAFT_WONDROUS: return FEAT_CRAFT_EPIC_WONDROUS_ITEM; + case FEAT_CRAFT_CONSTRUCT: + case FEAT_CRAFT_ARMS_ARMOR: return FEAT_CRAFT_EPIC_MAGIC_ARMS_ARMOR; + case FEAT_CRAFT_ROD: return FEAT_CRAFT_EPIC_ROD; + case FEAT_CRAFT_STAFF: return FEAT_CRAFT_EPIC_STAFF; + case FEAT_FORGE_RING: return FEAT_FORGE_EPIC_RING; + } + return -1; +} + +//Returns whether the item can be made of a material +int CheckCraftingMaterial(int nBaseItem, int nMaterial, int nBaseAC = -1) +{ + if(nBaseItem == BASE_ITEM_WHIP || nBaseItem == BASE_ITEM_SAP) return (nMaterial == PRC_CRAFT_MATERIAL_LEATHER); + + if((nBaseItem == BASE_ITEM_SMALLSHIELD) || + (nBaseItem == BASE_ITEM_LARGESHIELD) || + (nBaseItem == BASE_ITEM_TOWERSHIELD) + ) + return ((nMaterial == PRC_CRAFT_MATERIAL_METAL) || (nMaterial == PRC_CRAFT_MATERIAL_WOOD)); + + if(nBaseItem == BASE_ITEM_ARMOR) + { + /* + if(nBaseAC >= 0 && nBaseAC <= 1) return (nMaterial == PRC_CRAFT_MATERIAL_CLOTH); + if(nBaseAC >= 2 && nBaseAC <= 3) return (nMaterial == PRC_CRAFT_MATERIAL_LEATHER); + else return (nMaterial == PRC_CRAFT_MATERIAL_METAL); + */ + return ((nMaterial == PRC_CRAFT_MATERIAL_METAL) || (nMaterial == PRC_CRAFT_MATERIAL_LEATHER)); + } + //since you can't make adamantine weapons at the moment + if((nBaseItem == BASE_ITEM_HEAVYCROSSBOW) || + (nBaseItem == BASE_ITEM_LIGHTCROSSBOW) || + (nBaseItem == BASE_ITEM_LONGBOW) || + (nBaseItem == BASE_ITEM_SHORTBOW) || + (nBaseItem == BASE_ITEM_QUARTERSTAFF) || + (nBaseItem == BASE_ITEM_CLUB) || + (nBaseItem == BASE_ITEM_NUNCHAKU) || //nunchaku + (nBaseItem == BASE_ITEM_SCYTHE) || + (nBaseItem == BASE_ITEM_SHORTSPEAR) || + (nBaseItem == BASE_ITEM_TRIDENT) || + (nBaseItem == BASE_ITEM_HALBERD) || + (nBaseItem == BASE_ITEM_GOAD) || //goad + (nBaseItem == BASE_ITEM_CLUB) + ) + { + return (nMaterial == PRC_CRAFT_MATERIAL_WOOD); + } + //assume stuff is made of metal (most of it is) + return (nMaterial == PRC_CRAFT_MATERIAL_METAL); +} + +//Returns the DC for crafting a particular item +int GetCraftingDC(object oItem) +{ + int nDC = 0; + int nBase = GetBaseItemType(oItem); + int nType = GetWeaponType(nBase); + if(((nBase == BASE_ITEM_ARMOR) || + (nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD)) + ) + { + nDC = 10 + GetItemBaseAC(oItem); + } + else if(((nBase == BASE_ITEM_HEAVYCROSSBOW) || + (nBase == BASE_ITEM_LIGHTCROSSBOW)) + ) + { + nDC = 15; + } + else if(((nBase == BASE_ITEM_LONGBOW) || + (nBase == BASE_ITEM_SHORTBOW)) + ) + { + nDC = 12; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_MIGHTY) + { + nDC = 15 + 2 * GetItemPropertyCostTableValue(ip); + break; + } + ip = GetNextItemProperty(oItem); + } + } + else if(nType == PRC_CRAFT_SIMPLE_WEAPON) + nDC = 12; + else if(nType == PRC_CRAFT_MARTIAL_WEAPON) + nDC = 15; + else if(nType == PRC_CRAFT_EXOTIC_WEAPON) + nDC = 18; + return nDC; +} + +//Applies Masterwork properties to oItem +void MakeMasterwork(object oItem) +{ + if(GetPlotFlag(oItem)) return; //sanity check + int nBase = GetBaseItemType(oItem); + if((nBase == BASE_ITEM_ARMOR) || + (nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD) + ) + { + //no armour check penalty here + if(GetItemArmourCheckPenalty(oItem) == 0) return; + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_HIDE, 1) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_MOVE_SILENTLY, 1), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_PICK_POCKET, 1) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_SET_TRAP, 1) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_TUMBLE, 1) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_JUMP, 1) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } + else if(GetWeaponType(nBase)) + { + IPSafeAddItemProperty(oItem, ItemPropertyAttackBonus(1), 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + } + else if(StringToInt(Get2DACache("prc_craft_gen_it", "Type", nBase)) == PRC_CRAFT_ITEM_TYPE_AMMO) + { + /* + int nDamageType = (nBase == BASE_ITEM_BULLET) ? DAMAGE_TYPE_BLUDGEONING : DAMAGE_TYPE_PIERCING; + itemproperty ip1 = ItemPropertyDamageBonus(nDamageType, IP_CONST_DAMAGEBONUS_1); + */ + IPSafeAddItemProperty(oItem, ItemPropertyAttackBonus(1), 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + } + else + return; +} + +void MakeAdamantine(object oItem) +{ + if(GetPlotFlag(oItem)) return; //sanity check + if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR) + { + int nBonus = 0; + switch(GetItemBaseAC(oItem)) + { + case 1: + case 2: + case 3: nBonus = IP_CONST_DAMAGERESIST_1; break; + case 4: + case 5: nBonus = IP_CONST_DAMAGERESIST_2; break; + case 6: + case 7: + case 8: nBonus = IP_CONST_DAMAGERESIST_3; break; + } + if(nBonus) + { + IPSafeAddItemProperty(oItem, ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_BLUDGEONING, nBonus), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_PIERCING, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SLASHING, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } + } +} + +void MakeDarkwood(object oItem) +{ + if(GetPlotFlag(oItem)) return; //sanity check + IPSafeAddItemProperty(oItem, ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_50_PERCENT), 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + int nBase = GetBaseItemType(oItem); + if(((nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD)) + ) + { + int nBonus = 2; + if(nBase == BASE_ITEM_SMALLSHIELD) nBonus = 1; + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_HIDE, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_MOVE_SILENTLY, nBonus), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_PICK_POCKET, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_SET_TRAP, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_TUMBLE, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_JUMP, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } +} + +void MakeDragonhide(object oItem) +{ + //Does nothing so far +} + +void MakeMithral(object oItem) +{ + if(GetPlotFlag(oItem)) return; //sanity check + IPSafeAddItemProperty(oItem, ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_50_PERCENT), 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + int nBase = GetBaseItemType(oItem); + if(((nBase == BASE_ITEM_ARMOR) || + (nBase == BASE_ITEM_SMALLSHIELD) || + (nBase == BASE_ITEM_LARGESHIELD) || + (nBase == BASE_ITEM_TOWERSHIELD)) + ) + { + int nBonus = 3; + int nPenalty = GetItemArmourCheckPenalty(oItem); + if(nBonus > nPenalty) nBonus = nPenalty; + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_HIDE, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_MOVE_SILENTLY, nBonus), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_PICK_POCKET, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_SET_TRAP, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_TUMBLE, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_JUMP, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_BALANCE, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oItem, ItemPropertySkillBonus(SKILL_CLIMB, nBonus) , 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + if(GetItemBaseAC(oItem) == 1) + IPSafeAddItemProperty(oItem, ItemPropertyArcaneSpellFailure(IP_CONST_ARCANE_SPELL_FAILURE_MINUS_5_PERCENT), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + else + IPSafeAddItemProperty(oItem, ItemPropertyArcaneSpellFailure(IP_CONST_ARCANE_SPELL_FAILURE_MINUS_10_PERCENT), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } +} + +void MakeColdIron(object oItem) +{ + //Does nothing so far +} + +void MakeSilver(object oItem) +{ + //Does nothing so far +} + +void MakeMundaneCrystal(object oItem) +{ + //Does nothing so far +} + +void MakeDeepCrystal(object oItem) +{ + //Does nothing so far +} + +//Creates an item on oOwner, from the baseitemtype and base AC (for armour) +object CreateStandardItem(object oOwner, int nBaseItemType, int nBaseAC = -1) +{ + string sResRef = Get2DACache("prc_craft_gen_it", "Blueprint", nBaseItemType); + int nStackSize = StringToInt(Get2DACache("baseitems", "ILRStackSize", nBaseItemType)); + if(nBaseItemType == BASE_ITEM_ARMOR) + { + switch(nBaseAC) + { + case 0: sResRef = "x2_cloth008"; break; + case 1: sResRef = "nw_aarcl009"; break; + case 2: sResRef = "nw_aarcl001"; break; + case 3: sResRef = "nw_aarcl002"; break; + case 4: sResRef = "nw_aarcl012"; break; + case 5: sResRef = "nw_aarcl004"; break; + case 6: sResRef = "nw_aarcl005"; break; + case 7: sResRef = "nw_aarcl006"; break; + case 8: sResRef = "nw_aarcl007"; break; + } + } + string sTag; + if (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oOwner)) sTag = GetName(oOwner); + + return CreateItemOnObject(sResRef, oOwner, nStackSize, sTag); +} + +int GetEnhancementBaseCost(object oItem) +{ + string sFile = GetCrafting2DA(oItem); + if(sFile == "craft_armour") return 1000; + if(sFile == "craft_weapon") return 2000; + + return 0; +} + +//returns pnp market price of an item +int GetPnPItemCost(struct itemvars strTemp, int bIncludeBaseCost = TRUE) +{ + int nMaterial, nEnhancement; + int nAdd = 0, nTemp = 0; + int nType = GetBaseItemType(strTemp.item); + + if(DEBUG) + { + DoDebug("nType: " + IntToString(nType)); + FloatingTextStringOnCreature( "nType = " + IntToString(nType), GetFirstPC()); + } + if(bIncludeBaseCost) + { + SetIdentified(strTemp.item, FALSE); + int nMultiplyer = StringToInt(Get2DACache("baseitems", "ItemMultiplier", nType)); + if(nMultiplyer < 1) nMultiplyer = 1; + if(DEBUG) + { + DoDebug("nMultiplyer: " + IntToString(nMultiplyer)); + FloatingTextStringOnCreature( "nMultiplyer = " + IntToString(nMultiplyer), GetFirstPC()); + } + + nTemp = GetGoldPieceValue(strTemp.item) / nMultiplyer; + if(DEBUG) + { + DoDebug("nTemp: " + IntToString(nTemp)); + FloatingTextStringOnCreature( "nTemp = " + IntToString(nTemp), GetFirstPC()); + } + + SetIdentified(strTemp.item, TRUE); + int nFlag = StringToInt(Get2DACache("prc_craft_gen_it", "Type", nType)); + string sMaterial = GetStringLeft(GetTag(strTemp.item), 3); + nMaterial = StringToInt(sMaterial); + if(GetMaterialString(nMaterial) != sMaterial) + nMaterial = 0; + if(nMaterial & PRC_CRAFT_FLAG_MASTERWORK) + { + switch(nFlag) + { + case PRC_CRAFT_ITEM_TYPE_WEAPON: nAdd += 300; break; + case PRC_CRAFT_ITEM_TYPE_ARMOUR: nAdd += 150; break; + case PRC_CRAFT_ITEM_TYPE_SHIELD: nAdd += 150; break; + case PRC_CRAFT_ITEM_TYPE_AMMO: nAdd += 594; break; + } + } + if(nMaterial & PRC_CRAFT_FLAG_ADAMANTINE) + { + switch(GetItemBaseAC(strTemp.item)) + { + case 1: + case 2: + case 3: nAdd += 5000; break; + case 4: + case 5: nAdd += 10000; break; + case 6: + case 7: + case 8: nAdd += 15000; break; + } + } + if(nMaterial & PRC_CRAFT_FLAG_DARKWOOD) + { + nAdd += StringToInt(Get2DACache("baseitems", "TenthLBS", nType)); + } + if(nMaterial & PRC_CRAFT_FLAG_DRAGONHIDE) + { + nAdd += nAdd + nTemp; + } + if(nMaterial & PRC_CRAFT_FLAG_MITHRAL) + { + if(nType == BASE_ITEM_ARMOR) + { + switch(GetItemBaseAC(strTemp.item)) + { + case 1: + case 2: + case 3: nAdd += 1000; break; + case 4: + case 5: nAdd += 4000; break; + case 6: + case 7: + case 8: nAdd += 9000; break; + } + } + else + { + switch(nFlag) + { + case PRC_CRAFT_ITEM_TYPE_WEAPON: nAdd += 50 * StringToInt(Get2DACache("baseitems", "TenthLBS", nType)); break; + case PRC_CRAFT_ITEM_TYPE_SHIELD: nAdd += 1000; break; + } + } + } + if(nMaterial & PRC_CRAFT_FLAG_COLD_IRON) + { + //not implemented + } + if(nMaterial & PRC_CRAFT_FLAG_ALCHEMICAL_SILVER) + { + //not implemented + } + if(nMaterial & PRC_CRAFT_FLAG_MUNDANE_CRYSTAL) + { + //not implemented + } + if(nMaterial & PRC_CRAFT_FLAG_DEEP_CRYSTAL) + { + //not implemented + } + if(((nType == BASE_ITEM_LONGBOW) || + (nType == BASE_ITEM_SHORTBOW)) + ) + { + int nCompMult = (nType == BASE_ITEM_LONGBOW) ? 100 : 75; + itemproperty ip = GetFirstItemProperty(strTemp.item); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_MIGHTY) + { + nAdd += GetItemPropertyCostTableValue(ip) * nCompMult; + break; + } + ip = GetNextItemProperty(strTemp.item); + } + } + } + if(DEBUG) + { + DoDebug("nAdd: " + IntToString(nAdd)); + FloatingTextStringOnCreature( "nAdd = " + IntToString(nAdd), GetFirstPC()); + } + nTemp += nAdd; + nEnhancement = GetEnhancementBaseCost(strTemp.item) * strTemp.enhancement * strTemp.enhancement; + if(strTemp.epic) nEnhancement *= 10; + nTemp += nEnhancement + strTemp.additionalcost; + + if(StringToInt(Get2DACache("baseitems", "Stacking", nType)) > 1) + nTemp = FloatToInt(IntToFloat(nTemp) * IntToFloat(GetItemStackSize(strTemp.item))/ 50.0); + if(nTemp < 1) nTemp = 1; + + return nTemp; +} + +struct golemhds GetGolemHDsFromString(string sHD) +{ + struct golemhds hds; + //initialise variables + string sTemp = sHD; + string sSub; + int nLength = GetStringLength(sTemp); + int nPosition; + int nTemp; + int i; + + for(i = 0; i < 3; i++) + { + nPosition = FindSubString(sTemp, "_"); + sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + if(sSub == "*") + nTemp = -1; + else + nTemp = StringToInt(sSub); + switch(i) + { + case 0: hds.base = nTemp; break; + case 1: hds.max1 = nTemp; break; + case 2: hds.max2 = nTemp; break; + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + return hds; +} + +//returns market price in gp, +int GetPnPGolemCost(int nLine, int nHD, int nCosttype) +{ + int nCost = StringToInt(Get2DACache("craft_golem", "MarketPrice", nLine)); + struct golemhds ghds = GetGolemHDsFromString(Get2DACache("craft_golem", "HD", nLine)); + + + //sanity checking + if(nHD < ghds.base) nHD = ghds.base; + if(nHD > ghds.max2) nHD = ghds.max2; + + if(nHD > ghds.base) + { + nCost += (ghds.base - nHD) * 5000; + if(nHD > ghds.max1) + nCost += 50000; + } + + if(nCosttype == CRAFT_COST_TYPE_XP) + { + if(StringToInt(Get2DACache("craft_golem", "Epic", nLine))) + { + nCost /= 100; + } + else + { + nCost /= 25; + } + } + else if(nCosttype == CRAFT_COST_TYPE_CRAFTING) + { + nCost /= 2; + } + + if(nCosttype != CRAFT_COST_TYPE_XP) + nCost += StringToInt(Get2DACache("craft_golem", "SpecialCost", nLine)); + + return nCost; +} + +//Creates an item for oPC of nBaseItemType, made of nMaterial +object MakeMyItem(object oPC, int nBaseItemType, int nBaseAC = -1, int nMaterial = 0, int nMighty = -1) +{ + object oTemp = CreateStandardItem(GetTempCraftChest(), nBaseItemType, nBaseAC); + string sMaterial = GetMaterialString(nMaterial); + string sTag = sMaterial + GetUniqueID() + PRC_CRAFT_UID_SUFFIX; + object oChest = GetCraftChest(); + while(GetIsObjectValid(GetItemPossessedBy(oChest, sTag)))//make sure there aren't any tag conflicts + sTag = sMaterial + GetUniqueID() + PRC_CRAFT_UID_SUFFIX; + if (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oPC)) sTag = GetName(oPC); + object oNew = CopyObject(oTemp, GetLocation(oChest), oChest, sTag); + string sPrefix = ""; + if(nMighty > 0) + { + if(nMighty > 20) nMighty = 20; + IPSafeAddItemProperty(oNew, ItemPropertyMaxRangeStrengthMod(nMighty), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + IPSafeAddItemProperty(oNew, ItemPropertyLimitUseByAbility(ABILITY_STRENGTH, ((nMighty * 2) + 10)), 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); + } + DestroyObject(oTemp, 0.1); + if(nMaterial & PRC_CRAFT_FLAG_MASTERWORK) //name prefix will be overridden by materials + { + sPrefix = "Masterwork "; + MakeMasterwork(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_ADAMANTINE) //assumes only 1 material at a time + { + sPrefix = "Adamantine "; + MakeAdamantine(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_DARKWOOD) + { + sPrefix = "Darkwood "; + MakeDarkwood(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_DRAGONHIDE) + { + sPrefix = "Dragonhide "; + MakeDragonhide(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_MITHRAL) + { + sPrefix = "Mithral "; + MakeMithral(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_COLD_IRON) + { + sPrefix = "Cold Iron "; + MakeColdIron(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_ALCHEMICAL_SILVER) + { + sPrefix = "Silver "; + MakeSilver(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_MUNDANE_CRYSTAL) + { + sPrefix = "Crystal "; + MakeMundaneCrystal(oNew); + } + if(nMaterial & PRC_CRAFT_FLAG_DEEP_CRYSTAL) + { + sPrefix = "Deep Crystal "; + MakeDeepCrystal(oNew); + } + if(nMighty > 0) sPrefix += "Composite "; + + if((nBaseItemType == BASE_ITEM_ARMOR) && (nBaseAC == 0)) + SetName(oNew, "Robe"); + SetName(oNew, sPrefix + GetName(oNew)); + + return oNew; +} + +//Adds action highlight to a conversation string +string ActionString(string sString) +{ + return "" + sString + ""; +} + +//Inserts a space at the end of a string if the string +// is not empty +string InsertSpaceAfterString(string sString) +{ + if(sString != "") + return sString + " "; + else return ""; +} + +string GetItemPropertyString(itemproperty ip) +{ + int nType = GetItemPropertyType(ip); + if(nType == -1) return ""; + int nSubType = GetItemPropertySubType(ip); + int nCostTable = GetItemPropertyCostTable(ip); + int nCostTableValue = GetItemPropertyCostTableValue(ip); + int nParam1 = GetItemPropertyParam1(ip); + int nParam1Value = GetItemPropertyParam1Value(ip); + string sDesc = InsertSpaceAfterString( + GetStringByStrRef(StringToInt(Get2DACache("itempropdef", "GameStrRef", nType))) + ); + string sSubType = Get2DACache("itempropdef", "SubTypeResRef", nType); + sSubType = Get2DACache(sSubType, "Name", nSubType); + if(sSubType != "") + sDesc += InsertSpaceAfterString(GetStringByStrRef(StringToInt(sSubType))); + string sCostTable = Get2DACache("itempropdef", "CostTableResRef", nType); + sCostTable = Get2DACache("iprp_costtable", "Name", StringToInt(sCostTable)); + sCostTable = Get2DACache(sCostTable, "Name", nCostTableValue); + if(sCostTable != "") + sDesc += InsertSpaceAfterString(GetStringByStrRef(StringToInt(sCostTable))); + string sParam1; + if(nType == ITEM_PROPERTY_ON_HIT_PROPERTIES) //Param1 depends on subtype + { + sParam1 = Get2DACache(Get2DACache("itempropdef", "SubTypeResRef", nType), "Param1ResRef", nSubType); + } + else + { + sParam1 = Get2DACache("itempropdef", "Param1ResRef", nType); + } + if(sParam1 != "") + { + sDesc += InsertSpaceAfterString(GetStringByStrRef(StringToInt(Get2DACache("iprp_paramtable", "Name", StringToInt(sParam1))))); + sParam1 = Get2DACache("iprp_paramtable", "TableResRef", StringToInt(sParam1)); + sParam1 = Get2DACache(sParam1, "Name", nParam1Value); + if(sParam1 != "") + sDesc += InsertSpaceAfterString(GetStringByStrRef(StringToInt(sParam1))); + } + sDesc += "\n"; + + return sDesc; +} + +//Returns a string describing the item +string ItemStats(object oItem) +{ + string sDesc = GetName(oItem) + + "\n\n" + + GetStringByStrRef(StringToInt(Get2DACache("baseitems", "Name", GetBaseItemType(oItem)))) + + "\n\n"; + + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { + sDesc += GetItemPropertyString(ip); + } + ip = GetNextItemProperty(oItem); + } + return sDesc; +} + +//Returns TRUE if nBaseItem can have nItemProp +int ValidProperty(object oItem, int nItemProp) +{ + int nPropColumn = StringToInt(Get2DACache("baseitems", "PropColumn", GetBaseItemType(oItem))); + string sPropCloumn = ""; + switch(nPropColumn) + { + case 0: sPropCloumn = "0_Melee"; break; + case 1: sPropCloumn = "1_Ranged"; break; + case 2: sPropCloumn = "2_Thrown"; break; + case 3: sPropCloumn = "3_Staves"; break; + case 4: sPropCloumn = "4_Rods"; break; + case 5: sPropCloumn = "5_Ammo"; break; + case 6: sPropCloumn = "6_Arm_Shld"; break; + case 7: sPropCloumn = "7_Helm"; break; + case 8: sPropCloumn = "8_Potions"; break; + case 9: sPropCloumn = "9_Scrolls"; break; + case 10: sPropCloumn = "10_Wands"; break; + case 11: sPropCloumn = "11_Thieves"; break; + case 12: sPropCloumn = "12_TrapKits"; break; + case 13: sPropCloumn = "13_Hide"; break; + case 14: sPropCloumn = "14_Claw"; break; + case 15: sPropCloumn = "15_Misc_Uneq"; break; + case 16: sPropCloumn = "16_Misc"; break; + case 17: sPropCloumn = "17_No_Props"; break; + case 18: sPropCloumn = "18_Containers"; break; + case 19: sPropCloumn = "19_HealerKit"; break; + case 20: sPropCloumn = "20_Torch"; break; + case 21: sPropCloumn = "21_Glove"; break; + } + return(Get2DACache("itemprops", sPropCloumn, nItemProp) == "1"); +} + +//Makes an item property from values - total pain in the arse, need 1 per itemprop +itemproperty ConstructIP(int nType, int nSubTypeValue = 0, int nCostTableValue = 0, int nParam1Value = 0) +{ + itemproperty ip; + switch(nType) + { + case ITEM_PROPERTY_ABILITY_BONUS: return ItemPropertyAbilityBonus(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_AC_BONUS: return ItemPropertyACBonus(nCostTableValue); + case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP: return ItemPropertyACBonusVsAlign(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE: return ItemPropertyACBonusVsDmgType(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP: return ItemPropertyACBonusVsRace(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT: return ItemPropertyACBonusVsSAlign(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_ENHANCEMENT_BONUS: return ItemPropertyEnhancementBonus(nCostTableValue); + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: return ItemPropertyEnhancementBonusVsAlign(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: return ItemPropertyEnhancementBonusVsRace(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: return ItemPropertyEnhancementBonusVsSAlign(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER: return ItemPropertyEnhancementPenalty(nCostTableValue); + case ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION: return ItemPropertyWeightReduction(nCostTableValue); + case ITEM_PROPERTY_BONUS_FEAT: return ItemPropertyBonusFeat(nSubTypeValue); + case ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N: return ItemPropertyBonusLevelSpell(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_CAST_SPELL: return ItemPropertyCastSpell(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_BONUS: return ItemPropertyDamageBonus(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: return ItemPropertyDamageBonusVsAlign(nSubTypeValue, nParam1Value, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: return ItemPropertyDamageBonusVsRace(nSubTypeValue, nParam1Value, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT: return ItemPropertyDamageBonusVsSAlign(nSubTypeValue, nCostTableValue, nParam1Value); + case ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE: return ItemPropertyDamageImmunity(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DECREASED_DAMAGE: return ItemPropertyDamagePenalty(nCostTableValue); + case ITEM_PROPERTY_DAMAGE_REDUCTION: return ItemPropertyDamageReduction(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_RESISTANCE: return ItemPropertyDamageResistance(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_VULNERABILITY: return ItemPropertyDamageVulnerability(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DARKVISION: return ItemPropertyDarkvision(); + case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: return ItemPropertyDecreaseAbility(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DECREASED_AC: return ItemPropertyDecreaseAC(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER: return ItemPropertyDecreaseSkill(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT: return ItemPropertyContainerReducedWeight(nCostTableValue); + case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: return ItemPropertyExtraMeleeDamageType(nSubTypeValue); + case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE: return ItemPropertyExtraRangeDamageType(nSubTypeValue); + case ITEM_PROPERTY_HASTE: return ItemPropertyHaste(); + case ITEM_PROPERTY_HOLY_AVENGER: return ItemPropertyHolyAvenger(); + case ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS: return ItemPropertyImmunityMisc(nSubTypeValue); + case ITEM_PROPERTY_IMPROVED_EVASION: return ItemPropertyImprovedEvasion(); + case ITEM_PROPERTY_SPELL_RESISTANCE: return ItemPropertyBonusSpellResistance(nCostTableValue); + case ITEM_PROPERTY_SAVING_THROW_BONUS: return ItemPropertyBonusSavingThrowVsX(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: return ItemPropertyBonusSavingThrow(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_KEEN: return ItemPropertyKeen(); + case ITEM_PROPERTY_LIGHT: return ItemPropertyLight(nCostTableValue, nParam1Value); + case ITEM_PROPERTY_MIGHTY: return ItemPropertyMaxRangeStrengthMod(nCostTableValue); + case ITEM_PROPERTY_NO_DAMAGE: return ItemPropertyNoDamage(); + case ITEM_PROPERTY_ON_HIT_PROPERTIES:{ + if(nParam1Value == -1) + return ItemPropertyOnHitProps(nSubTypeValue, nCostTableValue); + else + return ItemPropertyOnHitProps(nSubTypeValue, nCostTableValue, nParam1Value);} + case ITEM_PROPERTY_DECREASED_SAVING_THROWS: return ItemPropertyReducedSavingThrowVsX(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: return ItemPropertyReducedSavingThrow(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_REGENERATION: return ItemPropertyRegeneration(nCostTableValue); + case ITEM_PROPERTY_SKILL_BONUS: return ItemPropertySkillBonus(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL: return ItemPropertySpellImmunitySpecific(nCostTableValue); + case ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL: return ItemPropertySpellImmunitySchool(nSubTypeValue); + case ITEM_PROPERTY_THIEVES_TOOLS: return ItemPropertyThievesTools(nCostTableValue); + case ITEM_PROPERTY_ATTACK_BONUS: return ItemPropertyAttackBonus(nCostTableValue); + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: return ItemPropertyAttackBonusVsAlign(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: return ItemPropertyAttackBonusVsRace(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: return ItemPropertyAttackBonusVsSAlign(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: return ItemPropertyAttackPenalty(nCostTableValue); + //IP_CONST_UNLIMITEDAMMO_* is costtablevalue, not subtype + case ITEM_PROPERTY_UNLIMITED_AMMUNITION: return ItemPropertyUnlimitedAmmo(nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP: return ItemPropertyLimitUseByAlign(nSubTypeValue); + case ITEM_PROPERTY_USE_LIMITATION_CLASS: return ItemPropertyLimitUseByClass(nSubTypeValue); + case ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE: return ItemPropertyLimitUseByRace(nSubTypeValue); + case ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT: return ItemPropertyLimitUseBySAlign(nSubTypeValue); + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: return ItemPropertyVampiricRegeneration(nCostTableValue); + case ITEM_PROPERTY_TRAP: return ItemPropertyTrap(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_TRUE_SEEING: return ItemPropertyTrueSeeing(); + case ITEM_PROPERTY_ON_MONSTER_HIT: return ItemPropertyOnMonsterHitProperties(nSubTypeValue); + case ITEM_PROPERTY_TURN_RESISTANCE: return ItemPropertyTurnResistance(nCostTableValue); + case ITEM_PROPERTY_MASSIVE_CRITICALS: return ItemPropertyMassiveCritical(nCostTableValue); + case ITEM_PROPERTY_FREEDOM_OF_MOVEMENT: return ItemPropertyFreeAction(); + case ITEM_PROPERTY_MONSTER_DAMAGE: return ItemPropertyMonsterDamage(nCostTableValue); + case ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL: return ItemPropertyImmunityToSpellLevel(nCostTableValue); + case ITEM_PROPERTY_SPECIAL_WALK: return ItemPropertySpecialWalk(nSubTypeValue); + case ITEM_PROPERTY_HEALERS_KIT: return ItemPropertyHealersKit(nCostTableValue); + case ITEM_PROPERTY_WEIGHT_INCREASE: return ItemPropertyWeightIncrease(nParam1Value); + case ITEM_PROPERTY_ONHITCASTSPELL: return ItemPropertyOnHitCastSpell(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_VISUALEFFECT: return ItemPropertyVisualEffect(nSubTypeValue); + case ITEM_PROPERTY_ARCANE_SPELL_FAILURE: return ItemPropertyArcaneSpellFailure(nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_ABILITY_SCORE: return ItemPropertyLimitUseByAbility(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_SKILL_RANKS: return ItemPropertyLimitUseBySkill(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_SPELL_LEVEL: return ItemPropertyLimitUseBySpellcasting(nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_ARCANE_SPELL_LEVEL: return ItemPropertyLimitUseByArcaneSpellcasting(nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_DIVINE_SPELL_LEVEL: return ItemPropertyLimitUseByDivineSpellcasting(nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_SNEAK_ATTACK: return ItemPropertyLimitUseBySneakAttackDice(nCostTableValue); + case ITEM_PROPERTY_USE_LIMITATION_GENDER: + { //no Use Limitation: Gender function entry + //return ItemPropertyAbilityBonus(nSubTypeValue); + break; + } + case ITEM_PROPERTY_SPEED_INCREASE: + { //no Speed Increase function entry + //return ItemPropertyAbilityBonus(nCostTableValue); + break; + } + case ITEM_PROPERTY_SPEED_DECREASE: + //no Speed Decrease function entry + //return ItemPropertyAbilityBonus(nCostTableValue); + case ITEM_PROPERTY_AREA_OF_EFFECT: return ItemPropertyAreaOfEffect(nSubTypeValue, nCostTableValue); + //requires spellid passed as subtype instead of actual subtype + case ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL: return ItemPropertyCastSpellCasterLevel(nSubTypeValue, nCostTableValue); + //requires spellid passed as subtype instead of actual subtype + case ITEM_PROPERTY_CAST_SPELL_METAMAGIC: return ItemPropertyCastSpellMetamagic(nSubTypeValue, nCostTableValue); + //requires spellid passed as subtype instead of actual subtype + case ITEM_PROPERTY_CAST_SPELL_DC: return ItemPropertyCastSpellDC(nSubTypeValue, nCostTableValue); + case ITEM_PROPERTY_PNP_HOLY_AVENGER: return ItemPropertyPnPHolyAvenger(); + case ITEM_PROPERTY_WIZARDRY: return ItemPropertyWizardry(nCostTableValue); + case ITEM_PROPERTY_DIVINITY: return ItemPropertyDivinity(nCostTableValue); + case ITEM_PROPERTY_MATERIAL: return ItemPropertyMaterial(nCostTableValue); + case ITEM_PROPERTY_QUALITY: return ItemPropertyQuality(nCostTableValue); + case ITEM_PROPERTY_ADDITIONAL: return ItemPropertyAdditional(nCostTableValue); + case ITEM_PROPERTY_ECHOBLADE: return ItemPropertyEchoblade(); + + + //ROOM FOR MORE - 89 so far, need increase/decrease cost + /* + case ITEM_PROPERTY_ABILITY_BONUS: + { + return ItemPropertyAbilityBonus(nSubTypeValue, nCostTableValue); + break; + } + */ + } + return ip; +} + +struct ipstruct GetIpStructFromString(string sIp) +{ + struct ipstruct iptemp; + //initialise variables + string sTemp = sIp; + string sSub; + int nLength = GetStringLength(sTemp); + int nPosition; + int nTemp; + int i; + + for(i = 0; i < 4; i++) + { + nPosition = FindSubString(sTemp, "_"); + sSub = (nPosition == -1) ? sTemp : GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + if(sSub == "*") + nTemp = -1; + else + nTemp = StringToInt(sSub); + switch(i) + { + case 0: iptemp.type = nTemp; break; + case 1: iptemp.subtype = nTemp; break; + case 2: iptemp.costtablevalue = nTemp; break; + case 3: iptemp.param1value = nTemp; break; + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + return iptemp; +} + +string PRCGetItemAppearanceString(object oPC, object oItem) +{ + int nBase = GetBaseItemType(oItem); + int nModelType = StringToInt(Get2DACache("baseitems", "ModelType", nBase)); + //PRCSetAppearanceArray(oPC, sString); + string sReturn = ""; + + switch(nModelType) + { + case 0: + { //simple model, 1 value + sReturn = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0)); + break; + } + case 1: + { //helmet, cloak, model + 6 colours, 7 values + sReturn = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER1)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER2)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH1)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH2)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL1)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL2)) + ; + break; + } + case 2: + { //weapon, 3 sections + 3 colours, 6 values + sReturn = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, ITEM_APPR_WEAPON_COLOR_BOTTOM)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, ITEM_APPR_WEAPON_COLOR_MIDDLE)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, ITEM_APPR_WEAPON_COLOR_TOP)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, ITEM_APPR_WEAPON_MODEL_BOTTOM)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, ITEM_APPR_WEAPON_MODEL_MIDDLE)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, ITEM_APPR_WEAPON_MODEL_TOP)) + ; + break; + } + case 3: + { //armour, 19 sections + 6 colours, 25 values + sReturn = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RFOOT)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LFOOT)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RSHIN)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LSHIN)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LTHIGH)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RTHIGH)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_PELVIS)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_TORSO)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_BELT)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_NECK)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RFOREARM)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LFOREARM)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RBICEP)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LBICEP)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RSHOULDER)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LSHOULDER)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RHAND)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LHAND)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_ROBE)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER1)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER2)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH1)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH2)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL1)) + "-" + + IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL2)) + ; + break; + } + } + return sReturn; +} + +//reads a string with ints delimited by "-", then set the appearance +// of an item accordingly +object PRCSetItemAppearance(object oPC, object oItem, string sArray, string sName = PRC_CRAFT_APPEARANCE_ARRAY) +{ + //initialise array + if(array_exists(oPC, sName)) + array_delete(oPC, sName); + array_create(oPC, sName); + //initialise variables + string sTemp = sArray; + string sSub; + int nLength = GetStringLength(sTemp); + int nPosition; + int nTemp; + int nIndex = 0; + object oChest = GetTempCraftChest(); + object oTemp; + while(nLength > 0) + { + nPosition = FindSubString(sTemp, "-"); + if(nPosition == -1) + { //last value + sSub = sTemp; + nLength = 0; + } + else + { + sSub = GetStringLeft(sTemp, nPosition); + nLength -= (nPosition + 1); + } + if(sSub == "*") + nTemp = -1; + else + nTemp = StringToInt(sSub); + array_set_int(oPC, sName, nIndex, nTemp); + nIndex++; + if(nPosition == -1) break; //last value + if(nLength < 0) + { + if(DEBUG) DoDebug("PRCSetItemAppearanceString: Error processing string"); + return oItem; //something went wrong + } + sTemp = GetSubString(sTemp, nPosition + 1, nLength); + } + int nBase = GetBaseItemType(oItem); + int nModelType = StringToInt(Get2DACache("baseitems", "ModelType", nBase)); + DestroyObject(oItem); + oItem = CopyItem(oItem, oChest, TRUE); + switch(nModelType) + { + case 0: + { //simple model, 1 value + nTemp = array_get_int(oPC, sName, 0); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0, nTemp, TRUE); + } + break; + } + case 1: + { //helmet, cloak, model + 6 colours, 7 values, cloak model change doesn't work in 1.68 + nTemp = array_get_int(oPC, sName, 0); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 1); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER1, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 2); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER2, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 3); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH1, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 4); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH2, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 5); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL1, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 6); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL2, nTemp, TRUE); + } + break; + } + case 2: + { //weapon, 3 sections + 3 colours, 6 values + nTemp = array_get_int(oPC, sName, 0); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, ITEM_APPR_WEAPON_COLOR_BOTTOM, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 1); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, ITEM_APPR_WEAPON_COLOR_MIDDLE, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 2); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, ITEM_APPR_WEAPON_COLOR_TOP, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 3); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, ITEM_APPR_WEAPON_MODEL_BOTTOM, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 4); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, ITEM_APPR_WEAPON_MODEL_MIDDLE, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 5); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, ITEM_APPR_WEAPON_MODEL_TOP, nTemp, TRUE); + } + break; + } + case 3: + { //armour, 19 sections + 6 colours, 25 values + nTemp = array_get_int(oPC, sName, 0); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RFOOT, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 1); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LFOOT, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 2); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RSHIN, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 3); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LSHIN, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 4); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LTHIGH, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 5); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RTHIGH, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 6); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_PELVIS, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 7); + if(nTemp != -1) + { + if(FloatToInt(StringToFloat(Get2DACache("parts_chest", "ACBONUS", GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_TORSO)))) == FloatToInt(StringToFloat(Get2DACache("parts_chest", "ACBONUS", nTemp)))) + { //won't allow change to armour with different AC + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_TORSO, nTemp, TRUE); + } + else + { + SendMessageToPC(oPC, "This torso appearance has a different AC value to the current appearance, aborting torso change."); + } + } + nTemp = array_get_int(oPC, sName, 8); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_BELT, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 9); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_NECK, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 10); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RFOREARM, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 11); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LFOREARM, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 12); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RBICEP, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 13); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LBICEP, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 14); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RSHOULDER, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 15); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LSHOULDER, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 16); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_RHAND, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 17); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_LHAND, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 18); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_ROBE, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 19); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER1, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 20); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_LEATHER2, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 21); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH1, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 22); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_CLOTH2, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 23); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL1, nTemp, TRUE); + } + nTemp = array_get_int(oPC, sName, 24); + if(nTemp != -1) + { + DestroyObject(oItem); + oItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, ITEM_APPR_ARMOR_COLOR_METAL2, nTemp, TRUE); + } + break; + } + } + DestroyObject(oItem); + oItem = CopyItem(oItem, oPC, TRUE); + return oItem; +} +/* +int ITEM_APPR_TYPE_SIMPLE_MODEL = 0; +int ITEM_APPR_TYPE_WEAPON_COLOR = 1; +int ITEM_APPR_TYPE_WEAPON_MODEL = 2; +int ITEM_APPR_TYPE_ARMOR_MODEL = 3; +int ITEM_APPR_TYPE_ARMOR_COLOR = 4; +int ITEM_APPR_NUM_TYPES = 5; + +int ITEM_APPR_ARMOR_COLOR_LEATHER1 = 0; +int ITEM_APPR_ARMOR_COLOR_LEATHER2 = 1; +int ITEM_APPR_ARMOR_COLOR_CLOTH1 = 2; +int ITEM_APPR_ARMOR_COLOR_CLOTH2 = 3; +int ITEM_APPR_ARMOR_COLOR_METAL1 = 4; +int ITEM_APPR_ARMOR_COLOR_METAL2 = 5; +int ITEM_APPR_ARMOR_NUM_COLORS = 6; + +int ITEM_APPR_ARMOR_MODEL_RFOOT = 0; +int ITEM_APPR_ARMOR_MODEL_LFOOT = 1; +int ITEM_APPR_ARMOR_MODEL_RSHIN = 2; +int ITEM_APPR_ARMOR_MODEL_LSHIN = 3; +int ITEM_APPR_ARMOR_MODEL_LTHIGH = 4; +int ITEM_APPR_ARMOR_MODEL_RTHIGH = 5; +int ITEM_APPR_ARMOR_MODEL_PELVIS = 6; +int ITEM_APPR_ARMOR_MODEL_TORSO = 7; +int ITEM_APPR_ARMOR_MODEL_BELT = 8; +int ITEM_APPR_ARMOR_MODEL_NECK = 9; +int ITEM_APPR_ARMOR_MODEL_RFOREARM = 10; +int ITEM_APPR_ARMOR_MODEL_LFOREARM = 11; +int ITEM_APPR_ARMOR_MODEL_RBICEP = 12; +int ITEM_APPR_ARMOR_MODEL_LBICEP = 13; +int ITEM_APPR_ARMOR_MODEL_RSHOULDER = 14; +int ITEM_APPR_ARMOR_MODEL_LSHOULDER = 15; +int ITEM_APPR_ARMOR_MODEL_RHAND = 16; +int ITEM_APPR_ARMOR_MODEL_LHAND = 17; +int ITEM_APPR_ARMOR_MODEL_ROBE = 18; +int ITEM_APPR_ARMOR_NUM_MODELS = 19; + +int ITEM_APPR_WEAPON_MODEL_BOTTOM = 0; +int ITEM_APPR_WEAPON_MODEL_MIDDLE = 1; +int ITEM_APPR_WEAPON_MODEL_TOP = 2; + +int ITEM_APPR_WEAPON_COLOR_BOTTOM = 0; +int ITEM_APPR_WEAPON_COLOR_MIDDLE = 1; +int ITEM_APPR_WEAPON_COLOR_TOP = 2; +*/ + +// void main () {} diff --git a/src/include/prc_effect_inc.nss b/src/include/prc_effect_inc.nss new file mode 100644 index 0000000..6186f71 --- /dev/null +++ b/src/include/prc_effect_inc.nss @@ -0,0 +1,750 @@ +/** + * @file + * This file contains SPApplyEffectToObject(). This was in inc_dispel, but that include should only be in + * dispel-type spells and not the core spell engine. + */ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gets a creature that can apply an effect + * Useful to apply/remove specific effects rather than using spellID + * Remember to assigncommand the effect creation + */ +object GetObjectToApplyNewEffect(string sTag, object oPC, int nStripEffects = TRUE); + +/** + * A wrapper for ApplyEffectToObject() that takes PRC feats into account. + * + * @param nDurationType One of the DURATION_TYPE_* constants + * @param eEffect The effect to apply + * @param oTarget The target of the effect + * @param fDuration The duration for temporary effects + * @param bDispellable TRUE if the effect should be dispellable, else FALSE + * @param nSpellID The Spell ID of the spell applying the effect. If default is used, + * PRCGetSpellId() is used internally to get the spell ID. + * @param nCasterLevel The caster level that the spell is cast with. If default is used, + * PRCGetCasterLevel() is used internally to get the caster level. + * @param oCaster The spell caster. + */ +void SPApplyEffectToObject(int nDurationType, effect eEffect, object oTarget, float fDuration = 0.0f, + int bDispellable = TRUE, int nSpellID = -1, int nCasterLevel = -1, object oCaster = OBJECT_SELF); + +/** + * Removes all effects from target that are due to the given spell. + * + * Removes all effects caused by the spell nID regardless of caster. By + * default only removes magical effects. + * + * @author Georg Zoeller (presumably copied from a bio script somewhere) + * + * @param nID The spell ID whose effects to remove. + * @param oTarget The object to remove the effects from. + * @param bMagicalEffectsOnly Whether to remove only magical effects (TRUE, the default) + * or all effects (FALSE) + */ +void GZPRCRemoveSpellEffects(int nID,object oTarget, int bMagicalEffectsOnly = TRUE); + +/** + * Tests to make sure the data in the effect arrays still refers to an actual effect. + * + * Called from within ReorderEffects() and DispelMagicBestMod() (in inc_dispel). It's purpose + * is to verify that the effect referred to by an entry in the 3 arrays is still in effect, in + * case it has been dispelled or run out its duration since the data was put there. + * + * @param nSpellID SpellID of the effect to test + * @param oCaster Caster of the spell that caused the effectbeing tested + * @param oTarget The object whose effect arrays are being looked at. + * + * @return TRUE if the effect is still active, otherwise FALSE. + */ +int IsStillRealEffect(int nSpellID, object oCaster, object oTarget); + +/** + * Checks if target is a Frenzied Bersker with Deathless Frenzy Active + * If so removes immortality flag so that the death effect from a + * Death Spell can kill them + * + * @param oTarget Creature to test for Deathless Frenzy + */ +void DeathlessFrenzyCheck(object oTarget); + +// * Searchs through a persons effects and removes those from a particular spell by a particular caster. +// * PRC Version of a Bioware function to disable include loops +void PRCRemoveSpellEffects(int nSpell_ID, object oCaster, object oTarget); + +/** + * Dazzles the target: -1 Attack, Search, Spot, and VFX + * + * @return the Dazzle effect + */ +effect EffectDazzle(); + +/** + * Shaken effect: -2 to attack, all skills and saving throws + * + * @return the Shaken effect + */ +effect EffectShaken(); + +/** + * Fatigue effect: -2 to Strength and Dexterity, 25% speed decrease. Can't be dispelled. + * + * @return the Fatigue effect + */ +effect EffectFatigue(); + +/** + * Exhausted effect: -6 to Strength and Dexterity, 50% speed decrease. Can't be dispelled. + * + * @return the Exhausted effect + */ +effect EffectExhausted(); + +/** + * Cowering effect: -2 to AC, takes no actions (dazed) + * + * @return the Cowering effect + */ +effect EffectCowering(); + +/** + * Sickened effect: -2 to attack, damage, all skills, ability checks and saving throws + * + * @return the Sickened effect + */ +effect EffectSickened(); + +/** + * Nausea effect: Nauseated creatures are unable to attack, cast spells, concentrate on spells, or do anything + * else requiring attention. The only action such a character can take is a single move action per turn. + * This function takes a duration and target because it sets the enemy's number of attacks in a round to 1 + * Which means once it's called, the BAB effect is applied + * + * @return the Nausea effect + */ +effect EffectNausea(object oTarget, float fDur); + +/** + * Fascinate effect: charmed, -4 to Listen and Spot + * + * @return the Fascinate effect + */ +effect EffectFascinate(); + +/** + * It's Confused, as per normal + * + * @return the Confused effect + */ +effect PRCEffectConfused(); + +/** + * It's EffectHeal, but allows us to manipulate the HP total healed + * + * @return the Heal effect + */ +effect PRCEffectHeal(int nHP, object oTarget); + +/** + * Creates skill bonus for all skills based on particular ability. + * Should be updated if skills.2da file was edited. + * + * @param iAbility base ability + * @param iIncrease bonus applied to each skill + * + * @return Skill increase + */ +effect EffectAbilityBasedSkillIncrease(int iAbility, int iIncrease = 1); + +/** + * Creates skill penalty for all skills based on particular ability + * Should be updated if skills.2da file was edited. + * + * @param iAbility base ability + * @param iDecrease penalty applied to each skill + * + * @return Skill decrease + */ +effect EffectAbilityBasedSkillDecrease(int iAbility, int iDecrease = 1); + +//ebonfowl: adding this function to check if a target is already shaken +int GetIsShaken(object oTarget); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "prc_inc_castlvl" // get prc_racial_const, prc_inc_nwscript, prc_inc_newip + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** + * Cleans up the 3 arrays used to store effect information on the object. + * + * Goes through the whole 3 stored lists of caster levels, spell id's, and casters, + * deletes any that aren't real anymore (refer to an effect no longer present), then + * builds a new list out of the ones that are still real/current (refer to effects that + * are still present) + * Thus, the list gets cleaned up every time it is added to. + * + * @param nSpellID Spell ID of the spell being cast. + * @param oTarget Object to modify the effect arrays of. + * @param oCaster The caster of the spell. + * + * @return The number of effects in the 3 new arrays + */ +int _ReorderEffects(int nSpellID, object oTarget, object oCaster = OBJECT_SELF){ + int nIndex = GetLocalInt(oTarget, "X2_Effects_Index_Number"); + int nEffectCastLevel; + int nEffectSpellID; + object oEffectCaster; + int nWeave ; + int nRealIndex = 0; + int nPlace; + + for(nPlace = 0; nPlace <= nIndex; nPlace++) + { + nEffectSpellID = GetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nPlace)); + oEffectCaster = GetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nPlace)); + nEffectCastLevel = GetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nPlace)); + nWeave = GetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nPlace)); + + DeleteLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nPlace)); + DeleteLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nPlace)); + DeleteLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nPlace)); + DeleteLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nPlace)); + if(IsStillRealEffect(nEffectSpellID, oEffectCaster, oTarget)) + { + if(nEffectSpellID != nSpellID || oEffectCaster != oCaster) + // Take it out of the list if it's the spell now being cast, and by the same caster + // This way spells that don't self dispel when they're recast don't flood the list. + { + SetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nRealIndex), nEffectSpellID); + SetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nRealIndex), nEffectCastLevel); + SetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nRealIndex),oEffectCaster ); + SetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nRealIndex),nWeave); + nRealIndex++; + }// end of if is the same as the current spell and caster + }// end of if is valid effect statement + }// end of for statement + return nRealIndex; // This is the number of values currently in all 3 arrays -1. +}// end of function + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +object GetObjectToApplyNewEffect(string sTag, object oPC, int nStripEffects = TRUE) +{ + object oWP = GetObjectByTag(sTag); + object oLimbo = GetObjectByTag("HEARTOFCHAOS"); + location lLimbo = GetLocation(oLimbo); + if(!GetIsObjectValid(oLimbo)) + lLimbo = GetStartingLocation(); + //not valid, create it + if(!GetIsObjectValid(oWP)) + { + //has to be a creature so it can be jumped around + //re-used the 2da cache blueprint since it has no scripts + oWP = CreateObject(OBJECT_TYPE_CREATURE, "prc_2da_cache", lLimbo, FALSE, sTag); + if(!GetIsObjectValid(oWP) && DEBUG) + { + DoDebug(sTag+" is not valid"); + } + //make sure the player can never interact with WP + SetPlotFlag(oWP, TRUE); + SetCreatureAppearanceType(oWP, APPEARANCE_TYPE_INVISIBLE_HUMAN_MALE); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oWP); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneGhost(), oWP); + } + //remove previous effects + if(nStripEffects) + { + effect eTest = GetFirstEffect(oPC); + while(GetIsEffectValid(eTest)) + { + if(GetEffectCreator(eTest) == oWP + && GetEffectSubType(eTest) == SUBTYPE_SUPERNATURAL) + { + if(DEBUG) DoDebug("Stripping previous effect"); + RemoveEffect(oPC, eTest); + } + eTest = GetNextEffect(oPC); + } + } + //jump to PC + //must be in same area to apply effect + if(GetArea(oWP) != GetArea(oPC)) + AssignCommand(oWP, + ActionJumpToObject(oPC)); + //jump back to limbo afterwards + DelayCommand(0.1, + AssignCommand(oWP, + ActionJumpToObject(oLimbo))); + return oWP; +} + +void SPApplyEffectToObject(int nDurationType, effect eEffect, object oTarget, float fDuration = 0.0f, + int bDispellable = TRUE, int nSpellID = -1, int nCasterLevel = -1, object oCaster = OBJECT_SELF) +{ + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + //if it was cast from the new spellbook, remove previous effects + //if(GetLocalInt(OBJECT_SELF, "UsingActionCastSpell")) + // GZPRCRemoveSpellEffects(nSpellID, oTarget); + + //Fearsome Necromancy check + if(GetHasFeat(FEAT_FEARSOME_NECROMANCY, oCaster)) + { + if(GetSpellSchool(nSpellID) == SPELL_SCHOOL_NECROMANCY) + { + if(GetIsEnemy(oTarget, oCaster) + && !GetIsImmune(oTarget, IMMUNITY_TYPE_MIND_SPELLS)) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectShaken(), oTarget, 6.0); + } + } + } + + // Illusion Veil Meld + if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oCaster) && GetSpellSchool(nSpellID) == SPELL_SCHOOL_ILLUSION) + fDuration += GetEssentiaInvested(oCaster, MELD_ILLUSION_VEIL) * 6.0; + + //Eldritch Spellweave + if(GetIsObjectValid(GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))) + ExecuteScript("inv_splweave", oCaster); + + // Instant duration effects can use BioWare code, the PRC code doesn't care about those + if(DURATION_TYPE_INSTANT == nDurationType) + ApplyEffectToObject(nDurationType, eEffect, oTarget, fDuration); + else + { + // Extraordinary/Supernatural effects are not supposed to be dispellable. + if (GetEffectSubType(eEffect) == SUBTYPE_EXTRAORDINARY + || GetEffectSubType(eEffect) == SUBTYPE_SUPERNATURAL) + { + bDispellable = FALSE; + } + + // We need the extra arguments for the PRC code, get them if defaults were passed in. + if (-1 == nCasterLevel) nCasterLevel = PRCGetCasterLevel(oCaster); + + // Invoke the PRC apply function passing the extra data. + int nIndex = _ReorderEffects(nSpellID, oTarget, oCaster); + // Add this new effect to the slot after the last effect already on the character. + + //check if Master's Gift applies + if(GetHasFeat(FEAT_MASTERS_GIFT, oTarget) && GetIsArcaneClass(PRCGetLastSpellCastClass(), oCaster)) + { + if(!StringToInt(Get2DACache("spells", "HostileSetting", nSpellID))) + fDuration *= 2; + } + + ApplyEffectToObject(nDurationType, eEffect, oTarget, fDuration); + // may have code traverse the lists right here and not add the new effect + // if an identical one already appears in the list somewhere + + SetLocalInt(oTarget, " X2_Effect_Spell_ID_" + IntToString(nIndex), nSpellID); + SetLocalInt(oTarget, " X2_Effect_Cast_Level_" + IntToString(nIndex), nCasterLevel); + SetLocalObject(oTarget, " X2_Effect_Caster_" + IntToString(nIndex), oCaster ); + if(GetHasFeat(FEAT_SHADOWWEAVE, oCaster)) + SetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nIndex), GetHasFeat(FEAT_TENACIOUSMAGIC,oCaster)); + else + SetLocalInt(oTarget, " X2_Effect_Weave_ID_" + IntToString(nIndex), 0); + + //nIndex++; + /// Set new index number to the character. + SetLocalInt(oTarget, "X2_Effects_Index_Number", nIndex); + } +} + +void GZPRCRemoveSpellEffects(int nID,object oTarget, int bMagicalEffectsOnly = TRUE){ + effect eEff = GetFirstEffect(oTarget); + while (GetIsEffectValid(eEff)) + { + if (GetEffectSpellId(eEff) == nID) + { + if (GetEffectSubType(eEff) != SUBTYPE_MAGICAL && bMagicalEffectsOnly) + { + // ignore + } + else + { + RemoveEffect(oTarget,eEff); + } + } + eEff = GetNextEffect(oTarget); + } +} + +int IsStillRealEffect(int nSpellID, object oCaster, object oTarget){ + if(!GetHasSpellEffect(nSpellID, oTarget) || nSpellID == 0) + { + return FALSE; + } + + effect eTestSubject = GetFirstEffect(oTarget); + while(GetIsEffectValid(eTestSubject)) + { + if(GetEffectSpellId(eTestSubject) == nSpellID) + { + if(GetEffectCreator(eTestSubject) == oCaster) + { + return TRUE; + }// end of if originates from oCaster. + }// end if originates from nSpellID. + eTestSubject = GetNextEffect(oTarget); + } + return FALSE; +} + +void DeathlessFrenzyCheck(object oTarget) +{ + //if its immune to death, e.g via items + //then dont do this + if(GetIsImmune( oTarget, IMMUNITY_TYPE_DEATH)) + return; + if(GetHasFeat(FEAT_DEATHLESS_FRENZY, oTarget) + && GetHasFeatEffect(FEAT_FRENZY, oTarget) + && GetImmortal(oTarget)) + SetImmortal(oTarget, FALSE); + //mark them as being magically killed for death system + if(GetPRCSwitch(PRC_PNP_DEATH_ENABLE)) + { + SetLocalInt(oTarget, "PRC_PNP_EfectDeathApplied", + GetLocalInt(oTarget, "PRC_PNP_EfectDeathApplied")+1); + AssignCommand(oTarget, + DelayCommand(1.0, + SetLocalInt(oTarget, "PRC_PNP_EfectDeathApplied", + GetLocalInt(oTarget, "PRC_PNP_EfectDeathApplied")-1))); + } +} + +void PRCRemoveSpellEffects(int nSpell_ID, object oCaster, object oTarget){ + //Declare major variables + int bValid = FALSE; + effect eAOE; + if(GetHasSpellEffect(nSpell_ID, oTarget)) + { + //Search through the valid effects on the target. + eAOE = GetFirstEffect(oTarget); + while (GetIsEffectValid(eAOE) && bValid == FALSE) + { + if (GetEffectCreator(eAOE) == oCaster) + { + //If the effect was created by the spell then remove it + if(GetEffectSpellId(eAOE) == nSpell_ID) + { + RemoveEffect(oTarget, eAOE); + bValid = TRUE; + } + } + //Get next effect on the target + eAOE = GetNextEffect(oTarget); + } + } +} + +// The creature is unable to see well because of overstimulation of the eyes. A dazzled +// creature takes a -1 penalty on attack rolls, Search checks, and Spot checks. +effect EffectDazzle(){ + effect eBlank; + if (GetRacialType(PRCGetSpellTargetObject()) == RACIAL_TYPE_BHUKA) return eBlank; + + effect eReturn = EffectLinkEffects(EffectAttackDecrease(1), EffectSkillDecrease(SKILL_SEARCH, 1)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SPOT, 1)); + eReturn = TagEffect(eReturn, "PRCDazzle"); + return eReturn; +} + +// A shaken character takes a -2 penalty on attack rolls, saving throws, skill checks, +// and ability checks. +// Shaken is a less severe state of fear than frightened or panicked. +effect EffectShaken(){ + effect eBlank; + if (GetRacialType(PRCGetSpellTargetObject()) == RACIAL_TYPE_KRINTH) return eBlank; + + effect eReturn = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR); + eReturn = EffectLinkEffects(eReturn, EffectAttackDecrease(2)); + eReturn = EffectLinkEffects(eReturn, EffectSavingThrowDecrease(SAVING_THROW_ALL,2)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_ALL_SKILLS, 2)); + eReturn = TagEffect(eReturn, "PRCShaken"); + return eReturn; +} + +// A fatigued character can neither run nor charge and takes a -2 penalty to Strength +// and Dexterity. Doing anything that would normally cause fatigue causes the fatigued +// character to become exhausted. After 8 hours of complete rest, fatigued characters +// are no longer fatigued. +effect EffectFatigue(){ + effect eBlank; + if (GetLevelByClass(CLASS_TYPE_BATTLESMITH, PRCGetSpellTargetObject()) >= 3) return eBlank; + + effect eReturn = EffectAbilityDecrease(ABILITY_STRENGTH, 2); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_DEXTERITY, 2)); + eReturn = EffectLinkEffects(eReturn, EffectMovementSpeedDecrease(25)); + eReturn = TagEffect(eReturn, "PRCFatigue"); + return eReturn; +} + +// An exhausted character moves at half speed and takes a -6 penalty to Strength and +// Dexterity. After 1 hour of complete rest, an exhausted character becomes fatigued. +// A fatigued character becomes exhausted by doing something else that would normally +// cause fatigue. +effect EffectExhausted(){ + effect eBlank; + if (GetLevelByClass(CLASS_TYPE_BATTLESMITH, PRCGetSpellTargetObject()) >= 3) return eBlank; + + if (GetLocalInt(PRCGetSpellTargetObject(), "IncarnumDefenseLE")) return EffectFatigue(); + + effect eReturn = EffectAbilityDecrease(ABILITY_STRENGTH, 6); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_DEXTERITY, 6)); + eReturn = EffectLinkEffects(eReturn, EffectMovementSpeedDecrease(50)); + eReturn = TagEffect(eReturn, "PRCExhausted"); + return eReturn; +} + +// The character is frozen in fear and can take no actions. A cowering character takes +// a -2 penalty to Armor Class and loses her Dexterity bonus (if any). +effect EffectCowering(){ + effect eReturn = EffectACDecrease(2); + eReturn = EffectLinkEffects(eReturn, EffectDazed()); + eReturn = TagEffect(eReturn, "PRCCowering"); + return eReturn; +} + +// The character takes a -2 penalty on all attack rolls, weapon damage rolls, saving +// throws, skill checks, and ability checks. +effect EffectSickened(){ + effect eBlank; + if (GetHasFeat(FEAT_STRONG_STOMACH, PRCGetSpellTargetObject())) return eBlank; + + if(GetHasSpellEffect(MELD_PAULDRONS_OF_HEALTH, PRCGetSpellTargetObject())) return eBlank; + + effect eReturn = EffectAttackDecrease(2); + eReturn = EffectLinkEffects(eReturn, EffectDamageDecrease(2, DAMAGE_TYPE_BLUDGEONING|DAMAGE_TYPE_PIERCING|DAMAGE_TYPE_SLASHING)); + eReturn = EffectLinkEffects(eReturn, EffectSavingThrowDecrease(SAVING_THROW_ALL, 2)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_ALL_SKILLS, 2)); + eReturn = TagEffect(eReturn, "PRCSickened"); + return eReturn; +} + +// Nauseated creatures are unable to attack, cast spells, concentrate on spells, or do anything +// else requiring attention. The only action such a character can take is a single move action per turn. +effect EffectNausea(object oTarget, float fDur){ + effect eBlank; + if (GetHasFeat(FEAT_STRONG_STOMACH, PRCGetSpellTargetObject())) return EffectSickened(); + + if(GetHasSpellEffect(MELD_PAULDRONS_OF_HEALTH, PRCGetSpellTargetObject())) return eBlank; + + effect eReturn = EffectLinkEffects(EffectSpellFailure(), EffectAttackDecrease(20)); + eReturn = EffectLinkEffects(eReturn, EffectSlow()); + eReturn = EffectLinkEffects(EffectVisualEffect(VFX_IMP_DISEASE_S), eReturn); + eReturn = TagEffect(eReturn, "PRCNausea"); + SetBaseAttackBonus(1, oTarget); + DelayCommand(fDur, RestoreBaseAttackBonus(oTarget)); + return eReturn; +} + +// The creature is fascinated by a magical effect. A fascinate creature +// is charmed and takes a -4 penalty to listen and spot +effect EffectFascinate(){ + effect eReturn = EffectLinkEffects(EffectCharmed(), EffectSkillDecrease(SKILL_LISTEN, 4)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SPOT, 4)); + eReturn = TagEffect(eReturn, "PRCFascinate"); + return eReturn; +} + +// It's just Confused +effect PRCEffectConfused() +{ + effect eBlank; + if (GetLevelByClass(CLASS_TYPE_WILD_MAGE, PRCGetSpellTargetObject()) >= 6 || + (GetHasSpellEffect(VESTIGE_DAHLVERNAR, PRCGetSpellTargetObject()) && GetLocalInt(PRCGetSpellTargetObject(), "ExploitVestige") != VESTIGE_DAHLVERNAR_MAD_SOUL) + ) return eBlank; + + effect eEffect = EffectConfused(); + eEffect = TagEffect(eEffect, "PRCConfused"); + return eEffect; +} + +// It's just Healing +effect PRCEffectHeal(int nHP, object oTarget) +{ + // MELD_THERAPEUTIC_MANTLE + if (GetHasSpellEffect(MELD_THERAPEUTIC_MANTLE, oTarget)) + nHP += (StringToInt(Get2DACache("spells", "Innate", PRCGetSpellId())) + (GetEssentiaInvested(oTarget, MELD_THERAPEUTIC_MANTLE) * 2)); + + return EffectHeal(nHP); +} + +effect EffectAbilityBasedSkillIncrease(int iAbility, int iIncrease = 1){ + effect eReturn; + switch(iAbility) + { + case ABILITY_STRENGTH: + eReturn = EffectSkillIncrease(SKILL_DISCIPLINE, iIncrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_JUMP, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CLIMB, iIncrease)); + break; + case ABILITY_DEXTERITY: + eReturn = EffectSkillIncrease(SKILL_HIDE, iIncrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_MOVE_SILENTLY, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_OPEN_LOCK, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_SET_TRAP, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_TUMBLE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_RIDE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_BALANCE, iIncrease)); + break; + case ABILITY_CONSTITUTION: + eReturn = EffectSkillIncrease(SKILL_CONCENTRATION, iIncrease); + break; + case ABILITY_INTELLIGENCE: + eReturn = EffectSkillIncrease(SKILL_DISABLE_TRAP, iIncrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_LORE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_SEARCH, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_SPELLCRAFT, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_APPRAISE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CRAFT_TRAP, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CRAFT_ARMOR, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CRAFT_WEAPON, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_TRUESPEAK, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_MARTIAL_LORE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CRAFT_ALCHEMY, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CRAFT_POISON, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_PSICRAFT, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_CRAFT_GENERAL, iIncrease)); + break; + case ABILITY_WISDOM: + eReturn = EffectSkillIncrease(SKILL_HEAL, iIncrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_LISTEN, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_SPOT, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_SENSE_MOTIVE, iIncrease)); + break; + case ABILITY_CHARISMA: + eReturn = EffectSkillIncrease(SKILL_ANIMAL_EMPATHY, iIncrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_PERFORM, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_PERSUADE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_USE_MAGIC_DEVICE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_BLUFF, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_INTIMIDATE, iIncrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillIncrease(SKILL_IAIJUTSU_FOCUS, iIncrease)); + break; + } + return eReturn; +} + +effect EffectAbilityBasedSkillDecrease(int iAbility, int iDecrease = 1){ + effect eReturn; + switch(iAbility) + { + case ABILITY_STRENGTH: + eReturn = EffectSkillDecrease(SKILL_DISCIPLINE, iDecrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_JUMP, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CLIMB, iDecrease)); + break; + case ABILITY_DEXTERITY: + eReturn = EffectSkillIncrease(SKILL_HIDE, iDecrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_MOVE_SILENTLY, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_OPEN_LOCK, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SET_TRAP, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_TUMBLE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_RIDE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_BALANCE, iDecrease)); + break; + case ABILITY_CONSTITUTION: + eReturn = EffectSkillDecrease(SKILL_CONCENTRATION, iDecrease); + break; + case ABILITY_INTELLIGENCE: + eReturn = EffectSkillDecrease(SKILL_DISABLE_TRAP, iDecrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_LORE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SEARCH, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SPELLCRAFT, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_APPRAISE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CRAFT_TRAP, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CRAFT_ARMOR, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CRAFT_WEAPON, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_TRUESPEAK, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_MARTIAL_LORE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CRAFT_ALCHEMY, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CRAFT_POISON, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_PSICRAFT, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_CRAFT_GENERAL, iDecrease)); + break; + case ABILITY_WISDOM: + eReturn = EffectSkillDecrease(SKILL_HEAL, iDecrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_LISTEN, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SPOT, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_SENSE_MOTIVE, iDecrease)); + break; + case ABILITY_CHARISMA: + eReturn = EffectSkillDecrease(SKILL_ANIMAL_EMPATHY, iDecrease); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_PERFORM, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_PERSUADE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_USE_MAGIC_DEVICE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_BLUFF, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_INTIMIDATE, iDecrease)); + eReturn = EffectLinkEffects(eReturn, EffectSkillDecrease(SKILL_IAIJUTSU_FOCUS, iDecrease)); + break; + } + return eReturn; +} + +effect EffectDamageImmunityAll(){ + effect eReturn = EffectDamageImmunityIncrease(DAMAGE_TYPE_ACID, 100); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_BLUDGEONING, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_COLD, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_DIVINE, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_ELECTRICAL, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_FIRE, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_MAGICAL, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_NEGATIVE, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_PIERCING, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_POSITIVE, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_SLASHING, 100)); + eReturn = EffectLinkEffects(eReturn, EffectDamageImmunityIncrease(DAMAGE_TYPE_SONIC, 100)); + eReturn = TagEffect(eReturn, "PRCDamageImmunityAll"); + return eReturn; +} + +effect EffectImmunityMiscAll(){ + effect eReturn = EffectImmunity(IMMUNITY_TYPE_ABILITY_DECREASE); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_BLINDNESS)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_DEAFNESS)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_CRITICAL_HIT)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_DEATH)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_DISEASE)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_ENTANGLE)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_SLOW)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_KNOCKDOWN)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_NEGATIVE_LEVEL)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_PARALYSIS)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_SILENCE)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_SNEAK_ATTACK)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_TRAP)); + eReturn = EffectLinkEffects(eReturn, EffectImmunity(IMMUNITY_TYPE_MIND_SPELLS)); + eReturn = TagEffect(eReturn, "PRCImmunityMiscAll"); + return eReturn; +} + +int GetIsShaken(object oTarget) +{ + effect eEffect = GetFirstEffect(oTarget); + + string sTag; + + while (GetIsEffectValid(eEffect)) + { + sTag = GetEffectTag(eEffect); + + if (sTag == "PRCShaken") return TRUE; + + eEffect = GetNextEffect(oTarget); + } + return FALSE; +} \ No newline at end of file diff --git a/src/include/prc_feat_const.nss b/src/include/prc_feat_const.nss new file mode 100644 index 0000000..c961dab --- /dev/null +++ b/src/include/prc_feat_const.nss @@ -0,0 +1,6264 @@ +// The error code 5 prevention entry. Comment out or uncomment as necessary +//const int COMPILER_BREAKS_ON_ME_OR_NOT = 0xffffffff; + +//:: PRC Options Conversation +const int FEAT_OPTIONS_CONVERSATION = 2285; + +//:: Missing Bioware Feats +const int FEAT_EPIC_PLANAR_TURNING = 854; + +//:: New Favored Enemy Feats +const int FEAT_FAVORED_ENEMY_OOZE = 2354; +const int FEAT_FAVORED_ENEMY_PLANT = 2355; + +//:: Epic Base Class Marker Feats +const int FEAT_EPIC_DUSKBLADE = 3690; +const int FEAT_EPIC_DREAD_NECROMANCER = 24152; +const int FEAT_EPIC_FAVORED_SOUL = 24241; +const int FEAT_EPIC_HEALER = 3744; +const int FEAT_EPIC_MARSHAL = 1918; +const int FEAT_EPIC_SHAMAN = 24240; +const int FEAT_EPIC_SWASHBUCKLER = 1917; +const int FEAT_EPIC_WARLOCK = 24239; +const int FEAT_EPIC_WARMAGE = 24242; +const int FEAT_EPIC_BINDER = 25002; +const int FEAT_EPIC_SHUGENJA = 25107; + +//:: Epic Presitge Class Marker Feats +const int FEAT_EPIC_ARCANE_TRICKSTER = 4354; +const int FEAT_EPIC_ELDRITCH_KNIGHT = 4013; +const int FEAT_EPIC_ELDRITCH_THEURGE = 23516; +const int FEAT_EPIC_FRENZIED_BERZERKER = 5469; +const int FEAT_EPIC_LASHER = 4086; +const int FEAT_EPIC_OCULAR = 3425; +//const int FEAT_EPIC_RAVAGER = 2395; //Dupe +const int FEAT_EPIC_RED_AVENGER = 2753; +const int FEAT_EPIC_TEMPEST = 5470; + +const int FEAT_EPIC_EYE_OF_GRUUMSH = 25000; +const int FEAT_EPIC_UR_PRIEST = 25001; +const int FEAT_EPIC_ANIMA_MAGE = 25003; +const int FEAT_EPIC_SERENE_GUARDIAN = 25004; +const int FEAT_EPIC_SPELLFIRE_CHANNELER = 25005; +const int FEAT_EPIC_VIRTUOSO = 25006; +const int FEAT_EPIC_SPELLSWORD = 25007; +const int FEAT_EPIC_AOTS = 25008; +const int FEAT_EPIC_UNSEEN_SEER = 25009; +const int FEAT_EPIC_CELEBRANT_OF_SHARESS = 25010; +const int FEAT_EPIC_FORSAKER = 25011; +const int FEAT_EPIC_KOTC = 25012; +const int FEAT_EPIC_HATHRAN = 25013; +const int FEAT_EPIC_ISFM = 25014; +const int FEAT_EPIC_STORMLORD = 25015; +const int FEAT_EPIC_HEARTWARDER = 25016; +const int FEAT_EPIC_FIST_RAZIEL = 25017; +const int FEAT_EPIC_VASSAL = 25018; +const int FEAT_EPIC_SUBLIME_CHORD = 25019; +const int FEAT_EPIC_ARCANE_DUELIST = 25020; +const int FEAT_EPIC_WILD_MAGE = 25021; +const int FEAT_EPIC_SHADOWSMITH = 25022; +const int FEAT_EPIC_OOZEMASTER = 25023; +const int FEAT_EPIC_KOTW = 25024; +const int FEAT_EPIC_DROW_JUDICATOR = 25025; +const int FEAT_EPIC_SHADOWBANE_INQUISITOR = 25026; +const int FEAT_EPIC_SHADOWBANE_STALKER = 25027; +const int FEAT_EPIC_UMBRAL_DISCIPLE = 25028; +const int FEAT_EPIC_ALIENIST = 25029; +const int FEAT_EPIC_BLACK_BLOOD_CULTIST = 25030; +const int FEAT_EPIC_FOCHLUCAN_LYRIST = 25031; +const int FEAT_EPIC_SPINEMELD_WARRIOR = 25032; +const int FEAT_EPIC_NIGHTSHADE = 25033; +const int FEAT_EPIC_SHADOW_ADEPT = 25034; +const int FEAT_EPIC_SOLDIER_OF_LIGHT = 25035; +const int FEAT_EPIC_SAPPHIRE_HIERARCH = 25036; +const int FEAT_EPIC_BONDED_SUMMONER = 25037; +const int FEAT_EPIC_IODM = 25038; +const int FEAT_EPIC_BATTLEGUARD_OF_TEMPUS = 25039; +const int FEAT_EPIC_BLADESINGER = 25040; +const int FEAT_EPIC_SOULCASTER = 25041; +const int FEAT_EPIC_SACRED_FIST = 25042; +const int FEAT_EPIC_LEGENDARY_DREADNOUGHT = 25043; +const int FEAT_EPIC_DISCIPLE_OF_BAALZEBUL = 25044; +const int FEAT_EPIC_MIGHTY_CONTENDER_OF_KORD = 25045; +const int FEAT_EPIC_IAIJUTSU_MASTER = 25046; +const int FEAT_EPIC_DISCIPLE_OF_DISPATER = 25047; +const int FEAT_EPIC_RAVAGER = 25048; +const int FEAT_EPIC_RUNESCARRED_BERZERKER = 25049; +const int FEAT_EPIC_TALONTAR_BLIGHTLORD = 25050; +const int FEAT_EPIC_CHILD_OF_NIGHT = 25051; +const int FEAT_EPIC_MASTER_OF_SHADOW = 25052; +const int FEAT_EPIC_NOCTUMANCER = 25053; +const int FEAT_EPIC_TOTEM_RAGER = 25054; +const int FEAT_EPIC_SHADOWBLADE = 25055; +const int FEAT_EPIC_DRAGON_SHAMAN = 25056; +const int FEAT_EPIC_DRAGONFIRE_ADEPT = 25057; +const int FEAT_EPIC_RED_WIZARD = 25058; +const int FEAT_EPIC_TRUE_NECRO = 25059; +const int FEAT_EPIC_BLOOD_MAGUS = 25060; +const int FEAT_EPIC_DIABOLIST = 25061; +const int FEAT_EPIC_INCANDESCENT_CHAMPION = 25062; +const int FEAT_EPIC_JOWAW = 25063; +const int FEAT_EPIC_ACOLYTE_EGO = 25064; +const int FEAT_EPIC_PEERLESS_ARCHER = 25065; +const int FEAT_EPIC_THRALL_OF_ORCUS = 25066; +const int FEAT_EPIC_BFZ = 25067; +const int FEAT_EPIC_KOTMC = 25068; +const int FEAT_EPIC_SPIRIT_SHAMAN = 25069; +const int FEAT_EPIC_HOSPITALER = 25070; +const int FEAT_EPIC_MOS = 25071; +const int FEAT_EPIC_MASTER_HARPER = 25072; +const int FEAT_EPIC_FOE_HUNTER = 25073; +const int FEAT_EPIC_THRALL_OF_GRAZZT = 25074; +const int FEAT_EPIC_ELDRITCH_DISCIPLE = 25075; +const int FEAT_EPIC_GHOST_FACED_KILLER = 25076; +const int FEAT_EPIC_ULTIMATE_MAGUS = 25077; +const int FEAT_EPIC_FOREST_MASTER = 25078; +const int FEAT_EPIC_JADE_PHOENIX_MAGE = 25079; +const int FEAT_EPIC_RUBY_KNIGHT_VINDICATOR = 25080; +const int FEAT_EPIC_ETERNAL_BLADE = 25081; +const int FEAT_EPIC_SHADOW_SUN_NINJA = 25082; +const int FEAT_EPIC_WITCHBORN_BINDER = 25083; +const int FEAT_EPIC_DISCIPLE_MEPHISTO = 25084; +const int FEAT_EPIC_SOUL_EATER = 25085; +const int FEAT_EPIC_HENSHIN_MYSTIC = 25086; +const int FEAT_EPIC_DRUNKEN_MASTER = 25087; +const int FEAT_EPIC_ENLIGHTENED_FIST = 25088; +//const int FEAT_EPIC_MORNINGLORD = 25089; //Dupe +const int FEAT_EPIC_PYROKINETICIST = 25090; +const int FEAT_EPIC_SHADOWMIND = 25091; +const int FEAT_EPIC_PSYCHIC_THEURGE = 25092; +const int FEAT_EPIC_CEREBREMANCER = 25093; +const int FEAT_EPIC_THRALLHERD = 25094; +const int FEAT_EPIC_FIST_OF_ZOUKEN = 25095; +const int FEAT_EPIC_CONTEMPLATIVE = 25096; +const int FEAT_EPIC_RUNECASTER = 25097; +const int FEAT_EPIC_WARCHIEF = 25098; +const int FEAT_EPIC_WAR_MIND = 25099; +const int FEAT_EPIC_IRON_MIND = 25100; +const int FEAT_EPIC_SLAYER_OF_DOMIEL = 25101; +const int FEAT_EPIC_DISCIPLE_OF_ASMODEUS = 25102; +const int FEAT_EPIC_SUEL_ARCHANAMACH = 25103; +const int FEAT_EPIC_SKULLCLAN_HUNTER = 25104; +const int FEAT_EPIC_MASTER_ALCHEMIST = 25105; +const int FEAT_EPIC_BRIMSTONE_SPEAKER = 25106; +const int FEAT_EPIC_HOTWM = 25108; +const int FEAT_EPIC_TALON_OF_TIAMAT = 25109; +const int FEAT_EPIC_FROST_MAGE = 25110; +const int FEAT_EPIC_BLIGHTER = 25111; +const int FEAT_EPIC_RAGE_MAGE = 25112; +const int FEAT_EPIC_DRAGONHEART_MAGE = 25113; +const int FEAT_EPIC_SWIFT_WING = 25114; +const int FEAT_EPIC_DIAMOND_DRAGON = 25115; +const int FEAT_EPIC_CRUSADER = 25116; +const int FEAT_EPIC_SWORDSAGE = 25117; +const int FEAT_EPIC_WARBLADE = 25118; + +//:: Vile Martial Strike Expansion +const int FEAT_VILE_MARTIAL_EAGLE_CLAW = 24800; +const int FEAT_VILE_MARTIAL_LIGHT_LANCE = 24801; +const int FEAT_VILE_MARTIAL_HEAVY_PICK = 24802; +const int FEAT_VILE_MARTIAL_LIGHT_PICK = 24803; +const int FEAT_VILE_MARTIAL_SAI = 24804; +const int FEAT_VILE_MARTIAL_NUNCHAKU = 24805; +const int FEAT_VILE_MARTIAL_FALCHION = 24806; +const int FEAT_VILE_MARTIAL_SAP = 24807; +const int FEAT_VILE_MARTIAL_KATAR = 24808; +const int FEAT_VILE_MARTIAL_HEAVY_MACE = 24809; +const int FEAT_VILE_MARTIAL_MAUL = 24810; +const int FEAT_VILE_MARTIAL_DBL_SCIMITAR = 24811; +const int FEAT_VILE_MARTIAL_GOAD = 24812; +const int FEAT_VILE_MARTIAL_ELVEN_LIGHTBLADE = 24813; +const int FEAT_VILE_MARTIAL_ELVEN_THINBLADE = 24814; +const int FEAT_VILE_MARTIAL_ELVEN_COURTBLADE = 24815; + +//:: Sanctify Martial Strike Expansion +const int FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW = 24850; +const int FEAT_SANCTIFY_MARTIAL_LIGHT_LANCE = 24851; +const int FEAT_SANCTIFY_MARTIAL_HEAVY_PICK = 24852; +const int FEAT_SANCTIFY_MARTIAL_LIGHT_PICK = 24853; +const int FEAT_SANCTIFY_MARTIAL_SAI = 24854; +const int FEAT_SANCTIFY_MARTIAL_NUNCHAKU = 24855; +const int FEAT_SANCTIFY_MARTIAL_FALCHION = 24856; +const int FEAT_SANCTIFY_MARTIAL_SAP = 24857; +const int FEAT_SANCTIFY_MARTIAL_KATAR = 24858; +const int FEAT_SANCTIFY_MARTIAL_HEAVY_MACE = 24859; +const int FEAT_SANCTIFY_MARTIAL_MAUL = 24860; +const int FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR = 24861; +const int FEAT_SANCTIFY_MARTIAL_GOAD = 24862; +const int FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE = 24863; +const int FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE = 24864; +const int FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE = 24865; + +//:: Web Enhancement Feats +const int FEAT_CHARMING_THE_ARROW = 25998; + +//:: Skill Based Feats +const int FEAT_JUMP = 2884; + +//:: Racial Feats +const int FEAT_WEMIC_JUMP_8 = 4518; +const int FEAT_URDINNIR_STONESKIN = 4644; +const int FEAT_AVARIEL_DIVE = 4645; +const int FEAT_AYUAN_FEAR = 4646; +const int FEAT_AYUAN_ENTANGLE = 4647; +const int FEAT_AYUAN_DARKNESS = 4648; +const int FEAT_AYUAN_NEUTRALIZE_POISON = 4649; +const int FEAT_AASIMAR_LIGHT = 4650; +const int FEAT_TIEFLING_DARK = 4651; +const int FEAT_SVIRF_BLIND_DEAF = 4652; +const int FEAT_SVIRF_BLUR = 4653; +const int FEAT_SVIRF_DISGUISE = 4654; +const int FEAT_SVIRF_REVERT = 4655; +const int FEAT_RAK_DISGUISE = 4656; +const int FEAT_RAK_CHANGE_SHAPE = 4657; +const int FEAT_PYUAN_CHARM = 4658; +const int FEAT_PYUAN_DARKNESS = 4659; +const int FEAT_PYUAN_ENTANGLE = 4660; +const int FEAT_PYUAN_FEAR = 4661; +const int FEAT_PIXIE_INVIS = 4662; +const int FEAT_PIXIE_CONFUSE = 4663; +const int FEAT_PIXIE_ENTANGLE = 4664; +const int FEAT_PIXIE_DISPEL_MAGIC = 4665; +const int FEAT_PIXIE_POLYMORPH = 4666; +const int FEAT_PIXIE_REVERT = 4667; +const int FEAT_HYBSIL_MIRROR_IMAGE = 5382; +const int FEAT_HYBSIL_DANCLIGHTS = 5383; +const int FEAT_HYBSIL_JUMP = 5384; +const int FEAT_MINOTAUR_CHARGE = 4668; +const int FEAT_ILLITHID_CHARM = 4669; +const int FEAT_ILLITHID_BLAST = 4670; +const int FEAT_ILLITHID_SUCKBRAIN = 4671; +const int FEAT_GITHZERAI_DAZE = 4672; +const int FEAT_GITHYANKI_DAZE = 4673; +const int FEAT_GITHYANKI_BLUR = 4674; +const int FEAT_FEYRI_CHARMPERSON = 4675; +const int FEAT_FEYRI_CLAIRAUDIENCE = 4676; +const int FEAT_FEYRI_DARKNESS = 4677; +const int FEAT_FEYRI_ENERVATION = 4678; +const int FEAT_FEYRI_ALTER = 4679; +const int FEAT_FEYRI_CHANGE_SHAPE = 4680; +const int FEAT_DUERGAR_INVIS = 4681; +const int FEAT_DROW_DARKNESS = 4682; +const int FEAT_DROW_FAERIE_FIRE = 4683; +const int FEAT_WGNOME_MAGEHAND = 4519; +const int FEAT_WGNOME_SILENCE = 4555; +const int FEAT_HOUND_AURAMENACE = 4548; +const int FEAT_HOUND_AID = 4574; +const int FEAT_HOUND_CONTFLAME = 4514; +const int FEAT_HOUND_DETECTEVIL = 4618; +const int FEAT_HOUND_DISGUISE = 4613; +const int FEAT_HOUND_MAGICCIRCLE = 4515; +const int FEAT_HOUND_TELEPORT = 4516; +const int FEAT_DRIDER_DETECTGOOD = 4543; +const int FEAT_DRIDER_DETECTLAW = 4544; +const int FEAT_RACIAL_SNEAK_1D6 = 4545; + +const int FEAT_RACE_ENERGYRAY = 4856; +const int FEAT_RACE_ENERGYRAYSONIC = 4520; +const int FEAT_ELAN_RESISTANCE = 4521; +const int FEAT_ELAN_RESILIANCE = 4522; +const int FEAT_PSIRACE_STOMP = 4524; +const int FEAT_MAENAD_OUTBURST = 4525; +const int FEAT_XEPH_SPELLHARD = 4527; +const int FEAT_XEPH_BURST = 4526; + +const int FEAT_ZENYTH_TRUESTRIKE = 4536; +const int FEAT_TULADH_MAGIC_CIRCLE = 4533; +const int FEAT_NATHRI_EXP_RETREAT = 4534; +const int FEAT_METAL_HIDE = 4538; +const int FEAT_BLADELING_RAZOR_STORM = 4539; +const int FEAT_NATURAL_SPIKES = 5376; + +const int FEAT_KAPAK_SALIVA = 4546; +const int FEAT_IRDA_FLARE = 4551; +const int FEAT_IRDA_CHANGE_SHAPE = 4562; + +const int FEAT_NIXIE_CHARMPERSON = 4597; +const int FEAT_NIXIE_WATERBREATHING = 4641; +const int FEAT_NYMPH_DIMENSION_DOOR = 4755; +const int FEAT_NYMPH_STUNNING_GLANCE = 4684; +const int FEAT_NYMPH_BLINDING_BEAUTY = 4685; +const int FEAT_GRIG_PYROTECHNICS = 4686; +const int FEAT_GRIG_INVIS = 4687; +const int FEAT_GRIG_ENTANGLE = 4635; +const int FEAT_BRALANI_LIGHTNING_BOLT = 4995; +const int FEAT_BRALANI_CURE_SERIOUS = 4996; +const int FEAT_BRALANI_BLUR = 4997; +const int FEAT_BRALANI_CHARM_PERSON = 4998; +const int FEAT_BRALANI_MIRROR_IMAGE = 4999; +const int FEAT_BRALANI_GUST_OF_WIND = 4978; + +const int FEAT_ASHERATI_BODY_DAZZLE = 5213; //:: Asherati +const int FEAT_ASHERATI_BODY_LAMP = 5214; + +const int FEAT_MUCK_SQUIRT = 5425; //:: Muckdweller + +const int FEAT_ARANEA_ALTFORM = 5426; //:: Aranea +const int FEAT_ARANEA_WEB = 5427; + +const int FEAT_SECONDARY_ATTACKER = 5428; //:: Animal Companions + +const int FEAT_MEPHLING_BREATH = 5429; //:: Mephlings +const int FEAT_AIR_MEPHLING = 5430; +const int FEAT_EARTH_MEPHLING = 5431; +const int FEAT_FIRE_MEPHLING = 5432; +const int FEAT_WATER_MEPHLING = 5433; + +const int FEAT_FOG_CLOUD_BREATH = 5434; //:: Spiretop Dragon +const int FEAT_LTSENSE = 4700; +const int FEAT_LTBLIND = 4701; + +const int FEAT_SPELL5 = 4702; +const int FEAT_SPELL10 = 4420; +const int FEAT_SPELL11 = 4703; +const int FEAT_SPELL13 = 4704; +const int FEAT_SPELL14 = 4705; +const int FEAT_SPELL15 = 4706; +const int FEAT_SPELL18 = 4707; +const int FEAT_SPELL25 = 4708; +const int FEAT_SPELL27 = 4709; + +const int FEAT_DWARVEN = 4710; +const int FEAT_ELVEN = 4711; +const int FEAT_GNOMISH = 4712; +const int FEAT_HALFLING = 4713; +const int FEAT_ORCISH = 4714; +const int FEAT_HUMAN = 4715; +const int FEAT_OUTSIDER = 4716; +const int FEAT_GIANT = 4717; +const int FEAT_FEY = 4718; +const int FEAT_GOBLINOID = 4719; +const int FEAT_MONSTEROUS = 4720; +const int FEAT_REPTILIAN = 4721; +const int FEAT_ABERRATION = 4722; +const int FEAT_ELEMENTAL = 4759; +const int FEAT_UNDEAD = 4769; +const int FEAT_BEAST = 4770; +const int FEAT_VERMIN = 4771; +const int FEAT_DRAGON = 4773; +const int FEAT_PLANT = 4542; +const int FEAT_SHAPECHANGER = 4550; + +const int FEAT_HARD_AIR = 4723; +const int FEAT_HARD_EARTH = 4724; +const int FEAT_HARD_FIRE = 4725; +const int FEAT_HARD_WATER = 4726; +const int FEAT_HARD_ELEC = 4727; +const int FEAT_POISON_3 = 4728; +const int FEAT_POISON_4 = 4768; +const int FEAT_ACCLIMATED_FIRE = 4523; +const int FEAT_PARTIAL_PIERCE_IMMUNE = 4541; + +const int FEAT_IMM_COLD = 4729; +const int FEAT_IMM_PHANT = 4730; +const int FEAT_NONDETECTION = 4731; +const int FEAT_IMM_APOI = 4732; +const int FEAT_PLANT_IMM = 4540; + +const int FEAT_NATARM_1 = 4733; +const int FEAT_NATARM_2 = 4761; +const int FEAT_NATARM_3 = 4734; +const int FEAT_NATARM_4 = 4735; +const int FEAT_NATARM_5 = 4736; +const int FEAT_NATARM_6 = 4737; +const int FEAT_NATARM_7 = 4764; +const int FEAT_NATARM_8 = 4765; +const int FEAT_NATARM_9 = 4738; +const int FEAT_NATARM_10 = 4766; + +const int FEAT_BREATHLESS = 4739; +const int FEAT_CONSTRICT = 4740; +const int FEAT_REGEN5 = 4741; +const int FEAT_REND = 4742; +const int FEAT_RESIST_FIRE5 = 4743; +const int FEAT_SUFFOCATION = 4744; +const int FEAT_VERYHEROIC = 4745; +const int FEAT_VULN_COLD = 4746; +const int FEAT_DAM_RED10 = 4747; +const int FEAT_DAM_RED15 = 4748; +const int FEAT_DAM_RED5 = 4537; +const int FEAT_RESIST_COLD_10 = 4776; +const int FEAT_RESIST_FIRE_10 = 4788; + +const int FEAT_SA_HIDE = 4767; +const int FEAT_SA_MOVE = 4553; +const int FEAT_SA_HIDEU = 4749; +const int FEAT_SA_HIDEF = 4750; +const int FEAT_SA_MOVE4 = 4751; +const int FEAT_SA_JUMP = 4752; +const int FEAT_SA_BLUFF = 4753; +const int FEAT_SA_HIDE4 = 4754; +const int FEAT_SA_CRFTARM = 4757; +const int FEAT_SA_CRFTWEAP = 4758; +const int FEAT_SA_CRFTTRAP = 4517; +const int FEAT_SA_SPOT_4 = 4762; +const int FEAT_SA_JUMP_4 = 4763; +const int FEAT_LEAP = 4772; +const int FEAT_KEEN_SIGHT = 4774; +const int FEAT_SA_APPRAISE = 4792; +const int FEAT_SA_BALANCE = 4511; +const int FEAT_SA_PERSUADE = 4527; +const int FEAT_SA_SENSE_MOTIVE = 4532; +const int FEAT_SA_TUMBLE = 4535; +const int FEAT_SA_INTIMIDATE = 4547; +const int FEAT_SA_PICKPOCKET = 4552; +const int FEAT_SA_HIDE_TROG = 23550; + +const int FEAT_LARGE = 4760; +const int FEAT_AZER_HEAT = 4790; +const int FEAT_SVIRFNEBLIN_DODGE = 4791; +const int FEAT_WATER_BREATHING = 4793; +const int FEAT_ABILITY_SCENT = 4795; + +const int FEAT_CHAMELEON = 4576; +const int FEAT_BONUS_RIVER = 4578; +const int FEAT_BONUS_BAMBOO = 4577; +const int FEAT_BONUS_SEA = 4579; +const int FEAT_SA_LISTEN_4 = 4581; +const int FEAT_SA_BLUFF_4 = 4582; +const int FEAT_RACIAL_SNEAK_6D6 = 4583; +const int FEAT_SUBDUAL = 4584; +const int FEAT_SUBDUAL_ELEMENTS = 4585; +const int FEAT_LESSER_FEY_DR = 4586; +const int FEAT_FEY_DR = 4587; +const int FEAT_POORHEARING = 4588; +const int FEAT_MINUS_PERSUADE_2 = 4589; +const int FEAT_SA_HIDEF_5 = 4590; +const int FEAT_DAYLIGHTADAPT = 4592; +const int FEAT_UNEARTHLY_GRACE = 4593; +const int FEAT_LACKOFFOCUS = 4594; +const int FEAT_PSA_LORESPELL = 4595; +const int FEAT_SA_PERFORM_4 = 4596; +const int FEAT_TINY = 4614; +const int FEAT_SPELL8 = 4615; +const int FEAT_SPELL16 = 4616; +const int FEAT_SPELL17 = 4617; +const int FEAT_SPELL19 = 4619; +const int FEAT_SPELL20 = 4620; +const int FEAT_SPELL21 = 4621; +const int FEAT_SPELL22 = 4622; +const int FEAT_SPELL23 = 4623; +const int FEAT_NATARM_11 = 4624; +const int FEAT_NATARM_12 = 4625; +const int FEAT_NATARM_13 = 4626; +const int FEAT_NATARM_14 = 4627; +const int FEAT_NATARM_15 = 4628; +const int FEAT_NATARM_16 = 4629; +const int FEAT_NATARM_17 = 4630; +const int FEAT_NATARM_18 = 4631; +const int FEAT_NATARM_19 = 4632; +const int FEAT_LIFEPATH = 4636; +const int FEAT_CRAFTGUILD = 4637; +const int FEAT_TECHGUILD = 4638; +const int FEAT_SAGEGUILD = 4639; +const int FEAT_VOWOFSILENCE = 4640; +const int FEAT_COWARDPITY = 4642; +const int FEAT_IMMUNE_PETRIFICATION = 4643; +const int FEAT_BONUS_SONIC_4 = 4686; +const int FEAT_BONUS_PSIONICS_4 = 4687; +const int FEAT_SA_PERFORM = 4756; +const int FEAT_THRIKREEN_LEAP = 4775; +const int FEAT_SA_ANIMAL_EMP = 4777; +const int FEAT_SA_ANIMAL_EMP_6 = 4778; +const int FEAT_KENDERBLUFF = 4779; +const int FEAT_CLAUSTROPHOBIC = 4780; +const int FEAT_IMMUNE_ACID = 4782; +const int FEAT_IMMUNE_CHARM = 4783; +const int FEAT_IMMUNE_CONFUSION = 4784; +const int FEAT_IMMUNE_DISEASE = 4785; +const int FEAT_IMMUNE_ELECTRICITY = 4786; +const int FEAT_VULN_FIRE = 4787; +const int FEAT_FROSTYTOUCH = 4789; +const int FEAT_SA_OPEN = 4794; +const int FEAT_NATPSIONIC_1 = 4796; +const int FEAT_NATPSIONIC_2 = 4797; +const int FEAT_NATPSIONIC_3 = 4798; +const int FEAT_HUGE = 4799; +const int FEAT_SMALL = 375; +const int FEAT_RACE_POWERFUL_BUILD = 4512; +const int FEAT_DRAGON_TRAINING = 4549; +const int FEAT_TOUCH_OF_LUCK = 4634; +const int FEAT_FORESTLORD_TREEWALK = 4633; +const int FEAT_KALASHTAR_PP = 5205; + +const int FEAT_RACE_HARDINESS_VS_DISEASE = 26400; +const int FEAT_NEZUMI_IMMUNE_SHADOWLANDS = 26401; +const int FEAT_NEZUMI_KEEN_SCENT = 26402; + +//:: PnP Weapon Proficiencies +const int FEAT_WEAPON_PROFICIENCY_SHORTSWORD = 7901; +const int FEAT_WEAPON_PROFICIENCY_LONGSWORD = 7902; +const int FEAT_WEAPON_PROFICIENCY_BATTLEAXE = 7903; +const int FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD = 7904; +const int FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL = 7905; +const int FEAT_WEAPON_PROFICIENCY_WARHAMMER = 7906; +const int FEAT_WEAPON_PROFICIENCY_LONGBOW = 7907; +const int FEAT_WEAPON_PROFICIENCY_LIGHT_MACE = 7908; +const int FEAT_WEAPON_PROFICIENCY_HALBERD = 7909; +const int FEAT_WEAPON_PROFICIENCY_SHORTBOW = 7910; +const int FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD = 7911; +const int FEAT_WEAPON_PROFICIENCY_GREATSWORD = 7912; +const int FEAT_WEAPON_PROFICIENCY_GREATAXE = 7913; +const int FEAT_WEAPON_PROFICIENCY_DART = 7914; +const int FEAT_WEAPON_PROFICIENCY_DIRE_MACE = 7915; +const int FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE = 7916; +const int FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL = 7917; +const int FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER = 7918; +const int FEAT_WEAPON_PROFICIENCY_HANDAXE = 7919; +const int FEAT_WEAPON_PROFICIENCY_KAMA = 7920; +const int FEAT_WEAPON_PROFICIENCY_KATANA = 7921; +const int FEAT_WEAPON_PROFICIENCY_KUKRI = 7922; +const int FEAT_WEAPON_PROFICIENCY_MORNINGSTAR = 7923; +const int FEAT_WEAPON_PROFICIENCY_RAPIER = 7924; +const int FEAT_WEAPON_PROFICIENCY_SCIMITAR = 7925; +const int FEAT_WEAPON_PROFICIENCY_SCYTHE = 7926; +const int FEAT_WEAPON_PROFICIENCY_SHORTSPEAR = 7927; +const int FEAT_WEAPON_PROFICIENCY_SHURIKEN = 7928; +const int FEAT_WEAPON_PROFICIENCY_SICKLE = 7929; +const int FEAT_WEAPON_PROFICIENCY_SLING = 7930; +const int FEAT_WEAPON_PROFICIENCY_THROWING_AXE = 7931; +const int FEAT_WEAPON_PROFICIENCY_TRIDENT = 7932; +const int FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE = 7933; +const int FEAT_WEAPON_PROFICIENCY_WHIP = 7934; +const int FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE = 7935; +const int FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE = 7936; +const int FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE = 7937; +const int FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE = 7938; +const int FEAT_WEAPON_PROFICIENCY_HEAVY_PICK = 7939; +const int FEAT_WEAPON_PROFICIENCY_LIGHT_PICK = 7940; +const int FEAT_WEAPON_PROFICIENCY_SAI = 7941; +const int FEAT_WEAPON_PROFICIENCY_NUNCHAKU = 7942; +const int FEAT_WEAPON_PROFICIENCY_FALCHION = 7943; +const int FEAT_WEAPON_PROFICIENCY_SAP = 7944; +const int FEAT_WEAPON_PROFICIENCY_KATAR = 7945; +const int FEAT_WEAPON_PROFICIENCY_HEAVY_MACE = 7946; +const int FEAT_WEAPON_PROFICIENCY_MAUL = 7947; +const int FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR = 7948; +const int FEAT_WEAPON_PROFICIENCY_GOAD = 7949; +const int FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW = 7950; +const int FEAT_WEAPON_PROFICIENCY_LIGHT_XBOW = 7951; +const int FEAT_WEAPON_PROFICIENCY_HEAVY_XBOW = 7952; +const int FEAT_WEAPON_PROFICIENCY_QUARTERSTAFF = 7953; +const int FEAT_WEAPON_PROFICIENCY_DAGGER = 7954; +const int FEAT_WEAPON_PROFICIENCY_CLUB = 7955; + + +//:: New Weapon Focus Feats +const int FEAT_WEAPON_FOCUS_LIGHT_LANCE = 24601; +const int FEAT_WEAPON_FOCUS_HEAVY_PICK = 24602; +const int FEAT_WEAPON_FOCUS_LIGHT_PICK = 24603; +const int FEAT_WEAPON_FOCUS_SAI = 24604; +const int FEAT_WEAPON_FOCUS_NUNCHAKU = 24605; +const int FEAT_WEAPON_FOCUS_FALCHION = 24606; +const int FEAT_WEAPON_FOCUS_SAP = 24607; +const int FEAT_WEAPON_FOCUS_KATAR = 24608; +const int FEAT_WEAPON_FOCUS_HEAVY_MACE = 24609; +const int FEAT_WEAPON_FOCUS_MAUL = 24610; +const int FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR = 24611; +const int FEAT_WEAPON_FOCUS_GOAD = 24612; +const int FEAT_WEAPON_FOCUS_EAGLE_CLAW = 24721; +const int FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE = 24697; +const int FEAT_WEAPON_FOCUS_ELVEN_THINBLADE = 24705; +const int FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE = 24713; + +//:: New Weapon Improved Critical Feats +const int FEAT_IMPROVED_CRITICAL_LIGHT_LANCE = 24649; +const int FEAT_IMPROVED_CRITICAL_HEAVY_PICK = 24650; +const int FEAT_IMPROVED_CRITICAL_LIGHT_PICK = 24651; +const int FEAT_IMPROVED_CRITICAL_SAI = 24652; +const int FEAT_IMPROVED_CRITICAL_NUNCHAKU = 24653; +const int FEAT_IMPROVED_CRITICAL_FALCHION = 24654; +const int FEAT_IMPROVED_CRITICAL_SAP = 24655; +const int FEAT_IMPROVED_CRITICAL_KATAR = 24656; +const int FEAT_IMPROVED_CRITICAL_HEAVY_MACE = 24657; +const int FEAT_IMPROVED_CRITICAL_MAUL = 24658; +const int FEAT_IMPROVED_CRITICAL_DBL_SCIMITAR = 24659; +const int FEAT_IMPROVED_CRITICAL_GOAD = 24660; +const int FEAT_IMPROVED_CRITICAL_EAGLE_CLAW = 24725; +const int FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE = 24701; +const int FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE = 24709; +const int FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE = 24717; + +//:: New Weapon Epic Weapon Focus Feats +const int FEAT_EPIC_WEAPON_FOCUS_LIGHT_LANCE = 24613; +const int FEAT_EPIC_WEAPON_FOCUS_HEAVY_PICK = 24614; +const int FEAT_EPIC_WEAPON_FOCUS_LIGHT_PICK = 24615; +const int FEAT_EPIC_WEAPON_FOCUS_SAI = 24616; +const int FEAT_EPIC_WEAPON_FOCUS_NUNCHAKU = 24617; +const int FEAT_EPIC_WEAPON_FOCUS_FALCHION = 24618; +const int FEAT_EPIC_WEAPON_FOCUS_SAP = 24619; +const int FEAT_EPIC_WEAPON_FOCUS_KATAR = 24620; +const int FEAT_EPIC_WEAPON_FOCUS_HEAVY_MACE = 24621; +const int FEAT_EPIC_WEAPON_FOCUS_MAUL = 24622; +const int FEAT_EPIC_WEAPON_FOCUS_DBL_SCIMITAR = 24623; +const int FEAT_EPIC_WEAPON_FOCUS_GOAD = 24624; +const int FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW = 24722; +const int FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE = 24698; +const int FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE = 24706; +const int FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE = 24714; + +//:: New Weapon Overwhelming Critical Feats +const int FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_LANCE = 24661; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_PICK = 24662; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_PICK = 24663; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_SAI = 24664; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_NUNCHAKU = 24665; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_FALCHION = 24666; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_SAP = 24667; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_KATAR = 24668; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_MACE = 24669; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_MAUL = 24670; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_DBL_SCIMITAR = 24671; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_GOAD = 24672; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW = 24726; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE = 24702; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_THINBLADE = 24710; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE = 24718; + +//:: New Weapon Devastating Critical Feats +const int FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_LANCE = 24673; +const int FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_PICK = 24674; +const int FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_PICK = 24675; +const int FEAT_EPIC_DEVASTATING_CRITICAL_SAI = 24676; +const int FEAT_EPIC_DEVASTATING_CRITICAL_NUNCHAKU = 24677; +const int FEAT_EPIC_DEVASTATING_CRITICAL_FALCHION = 24678; +const int FEAT_EPIC_DEVASTATING_CRITICAL_SAP = 24679; +const int FEAT_EPIC_DEVASTATING_CRITICAL_KATAR = 24680; +const int FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_MACE = 24681; +const int FEAT_EPIC_DEVASTATING_CRITICAL_MAUL = 24682; +const int FEAT_EPIC_DEVASTATING_CRITICAL_DBL_SCIMITAR = 24683; +const int FEAT_EPIC_DEVASTATING_CRITICAL_GOAD = 24684; +const int FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW = 24727; +const int FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE = 24703; +const int FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_THINBLADE = 24711; +const int FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_COURTBLADE = 24719; + +//:: New Weapon of Choice Feats +const int FEAT_WEAPON_OF_CHOICE_LIGHT_LANCE = 24685; +const int FEAT_WEAPON_OF_CHOICE_HEAVY_PICK = 24686; +const int FEAT_WEAPON_OF_CHOICE_LIGHT_PICK = 24687; +const int FEAT_WEAPON_OF_CHOICE_SAI = 24688; +const int FEAT_WEAPON_OF_CHOICE_NUNCHAKU = 24689; +const int FEAT_WEAPON_OF_CHOICE_FALCHION = 24690; +const int FEAT_WEAPON_OF_CHOICE_SAP = 24691; +const int FEAT_WEAPON_OF_CHOICE_KATAR = 24692; +const int FEAT_WEAPON_OF_CHOICE_HEAVY_MACE = 24693; +const int FEAT_WEAPON_OF_CHOICE_MAUL = 24694; +const int FEAT_WEAPON_OF_CHOICE_DBL_SCIMITAR = 24695; +const int FEAT_WEAPON_OF_CHOICE_GOAD = 24696; +const int FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW = 24728; +const int FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE = 24704; +const int FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE = 24712; +const int FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE = 24720; + +//:: New Weapon Specialization Feats +const int FEAT_WEAPON_SPECIALIZATION_LIGHT_LANCE = 24625; +const int FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK = 24626; +const int FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK = 24627; +const int FEAT_WEAPON_SPECIALIZATION_SAI = 24628; +const int FEAT_WEAPON_SPECIALIZATION_NUNCHAKU = 24629; +const int FEAT_WEAPON_SPECIALIZATION_FALCHION = 24630; +const int FEAT_WEAPON_SPECIALIZATION_SAP = 24631; +const int FEAT_WEAPON_SPECIALIZATION_KATAR = 24632; +const int FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE = 24633; +const int FEAT_WEAPON_SPECIALIZATION_MAUL = 24634; +const int FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR = 24635; +const int FEAT_WEAPON_SPECIALIZATION_GOAD = 24636; +const int FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW = 24723; +const int FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE = 24699; +const int FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE = 24707; +const int FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE = 24715; + +//:: New Weapon Epic Specialization Feats +const int FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_LANCE = 24637; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_PICK = 24638; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_PICK = 24639; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_SAI = 24640; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_NUNCHAKU = 24641; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_FALCHION = 24642; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_SAP = 24643; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_KATAR = 24644; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_MACE = 24645; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_MAUL = 24646; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_DBL_SCIMITAR = 24647; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_GOAD = 24648; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW = 24724; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE = 24700; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE = 24708; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE = 24716; + +//:: Ability Focus Feats +const int FEAT_ABFOC_DEAFENING_ROAR = 25150; +const int FEAT_ABFOC_SCALDING_GUST = 25151; +const int FEAT_ABFOC_CHARM = 25152; +const int FEAT_ABFOC_FRIGHTFUL_PRESENCE = 25153; +const int FEAT_ABFOC_TERRIFYING_ROAR = 25154; +const int FEAT_ABFOC_BALEFUL_UTTERANCE = 25155; +const int FEAT_ABFOC_CALL_OF_THE_BEAST = 25156; +const int FEAT_ABFOC_DRAIN_INCARNUM = 25157; +const int FEAT_ABFOC_ELDRITCH_GLAIVE = 25158; +const int FEAT_ABFOC_ELDRITCH_SPEAR = 25159; +const int FEAT_ABFOC_FRIGHTFUL_BLAST = 25160; +const int FEAT_ABFOC_HAMMER_BLAST = 25161; +const int FEAT_ABFOC_HIDEOUS_BLOW = 25162; +const int FEAT_ABFOC_MIASMIC_CLOUD = 25163; +const int FEAT_ABFOC_SICKENING_BLAST = 25164; +const int FEAT_ABFOC_SOULREAVING_AURA = 25165; +const int FEAT_ABFOC_BANEFUL_BLAST_ABERRATION = 25166; +const int FEAT_ABFOC_BANEFUL_BLAST_BEAST = 25167; +const int FEAT_ABFOC_BANEFUL_BLAST_CONSTRUCT = 25168; +const int FEAT_ABFOC_BANEFUL_BLAST_DRAGON = 25169; +const int FEAT_ABFOC_BANEFUL_BLAST_DWARF = 25170; +const int FEAT_ABFOC_BANEFUL_BLAST_ELEMENTAL = 25171; +const int FEAT_ABFOC_BANEFUL_BLAST_ELF = 25172; +const int FEAT_ABFOC_BANEFUL_BLAST_FEY = 25173; +const int FEAT_ABFOC_BANEFUL_BLAST_GIANT = 25174; +const int FEAT_ABFOC_BANEFUL_BLAST_GOBLINOID = 25175; +const int FEAT_ABFOC_BANEFUL_BLAST_GNOME = 25176; +const int FEAT_ABFOC_BANEFUL_BLAST_HALFLING = 25177; +const int FEAT_ABFOC_BANEFUL_BLAST_HUMAN = 25178; +const int FEAT_ABFOC_BANEFUL_BLAST_MONSTROUS = 25179; +const int FEAT_ABFOC_BANEFUL_BLAST_OOZE = 25180; +const int FEAT_ABFOC_BANEFUL_BLAST_ORC = 25181; +const int FEAT_ABFOC_BANEFUL_BLAST_OUTSIDER = 25182; +const int FEAT_ABFOC_BANEFUL_BLAST_PLANT = 25183; +const int FEAT_ABFOC_BANEFUL_BLAST_REPTILIAN = 25184; +const int FEAT_ABFOC_BANEFUL_BLAST_SHAPECHANGER = 25185; +const int FEAT_ABFOC_BANEFUL_BLAST_UNDEAD = 25186; +const int FEAT_ABFOC_BANEFUL_BLAST_VERMIN = 25187; +const int FEAT_ABFOC_BESHADOWED_BLAST = 25188; +const int FEAT_ABFOC_BRIMSTONE_BLAST = 25189; +const int FEAT_ABFOC_CURSE_OF_DESPAIR = 25190; +const int FEAT_ABFOC_DREAD_SEIZURE = 25191; +const int FEAT_ABFOC_ELDRITCH_CHAIN = 25192; +const int FEAT_ABFOC_HELLRIME_BLAST = 25193; +const int FEAT_ABFOC_STEAL_INCARNUM = 25194; +const int FEAT_ABFOC_WALL_OF_GLOOM = 25195; +const int FEAT_ABFOC_BEWITCHING_BLAST = 25196; +const int FEAT_ABFOC_ELDRITCH_CONE = 25197; +const int FEAT_ABFOC_ELDRITCH_LINE = 25198; +const int FEAT_ABFOC_ENERVATING_SHADOW = 25199; +const int FEAT_ABFOC_HINDERING_BLAST = 25200; +const int FEAT_ABFOC_INCARNUM_BLAST = 25201; +const int FEAT_ABFOC_NIGHTMARES_MADE_REAL = 25202; +const int FEAT_ABFOC_NOXIOUS_BLAST = 25203; +const int FEAT_ABFOC_PAINFUL_SLUMBER_OF_THE_AGES = 25204; +const int FEAT_ABFOC_PENETRATING_BLAST = 25205; +const int FEAT_ABFOC_UTTERDARK_BLAST = 25206; +const int FEAT_ABFOC_WALL_OF_PERILOUS_FLAME = 25207; +const int FEAT_ABFOC_BINDING_BLAST = 25208; +const int FEAT_ABFOC_ELDRITCH_DOOM = 25209; +const int FEAT_ABFOC_INCARNUM_SHROUD = 25210; +const int FEAT_ABFOC_STEAL_SUMMONING = 25211; +const int FEAT_ABFOC_ELDRITCH_BLAST = 25212; +const int FEAT_ABFOC_WORD_OF_CHANGING = 25213; + +//::: Eldritch Disciple Divine Gifts +const int FEAT_ED_CORRUPTING_BLAST = 23521; +const int FEAT_ED_DAMAGE_REDUCTION = 23522; +const int FEAT_ED_FEARFUL_GLARE = 23523; +const int FEAT_ED_FIENDISH_RESISTANCE = 23524; +const int FEAT_ED_HEALING_BLAST = 23525; +const int FEAT_ED_PROTECTIVE_AURA = 23526; +const int FEAT_ED_STRENGTH_OF_WILL = 23527; +const int FEAT_ED_WILD_FRENZY = 23528; + +//:: Disciple of Asmodeus +const int FEAT_DOA_CHARM = 1961; +const int FEAT_DOA_COMMAND = 1962; +const int FEAT_DOA_HELLCAT = 1963; +const int FEAT_DOA_EVIL_AUTHORITY = 1964; +const int FEAT_DOA_SUMMON_DEVIL = 1965; +const int FEAT_DOA_GREATER_COMMAND = 1966; +const int FEAT_DOA_DREAD_MIGHT = 1967; +const int FEAT_DOA_LEARN_SECRETS = 1968; + +//:: Slayer of Domiel +const int FEAT_SOD_DEATH_TOUCH = 2282; + +//:: Suel Archanamach +const int FEAT_SUEL_TENACIOUS_SPELL = 2397; +const int FEAT_SUEL_IGNORE_SPELL_FAILURE = 2398; +const int FEAT_SUEL_EXTENDED_SPELL = 2399; +const int FEAT_SUEL_DISPELLING_STRIKE = 2400; + +//Passive Feats +const int FEAT_ETERNAL_FREEDOM = 4298; +const int FEAT_INTUITIVE_ATTACK = 3166; +const int FEAT_RAVAGEGOLDENICE = 3162; +const int FEAT_DRAGONSONG = 3440; +const int FEAT_UNDEAD_MASTERY = 3446; +const int FEAT_IMPROVED_FAVORED_ENEMY = 3454; +const int FEAT_FAVORED_POWER_ATTACK = 3455; +const int FEAT_EXTENDED_RAGE = 3456; +const int FEAT_DARKNESS_DOMAIN = 3458; +const int FEAT_STRENGTH_OF_MIND = 1974; +const int FEAT_SPIRIT_SIGHT = 1948; +const int FEAT_AURA_OF_DESPAIR = 24730; +const int FEAT_IMPROVED_AURA_OF_DESPAIR = 24731; + +const int FEAT_FOCUSED_SPELL_PENETRATION_ABJURATION = 3128; +const int FEAT_FOCUSED_SPELL_PENETRATION_CONJURATION = 3129; +const int FEAT_FOCUSED_SPELL_PENETRATION_DIVINATION = 3130; +const int FEAT_FOCUSED_SPELL_PENETRATION_ENCHATMENT = 3131; +const int FEAT_FOCUSED_SPELL_PENETRATION_EVOCATION = 3132; +const int FEAT_FOCUSED_SPELL_PENETRATION_ILLUSION = 3133; +const int FEAT_FOCUSED_SPELL_PENETRATION_NECROMANCY = 3134; +const int FEAT_FOCUSED_SPELL_PENETRATION_TRANSMUTATION = 3135; + +const int FEAT_DISCIPLE_OF_SUN = 3127; +const int FEAT_EMPOWER_TURNING = 3126; +const int FEAT_IMPROVED_TURNING = 3055; + +const int FEAT_PRACTICED_SPELLCASTER_BARD = 3121; +const int FEAT_PRACTICED_SPELLCASTER_CLERIC = 3122; +const int FEAT_PRACTICED_SPELLCASTER_DRUID = 3123; +const int FEAT_PRACTICED_SPELLCASTER_SORCERER = 3124; +const int FEAT_PRACTICED_SPELLCASTER_WIZARD = 3125; +const int FEAT_PRACTICED_SPELLCASTER_KOTW = 3312; +const int FEAT_PRACTICED_SPELLCASTER_PALADIN = 24086; +const int FEAT_PRACTICED_SPELLCASTER_RANGER = 24087; +const int FEAT_PRACTICED_SPELLCASTER_ASSASSIN = 24088; +const int FEAT_PRACTICED_SPELLCASTER_BLACKGUARD = 24089; +const int FEAT_PRACTICED_SPELLCASTER_OCULAR = 24090; +const int FEAT_PRACTICED_SPELLCASTER_HEXBLADE = 24091; +const int FEAT_PRACTICED_SPELLCASTER_DUSKBLADE = 24092; +const int FEAT_PRACTICED_SPELLCASTER_HEALER = 24093; +const int FEAT_PRACTICED_SPELLCASTER_KNIGHT_CHALICE = 24094; +const int FEAT_PRACTICED_SPELLCASTER_NENTYAR = 24095; +const int FEAT_PRACTICED_SPELLCASTER_VASSAL = 24096; +const int FEAT_PRACTICED_SPELLCASTER_UR_PRIEST = 24097; +const int FEAT_PRACTICED_SPELLCASTER_SOLDIER_OF_LIGHT = 24098; +const int FEAT_PRACTICED_SPELLCASTER_SHADOWLORD = 24099; +const int FEAT_PRACTICED_SPELLCASTER_JUSTICEWW = 24100; +const int FEAT_PRACTICED_SPELLCASTER_KNIGHT_MIDDLECIRCLE = 24101; +const int FEAT_PRACTICED_SPELLCASTER_SHAMAN = 24102; +const int FEAT_PRACTICED_SPELLCASTER_SLAYER_OF_DOMIEL = 24103; +const int FEAT_PRACTICED_SPELLCASTER_SUEL_ARCHANAMACH = 24104; +const int FEAT_PRACTICED_SPELLCASTER_FAVOURED_SOUL = 24105; +const int FEAT_PRACTICED_SPELLCASTER_SOHEI = 24106; +const int FEAT_PRACTICED_SPELLCASTER_WARMAGE = 24107; +const int FEAT_PRACTICED_SPELLCASTER_DREAD_NECROMANCER = 24155; +const int FEAT_PRACTICED_SPELLCASTER_CELEBRANT_SHARESS = 24160; +const int FEAT_PRACTICED_SPELLCASTER_CULTIST = 24255; +const int FEAT_PRACTICED_SPELLCASTER_ARCHIVIST = 24256; +const int FEAT_PRACTICED_SPELLCASTER_BEGUILER = 24257; +const int FEAT_PRACTICED_SPELLCASTER_BLIGHTER = 24258; +const int FEAT_PRACTICED_SPELLCASTER_HARPER = 24259; +const int FEAT_PRACTICED_SPELLCASTER_TEMPLAR = 24260; + +const int FEAT_PRACTICED_MANIFESTER_PSION = 24250; +const int FEAT_PRACTICED_MANIFESTER_PSYWAR = 24251; +const int FEAT_PRACTICED_MANIFESTER_WILDER = 24252; +const int FEAT_PRACTICED_MANIFESTER_WARMIND = 24253; +const int FEAT_PRACTICED_MANIFESTER_FIST_OF_ZUOKEN = 24254; +const int FEAT_PRACTICED_MANIFESTER_PSYROG = 24264; + +const int FEAT_PRACTICED_INVOKER_DRAGONFIRE_ADEPT = 24261; +const int FEAT_PRACTICED_INVOKER_WARLOCK = 24262; + +const int FEAT_EPIC_DIVINE_MIGHT = 3120; +const int FEAT_EPIC_DIVINE_RESISTANCE = 3119; +const int FEAT_EPIC_DIVINE_SHIELD = 3548; +const int FEAT_DIVINE_CLEANSING = 3118; +const int FEAT_DIVINE_RESISTANCE = 3117; +const int FEAT_DIVINE_VIGOR = 3116; +const int FEAT_EPIC_DIVINE_VIGOR = 3115; +const int FEAT_INVOKE_DIVINE_WRATH = 3114; +const int FEAT_SHADOWWEAVE = 2960; +const int FEAT_TENACIOUSMAGIC = 2961; +const int FEAT_PERNICIOUSMAGIC = 2962; +const int FEAT_INSIDIOUSMAGIC = 2963; +const int FEAT_UNHOLY_STRIKE = 3451; + +const int FEAT_LEADERSHIP = 4365; +const int FEAT_EPIC_LEADERSHIP = 4366; +const int FEAT_LEGENDARY_COMMANDER = 4367; + +const int FEAT_TWO_WEAPON_REND = 3113; + +const int FEAT_EPIC_SPELLCASTING = 4073; + +const int FEAT_BANE_MAGIC_ABERRATION = 23556; +const int FEAT_BANE_MAGIC_ANIMAL = 23557; +const int FEAT_BANE_MAGIC_BEAST = 23558; +const int FEAT_BANE_MAGIC_CONSTRUCT = 23559; +const int FEAT_BANE_MAGIC_DRAGON = 23560; +const int FEAT_BANE_MAGIC_DWARF = 23561; +const int FEAT_BANE_MAGIC_ELEMENTAL = 23562; +const int FEAT_BANE_MAGIC_ELF = 23563; +const int FEAT_BANE_MAGIC_FEY = 23564; +const int FEAT_BANE_MAGIC_GIANT = 23565; +const int FEAT_BANE_MAGIC_GNOME = 23566; +const int FEAT_BANE_MAGIC_HALFELF = 23567; +const int FEAT_BANE_MAGIC_HALFLING = 23568; +const int FEAT_BANE_MAGIC_HALFORC = 23569; +const int FEAT_BANE_MAGIC_HUMAN = 23570; +const int FEAT_BANE_MAGIC_HUMANOID_GOBLINOID = 23571; +const int FEAT_BANE_MAGIC_HUMANOID_MONSTROUS = 23572; +const int FEAT_BANE_MAGIC_HUMANOID_ORC = 23573; +const int FEAT_BANE_MAGIC_HUMANOID_REPTILIAN = 23574; +const int FEAT_BANE_MAGIC_MAGICAL_BEAST = 23575; +const int FEAT_BANE_MAGIC_OUTSIDER = 23576; +const int FEAT_BANE_MAGIC_SHAPECHANGER = 23577; +const int FEAT_BANE_MAGIC_UNDEAD = 23578; +const int FEAT_BANE_MAGIC_VERMIN = 23579; + +//RotD feats +const int FEAT_KOB_DRAGON_TAIL = 3837; +const int FEAT_KOB_DRAGON_WING_A = 3838; +const int FEAT_KOB_DRAGON_WING_BC = 3839; +const int FEAT_KOB_DRAGON_WING_BG = 3840; +const int FEAT_KOB_DRAGON_WING_BM = 3856; +const int FEAT_KOB_DRAGONWROUGHT_BK = 3841; +const int FEAT_KOB_DRAGONWROUGHT_BL = 3842; +const int FEAT_KOB_DRAGONWROUGHT_GR = 3843; +const int FEAT_KOB_DRAGONWROUGHT_RD = 3844; +const int FEAT_KOB_DRAGONWROUGHT_WH = 3845; +const int FEAT_KOB_DRAGONWROUGHT_AM = 3846; +const int FEAT_KOB_DRAGONWROUGHT_CR = 3847; +const int FEAT_KOB_DRAGONWROUGHT_EM = 3848; +const int FEAT_KOB_DRAGONWROUGHT_SA = 3849; +const int FEAT_KOB_DRAGONWROUGHT_TP = 3850; +const int FEAT_KOB_DRAGONWROUGHT_BS = 3851; +const int FEAT_KOB_DRAGONWROUGHT_BZ = 3852; +const int FEAT_KOB_DRAGONWROUGHT_CP = 3853; +const int FEAT_KOB_DRAGONWROUGHT_GD = 3854; +const int FEAT_KOB_DRAGONWROUGHT_SR = 3855; + +//Dragon Magic +const int FEAT_DRAGON_AUGMENT_STR_1 = 3857; +const int FEAT_DRAGON_AUGMENT_STR_2 = 3858; +const int FEAT_DRAGON_AUGMENT_STR_3 = 3859; +const int FEAT_DRAGON_AUGMENT_STR_4 = 26382; +const int FEAT_DRAGON_AUGMENT_STR_5 = 26383; +const int FEAT_DRAGON_AUGMENT_STR_6 = 26384; +const int FEAT_DRAGON_AUGMENT_STR_7 = 26385; +const int FEAT_DRAGON_AUGMENT_STR_8 = 26386; +const int FEAT_DRAGON_AUGMENT_STR_9 = 26387; + +const int FEAT_DRAGON_AUGMENT_DEX_1 = 3860; +const int FEAT_DRAGON_AUGMENT_DEX_2 = 3861; +const int FEAT_DRAGON_AUGMENT_DEX_3 = 3862; +const int FEAT_DRAGON_AUGMENT_DEX_4 = 26388; +const int FEAT_DRAGON_AUGMENT_DEX_5 = 26389; +const int FEAT_DRAGON_AUGMENT_DEX_6 = 26390; +const int FEAT_DRAGON_AUGMENT_DEX_7 = 26391; +const int FEAT_DRAGON_AUGMENT_DEX_8 = 26392; +const int FEAT_DRAGON_AUGMENT_DEX_9 = 26393; + +const int FEAT_DRAGON_AUGMENT_CON_1 = 3863; +const int FEAT_DRAGON_AUGMENT_CON_2 = 3864; +const int FEAT_DRAGON_AUGMENT_CON_3 = 3865; +const int FEAT_DRAGON_AUGMENT_CON_4 = 26394; +const int FEAT_DRAGON_AUGMENT_CON_5 = 26395; +const int FEAT_DRAGON_AUGMENT_CON_6 = 26396; +const int FEAT_DRAGON_AUGMENT_CON_7 = 26397; +const int FEAT_DRAGON_AUGMENT_CON_8 = 26398; +const int FEAT_DRAGON_AUGMENT_CON_9 = 26399; + +const int FEAT_CHANNEL_DRACLAWS = 3866; +const int FEAT_PSIONIC_BREATH = 3867; +const int FEAT_CHANNEL_DRAWINGS = 3868; +const int FEAT_CHANNEL_DRATAIL = 3869; +const int FEAT_PSIONIC_DRGNFEAR = 3870; +const int FEAT_PSIONIC_DRAGON_IMMUNITY = 3871; +const int FEAT_DRAGON_AFFINITY_BK = 3904; +const int FEAT_DRAGON_AFFINITY_BL = 3905; +const int FEAT_DRAGON_AFFINITY_GR = 3906; +const int FEAT_DRAGON_AFFINITY_RD = 3907; +const int FEAT_DRAGON_AFFINITY_WH = 3908; +const int FEAT_DRAGON_AFFINITY_AM = 3909; +const int FEAT_DRAGON_AFFINITY_CR = 3910; +const int FEAT_DRAGON_AFFINITY_EM = 3911; +const int FEAT_DRAGON_AFFINITY_SA = 3912; +const int FEAT_DRAGON_AFFINITY_TP = 3913; +const int FEAT_DRAGON_AFFINITY_BS = 3914; +const int FEAT_DRAGON_AFFINITY_BZ = 3915; +const int FEAT_DRAGON_AFFINITY_CP = 3916; +const int FEAT_DRAGON_AFFINITY_GD = 3917; +const int FEAT_DRAGON_AFFINITY_SR = 3918; +const int FEAT_BREATH_OF_LIFE = 3919; +const int FEAT_SWIFT_WING_WINGS = 3920; +const int FEAT_DRACONIC_SURGE_STR = 3921; +const int FEAT_DRACONIC_SURGE_DEX = 3922; +const int FEAT_DRACONIC_SURGE_CON = 3923; +const int FEAT_DRACONIC_SURGE_INT = 3924; +const int FEAT_DRACONIC_SURGE_WIS = 3925; +const int FEAT_DRACONIC_SURGE_CHA = 3926; +const int FEAT_DEVOTEE_ABILITY_INCREASE = 3927; +const int FEAT_DEVOTEE_CLAWS = 3929; +const int FEAT_DRACONIC_DEVOTEE = 3930; +const int FEAT_TOT_VOICE_OF_DRAGON = 3928; +const int FEAT_TOT_FIRE_IMMUNITY = 3932; +const int FEAT_TOT_COLD_IMMUNITY = 3933; +const int FEAT_TOT_ACID_IMMUNITY = 3934; +const int FEAT_TOT_ELEC_IMMUNITY = 3935; +const int FEAT_TOT_BREATH_WEAPONS = 3936; +const int FEAT_TOT_DOMINATE_DRAGON = 3937; +const int FEAT_TOT_DRAGONFEAR = 3938; +const int FEAT_SPECIAL_SNEAK_ATTACK_1D6 = 3939; +const int FEAT_SPECIAL_SNEAK_ATTACK_2D6 = 3940; +const int FEAT_SPECIAL_SNEAK_ATTACK_3D6 = 3941; +const int FEAT_SPECIAL_SNEAK_ATTACK_4D6 = 3942; +const int FEAT_SPECIAL_SNEAK_ATTACK_5D6 = 3943; +const int FEAT_SPECIAL_SNEAK_ATTACK_6D6 = 24901; +const int FEAT_SPECIAL_SNEAK_ATTACK_7D6 = 24902; +const int FEAT_SPECIAL_SNEAK_ATTACK_8D6 = 24903; +const int FEAT_SPECIAL_SNEAK_ATTACK_9D6 = 24904; +const int FEAT_SPECIAL_SNEAK_ATTACK_10D6 = 24905; +const int FEAT_SPECIAL_SKIRMISH_1D6 = 3944; +const int FEAT_SPECIAL_SKIRMISH_2D6 = 3945; +const int FEAT_SPECIAL_SKIRMISH_3D6 = 3946; +const int FEAT_SPECIAL_SKIRMISH_4D6 = 3947; +const int FEAT_SPECIAL_SKIRMISH_5D6 = 3948; +const int FEAT_SPECIAL_SKIRMISH_6D6 = 24911; +const int FEAT_SPECIAL_SKIRMISH_7D6 = 24912; +const int FEAT_SPECIAL_SKIRMISH_8D6 = 24913; +const int FEAT_SPECIAL_SKIRMISH_9D6 = 24914; +const int FEAT_SPECIAL_SKIRMISH_10D6 = 24915; +const int FEAT_IMP_DRAGONFIRE_STRIKE = 3950; +const int FEAT_MASTERS_GIFT = 3951; +const int FEAT_TRUE_STEALTH = 3952; +const int FEAT_DRAGONFRIEND = 3988; +const int FEAT_DRAGONTHRALL = 3989; +const int FEAT_FULL_DRAGON_BREATH = 3111; +const int FEAT_ADEPT_SCALES = 3002; +const int FEAT_ADEPT_REDUCTION = 3003; + +//Dragonfire Adept breath effects +const int FEAT_FIRE_ADEPTBREATH = 4979; +const int FEAT_FROST_ADEPTBREATH = 4980; +const int FEAT_ELECTRIC_ADEPTBREATH = 4981; +const int FEAT_SICK_ADEPTBREATH = 4982; +const int FEAT_ACID_ADEPTBREATH = 4983; +const int FEAT_SHAPED_ADEPTBREATH = 4984; +const int FEAT_SLOW_ADEPTBREATH = 4985; +const int FEAT_WEAK_ADEPTBREATH = 4986; +const int FEAT_CLOUD_ADEPTBREATH = 4987; +const int FEAT_ENDURE_ADEPTBREATH = 4988; +const int FEAT_SLEEP_ADEPTBREATH = 4989; +const int FEAT_THUNDER_ADEPTBREATH = 4990; +const int FEAT_BAHAMUT_ADEPTBREATH = 4991; +const int FEAT_FORCE_ADEPTBREATH = 4992; +const int FEAT_PARALYZE_ADEPTBREATH = 4993; +const int FEAT_TIAMAT_ADEPTBREATH = 4994; + +//Metabreath and Breath Channeling +const int FEAT_CLINGING_BREATH = 4966; +const int FEAT_LINGERING_BREATH = 4967; +const int FEAT_ENLARGE_BREATH = 4968; +const int FEAT_HEIGHTEN_BREATH = 4969; +const int FEAT_MAXIMIZE_BREATH = 4970; +const int FEAT_RECOVER_BREATH = 4971; +const int FEAT_SHAPE_BREATH = 4972; +const int FEAT_SPREAD_BREATH = 4973; +const int FEAT_TEMPEST_BREATH = 4974; +const int FEAT_ENTANGLING_EXHALATION = 4975; +const int FEAT_EXHALED_BARRIER = 4976; +const int FEAT_EXHALED_IMMUNITY = 4977; + +//Draconic Feats(from Dragon Magic and RotD) +const int FEAT_DRAGONTOUCHED = 3889; +const int FEAT_DRACONIC_HERITAGE_BK = 3872; +const int FEAT_DRACONIC_HERITAGE_BL = 3873; +const int FEAT_DRACONIC_HERITAGE_GR = 3874; +const int FEAT_DRACONIC_HERITAGE_RD = 3875; +const int FEAT_DRACONIC_HERITAGE_WH = 3876; +const int FEAT_DRACONIC_HERITAGE_AM = 3877; +const int FEAT_DRACONIC_HERITAGE_CR = 3878; +const int FEAT_DRACONIC_HERITAGE_EM = 3879; +const int FEAT_DRACONIC_HERITAGE_SA = 3880; +const int FEAT_DRACONIC_HERITAGE_TP = 3881; +const int FEAT_DRACONIC_HERITAGE_BS = 3882; +const int FEAT_DRACONIC_HERITAGE_BZ = 3883; +const int FEAT_DRACONIC_HERITAGE_CP = 3884; +const int FEAT_DRACONIC_HERITAGE_GD = 3885; +const int FEAT_DRACONIC_HERITAGE_SR = 3886; +const int FEAT_DRACONIC_ARMOR = 3891; +const int FEAT_DRACONIC_BREATH = 3899; +const int FEAT_DRACONIC_CLAW = 3893; +const int FEAT_DRACONIC_GRACE = 3898; +const int FEAT_DRACONIC_KNOWLEDGE = 3888; +const int FEAT_DRACONIC_PERSUADE = 3892; +const int FEAT_DRACONIC_POWER = 3895; +const int FEAT_DRACONIC_PRESENCE = 3894; +const int FEAT_DRACONIC_RESISTANCE = 3896; +const int FEAT_DRACONIC_SENSES = 3897; +const int FEAT_DRACONIC_SKIN = 3887; +const int FEAT_DRACONIC_VIGOR = 3890; +const int FEAT_DRACONIC_BREATH_1_5 = 3901; +const int FEAT_DRACONIC_BREATH_6_9 = 3903; +const int FEAT_DRACONIC_GRACE_1_5 = 3900; +const int FEAT_DRACONIC_GRACE_6_9 = 3902; +const int FEAT_DRAGONFIRE_STRIKE = 3949; +const int FEAT_DRAGONFIRE_ASSAULT = 1842; +const int FEAT_DRAGONFIRE_CHANNELING = 1843; +const int FEAT_DRAGONFIRE_INSPIRATION = 1844; + +// Sandstorm +const int FEAT_BLAZING_BERSERKER = 24023; +const int FEAT_SCORPIONS_RESOLVE = 24024; +const int FEAT_HEAT_ENDURANCE = 5266; +const int FEAT_IMP_HEAT_ENDURANCE = 5265; +const int FEAT_LIGHT_OF_AURIFAR = 5264; +const int FEAT_DRIFT_MAGIC = 5181; +const int FEAT_SCORPIONS_GRASP = 5178; + +// Frostburn +const int FEAT_CRAFT_SKULL_TALISMAN = 3004; +const int FEAT_FROZEN_BERSERKER = 24039; +const int FEAT_SPELL_FOCUS_COLD = 24040; +const int FEAT_GREATER_SPELL_FOCUS_COLD = 24041; +const int FEAT_BECKON_THE_FROZEN = 4247; +const int FEAT_CHOSEN_OF_IBORIGHU = 4248; +const int FEAT_FAITH_IN_THE_FROST = 4249; +const int FEAT_MOUNTAINEER = 4399; +const int FEAT_SNOWFLAKE_WARDANCE = 4489; +const int FEAT_BONUS_DOMAIN_COLD = 5296; +const int FEAT_BONUS_DOMAIN_WINTER = 5297; +const int FEAT_DOMAIN_COLD = 5298; +const int FEAT_DOMAIN_WINTER = 5299; +const int FEAT_FROZEN_MAGIC = 5238; +const int FEAT_COLD_ENDURANCE = 5318; +const int FEAT_IMP_COLD_ENDURANCE = 5317; + +// Lost Empires of Faerun +const int FEAT_CORMANTHYRAN_MOON_MAGIC = 24042; +const int FEAT_JERGALS_PACT = 24043; + +// Player's Guide to Faerun +const int FEAT_BLOODLINE_OF_FIRE = 24044; +const int FEAT_CELEBRANT_SHARESS_FASCINATE = 5361; +const int FEAT_CELEBRANT_SHARESS_CONFUSE = 5362; +const int FEAT_CELEBRANT_SHARESS_DOMINATE = 5363; +const int FEAT_FAVORED_ZULKIRS = 5380; +const int FEAT_EPIC_SPELLFIRE_WIELDER_I = 5399; +const int FEAT_EPIC_SPELLFIRE_WIELDER_II = 5400; +const int FEAT_EPIC_SPELLFIRE_WIELDER_III = 5401; +const int FEAT_EPIC_SPELLFIRE_WIELDER_IV = 5402; +const int FEAT_EPIC_SPELLFIRE_WIELDER_V = 5403; +const int FEAT_EPIC_SPELLFIRE_WIELDER_VI = 5404; +const int FEAT_EPIC_SPELLFIRE_WIELDER_VII = 5405; +const int FEAT_EPIC_SPELLFIRE_WIELDER_VIII = 5406; +const int FEAT_EPIC_SPELLFIRE_WIELDER_IX = 5407; +const int FEAT_EPIC_SPELLFIRE_WIELDER_X = 5408; + +// Power of Faerun +const int FEAT_RULERSHIP = 24045; + +// Races of Faerun +const int FEAT_MIGHT_MAKES_RIGHT = 24046; +const int FEAT_PLAGUE_RESISTANT = 24047; +const int FEAT_JOTUNBRUD = 5176; +const int FEAT_SHIELD_DWARF_WARDER = 3262; + +// Drow of the Underdark +const int FEAT_STEAL_AND_STRIKE = 5191; +const int FEAT_SADISTIC_REWARD = 5185; + +// Underdark +const int FEAT_STRONG_MIND = 24048; +const int FEAT_VAE_SCHOOL = 5285; +const int FEAT_INLINDL_SCHOOL = 5284; +const int FEAT_DIVINE_INTERCESSION = 5283; +const int FEAT_PROFANE_AGONY = 5282; +const int FEAT_SPIDERFRIEND_MAGIC = 5281; +const int FEAT_UNSPEAKABLE_VOW = 5280; +const int FEAT_LOLTHS_BOON = 5279; +const int FEAT_PSYCHIC_REFUSAL = 5278; +const int FEAT_XANIQOS_SCHOOL = 5277; +const int FEAT_DESPANA_SCHOOL = 5324; + +// Races of Stone +const int FEAT_RECKLESS_RAGE = 24049; +const int FEAT_HEAVY_LITHODERMS = 24050; +const int FEAT_MORADINS_SMILE = 24051; +const int FEAT_PIERCING_SIGHT = 24052; +const int FEAT_RAMPAGING_BULL_RUSH = 5378; +const int FEAT_FOCUSED_SHIELD = 3255; +const int FEAT_SHIELDED_CASTING = 3261; + +// Races of Destiny +const int FEAT_MENACING_DEMEANOUR = 24053; + +// Champions of Ruin +const int FEAT_DIRE_FLAIL_SMASH = 5184; +const int FEAT_SHADOWFORM_FAMILIAR = 5183; +const int FEAT_SHADOWSTRIKE = 5182; + +// Races of Eberron +const int FEAT_RELIC_HUNTER = 24054; +const int FEAT_LIVING_CONSTRUCT = 4556; +const int FEAT_COMPOSITE_PLATING = 4554; +const int FEAT_UNARMORED_BODY = 4591; +const int FEAT_IRONWOOD_PLATING = 4598; +const int FEAT_MITHRIL_PLATING = 4599; +const int FEAT_ADAMANTINE_PLATING = 4781; +const int FEAT_ZAKYA_TRUESTRIKE = 4557; +const int FEAT_ZAKYA_CHILLTOUCH = 4570; +const int FEAT_ZAKYA_VAMPTOUCH = 4558; +const int FEAT_BONUS_MIND_2 = 4559; +const int FEAT_RACE_MINOR_SHAPE_CHANGE = 4560; +const int FEAT_IMPROVED_FORTIFICATION = 4580; +const int FEAT_SOULBLADE_WARRIOR = 4854; +const int FEAT_SPIRITUAL_FORCE = 4855; +const int FEAT_STRENGTH_OF_TWO = 4965; +const int FEAT_CHANGELING_CHANGE_SHAPE = 4561; +const int FEAT_RACIAL_EMULATION = 4564; +const int FEAT_CHANGELING_QUICK_CHANGE = 4563; +const int FEAT_SHIELD_OF_THOUGHT = 4565; +const int FEAT_SHIFTER_SHIFTING = 4566; +const int FEAT_SHIFTER_WILDHUNT = 4575; +const int FEAT_SHIFTER_RAZORCLAW = 4567; +const int FEAT_SHIFTER_LONGTOOTH = 4568; +const int FEAT_SHIFTER_LONGSTRIDE = 4569; +const int FEAT_SHIFTER_BEASTHIDE = 4571; +const int FEAT_SHIFTER_DREAMSIGHT = 4572; +const int FEAT_SHIFTER_GOREBRUTE = 4573; +const int FEAT_SHIFTER_WINTERHIDE = 1807; +const int FEAT_BEASTHIDE_ELITE = 1808; +const int FEAT_DREAMSIGHT_ELITE = 1809; +const int FEAT_GOREBRUTE_ELITE = 1810; +const int FEAT_LONGSTRIDE_ELITE = 1811; +const int FEAT_LONGTOOTH_ELITE = 1812; +const int FEAT_RAZORCLAW_ELITE = 1813; +const int FEAT_WILDHUNT_ELITE = 1814; +const int FEAT_EXTRA_SHIFTER_TRAIT = 1815; +const int FEAT_HEALING_FACTOR = 1816; +const int FEAT_SHIFTER_AGILITY = 1817; +const int FEAT_SHIFTER_DEFENSE = 1818; +const int FEAT_GREATER_SHIFTER_DEFENSE = 1819; +const int FEAT_SHIFTER_FEROCITY = 1820; +const int FEAT_SHIFTER_INSTINCTS = 1821; +const int FEAT_SHIFTER_SAVAGERY = 1822; +const int FEAT_SPIKED_BODY = 5174; + +// Races of the Wild +const int FEAT_AGILE_ATHLETE = 24055; +const int FEAT_SHARED_FURY = 24056; +const int FEAT_CATFOLK_POUNCE = 5203; +const int FEAT_EXPEDITIOUS_DODGE = 5202; +const int FEAT_CENTAUR_TRAMPLE = 5418; + +// Libris Mortis +const int FEAT_REQUIEM = 2455; +const int FEAT_CORPSECRAFTER = 4014; +const int FEAT_BOLSTER_RESISTANCE = 4015; +const int FEAT_DEADLY_CHILL = 4016; +const int FEAT_DESTRUCTION_RETRIBUTION = 4017; +const int FEAT_HARDENED_FLESH = 4018; +const int FEAT_NIMBLE_BONES = 4019; + +// Magic of Faerun +const int FEAT_SPELL_GIRDING = 2456; +const int FEAT_ATTUNE_GEM = 2472; + +// Complete Adventurer +const int FEAT_FORCE_PERSONALITY = 2457; +const int FEAT_INSIGHTFUL_REFLEXES = 2458; +const int FEAT_TACTILE_TRAPSMITH = 2459; +const int FEAT_APPRAISE_MAGIC_VALUE = 1998; +const int FEAT_DIVE_FOR_COVER = 2578; +const int FEAT_EXTRAORDINARY_SPELL_AIM = 2599; +const int FEAT_OBSCURE_LORE = 2615; +const int FEAT_OTWF = 5371; +const int FEAT_POWER_THROW = 3249; + +// Complete Divine +const int FEAT_AUGMENT_HEALING = 2460; +const int FEAT_SPELL_FOCUS_CHAOS = 3263; +const int FEAT_SPELL_FOCUS_EVIL = 3264; +const int FEAT_SPELL_FOCUS_GOOD = 3265; +const int FEAT_SPELL_FOCUS_LAWFUL = 3266; + +// Complete Mage +const int FEAT_DAZZLING_ILLUSION = 2617; +const int FEAT_ENERGY_ABJURATION = 2639; +const int FEAT_FEARSOME_NECROMANCY = 2646; +const int FEAT_INSIGHTFUL_DIVINATION = 2668; +const int FEAT_PIERCING_EVOCATION = 2698; +const int FEAT_UNSETTLING_ENCHANTMENT = 2824; +const int FEAT_TOUGHENING_TRANSMUTATION = 2897; +const int FEAT_CLOUDY_CONJURATION = 3001; +const int FEAT_RANGED_RECALL = 5187; +const int FEAT_SOMATIC_WEAPONRY = 5186; + +// Forgotten Realms Campaign Setting +const int FEAT_INSCRIBE_RUNE = 2462; + +// Miniature Handbook +const int FEAT_SHIELDMATE = 3258; +const int FEAT_IMPROVED_SHIELDMATE = 3259; + +// Lords of Madness +const int FEAT_PARRYING_SHIELD = 3260; +const int FEAT_ABERRANT_EYES = 5387; +const int FEAT_ABERRANT_LIMBS = 5388; +const int FEAT_ABERRANT_SEGMENT = 5389; +const int FEAT_ABERRANT_FINGERS = 5390; +const int FEAT_ABERRANT_TAIL = 5391; +const int FEAT_ABERRANT_BESTIAL_HIDE = 5392; +const int FEAT_ABERRANT_DEEPSPAWN = 5393; +const int FEAT_ABERRANT_DURABLE_FORM = 5394; +const int FEAT_ABERRANT_INHUMAN_VISION = 5395; +const int FEAT_ABERRANT_SCAVENGING_GULLET = 5396; +const int FEAT_ABERRANT_WARPED_MIND = 5397; +const int FEAT_ABERRANT_WILD_SHAPE = 5398; + +// Bonus Domains +const int FEAT_BONUS_DOMAIN_AIR = 2001; +const int FEAT_BONUS_DOMAIN_ANIMAL = 2002; +const int FEAT_BONUS_DOMAIN_DEATH = 2003; +const int FEAT_BONUS_DOMAIN_DESTRUCTION = 2004; +const int FEAT_BONUS_DOMAIN_EARTH = 2005; +const int FEAT_BONUS_DOMAIN_EVIL = 2006; +const int FEAT_BONUS_DOMAIN_FIRE = 2007; +const int FEAT_BONUS_DOMAIN_GOOD = 2008; +const int FEAT_BONUS_DOMAIN_HEALING = 2009; +const int FEAT_BONUS_DOMAIN_KNOWLEDGE = 2010; +const int FEAT_BONUS_DOMAIN_MAGIC = 2011; +const int FEAT_BONUS_DOMAIN_PLANT = 2012; +const int FEAT_BONUS_DOMAIN_PROTECTION = 2013; +const int FEAT_BONUS_DOMAIN_STRENGTH = 2014; +const int FEAT_BONUS_DOMAIN_SUN = 2015; +const int FEAT_BONUS_DOMAIN_TRAVEL = 2016; +const int FEAT_BONUS_DOMAIN_TRICKERY = 2017; +const int FEAT_BONUS_DOMAIN_WAR = 2018; +const int FEAT_BONUS_DOMAIN_WATER = 2019; +const int FEAT_BONUS_DOMAIN_DARKNESS = 2020; +const int FEAT_BONUS_DOMAIN_STORM = 2021; +const int FEAT_BONUS_DOMAIN_METAL = 2022; +const int FEAT_BONUS_DOMAIN_PORTAL = 2023; +const int FEAT_BONUS_DOMAIN_FORCE = 2024; +const int FEAT_BONUS_DOMAIN_SLIME = 2025; +const int FEAT_BONUS_DOMAIN_TYRANNY = 2026; +const int FEAT_BONUS_DOMAIN_DOMINATION = 2027; +const int FEAT_BONUS_DOMAIN_SPIDER = 2028; +const int FEAT_BONUS_DOMAIN_UNDEATH = 2029; +const int FEAT_BONUS_DOMAIN_TIME = 2030; +const int FEAT_BONUS_DOMAIN_DWARF = 2031; +const int FEAT_BONUS_DOMAIN_CHARM = 2032; +const int FEAT_BONUS_DOMAIN_ELF = 2033; +const int FEAT_BONUS_DOMAIN_FAMILY = 2034; +const int FEAT_BONUS_DOMAIN_FATE = 2035; +const int FEAT_BONUS_DOMAIN_GNOME = 2036; +const int FEAT_BONUS_DOMAIN_ILLUSION = 2037; +const int FEAT_BONUS_DOMAIN_HATRED = 2038; +const int FEAT_BONUS_DOMAIN_HALFLING = 2039; +const int FEAT_BONUS_DOMAIN_NOBILITY = 2040; +const int FEAT_BONUS_DOMAIN_OCEAN = 2041; +const int FEAT_BONUS_DOMAIN_ORC = 2042; +const int FEAT_BONUS_DOMAIN_RENEWAL = 2043; +const int FEAT_BONUS_DOMAIN_RETRIBUTION = 2044; +const int FEAT_BONUS_DOMAIN_RUNE = 2045; +const int FEAT_BONUS_DOMAIN_SPELLS = 2046; +const int FEAT_BONUS_DOMAIN_SCALEYKIND = 2047; +const int FEAT_BONUS_DOMAIN_BLIGHTBRINGER = 2048; +const int FEAT_BONUS_DOMAIN_DRAGON = 2122; + +// Domain Power Feats +const int FEAT_DOMAIN_POWER_DARKNESS = 3458; +const int FEAT_DOMAIN_POWER_STORM = 4038; +const int FEAT_DOMAIN_POWER_METAL = 4037; +const int FEAT_DOMAIN_POWER_PORTAL = 4039; +const int FEAT_DOMAIN_POWER_FORCE = 4042; +const int FEAT_DOMAIN_POWER_SLIME = 4043; +const int FEAT_DOMAIN_POWER_TYRANNY = 4062; +const int FEAT_DOMAIN_POWER_DOMINATION = 4049; +const int FEAT_DOMAIN_POWER_SPIDER = 4061; +const int FEAT_DOMAIN_POWER_UNDEATH = 4050; +const int FEAT_DOMAIN_POWER_TIME = 4044; +const int FEAT_DOMAIN_POWER_DWARF = 4040; +const int FEAT_DOMAIN_POWER_CHARM = 4045; +const int FEAT_DOMAIN_POWER_ELF = 4059; +const int FEAT_DOMAIN_POWER_FAMILY = 4051; +const int FEAT_DOMAIN_POWER_FATE = 4048; +const int FEAT_DOMAIN_POWER_GNOME = 4058; +const int FEAT_DOMAIN_POWER_ILLUSION = 4053; +const int FEAT_DOMAIN_POWER_HATRED = 4054; +const int FEAT_DOMAIN_POWER_HALFLING = 4052; +const int FEAT_DOMAIN_POWER_NOBILITY = 4055; +const int FEAT_DOMAIN_POWER_OCEAN = 4063; +const int FEAT_DOMAIN_POWER_ORC = 4041; +const int FEAT_DOMAIN_POWER_RENEWAL = 4060; +const int FEAT_DOMAIN_POWER_RETRIBUTION = 4056; +const int FEAT_DOMAIN_POWER_RUNE = 4047; +const int FEAT_DOMAIN_POWER_SPELLS = 4046; +const int FEAT_DOMAIN_POWER_SCALEYKIND = 4057; +const int FEAT_DOMAIN_POWER_BLIGHTBRINGER = 2250; +const int FEAT_DOMAIN_POWER_DRAGON = 4064; + +// Cast Bonus Domain +const int FEAT_CAST_DOMAIN_LEVEL_ONE = 2049; +const int FEAT_CAST_DOMAIN_LEVEL_TWO = 2050; +const int FEAT_CAST_DOMAIN_LEVEL_THREE = 2051; +const int FEAT_CAST_DOMAIN_LEVEL_FOUR = 2052; +const int FEAT_CAST_DOMAIN_LEVEL_FIVE = 2053; +const int FEAT_CAST_DOMAIN_LEVEL_SIX = 2054; +const int FEAT_CAST_DOMAIN_LEVEL_SEVEN = 2055; +const int FEAT_CAST_DOMAIN_LEVEL_EIGHT = 2056; +const int FEAT_CAST_DOMAIN_LEVEL_NINE = 2057; + +const int FEAT_CHECK_DOMAIN_SLOTS = 2000; + +//Vile +const int FEAT_VILE_MARTIAL_CLUB = 3357; +const int FEAT_VILE_MARTIAL_DAGGER = 3358; +const int FEAT_VILE_MARTIAL_MACE = 3359; +const int FEAT_VILE_MARTIAL_LIGHTMACE = FEAT_VILE_MARTIAL_MACE; +const int FEAT_VILE_MARTIAL_MORNINGSTAR = 3360; +const int FEAT_VILE_MARTIAL_QUARTERSTAFF = 3361; +const int FEAT_VILE_MARTIAL_SPEAR = 3362; +const int FEAT_VILE_MARTIAL_SHORTSPEAR = FEAT_VILE_MARTIAL_SPEAR; +const int FEAT_VILE_MARTIAL_SHORTSWORD = 3363; +const int FEAT_VILE_MARTIAL_RAPIER = 3364; +const int FEAT_VILE_MARTIAL_SCIMITAR = 3365; +const int FEAT_VILE_MARTIAL_LONGSWORD = 3366; +const int FEAT_VILE_MARTIAL_GREATSWORD = 3367; +const int FEAT_VILE_MARTIAL_HANDAXE = 3368; +const int FEAT_VILE_MARTIAL_BATTLEAXE = 3369; +const int FEAT_VILE_MARTIAL_GREATAXE = 3370; +const int FEAT_VILE_MARTIAL_HALBERD = 3371; +const int FEAT_VILE_MARTIAL_LIGHTHAMMER = 3372; +const int FEAT_VILE_MARTIAL_LIGHTFLAIL = 3373; +const int FEAT_VILE_MARTIAL_WARHAMMER = 3374; +const int FEAT_VILE_MARTIAL_HEAVYFLAIL = 3375; +const int FEAT_VILE_MARTIAL_SCYTHE = 3376; +const int FEAT_VILE_MARTIAL_KATANA = 3377; +const int FEAT_VILE_MARTIAL_BASTARDSWORD = 3378; +const int FEAT_VILE_MARTIAL_DIREMACE = 3379; +const int FEAT_VILE_MARTIAL_DOUBLEAXE = 3380; +const int FEAT_VILE_MARTIAL_TWOBLADED = 3381; +const int FEAT_VILE_MARTIAL_KAMA = 3382; +const int FEAT_VILE_MARTIAL_KUKRI = 3383; +const int FEAT_VILE_MARTIAL_HEAVYCROSSBOW = 3384; +const int FEAT_VILE_MARTIAL_LIGHTCROSSBOW = 3385; +const int FEAT_VILE_MARTIAL_SLING = 3386; +const int FEAT_VILE_MARTIAL_LONGBOW = 3387; +const int FEAT_VILE_MARTIAL_SHORTBOW = 3396; +const int FEAT_VILE_MARTIAL_SHURIKEN = 3397; +const int FEAT_VILE_MARTIAL_DART = 3398; +const int FEAT_VILE_MARTIAL_SICKLE = 3399; +const int FEAT_VILE_MARTIAL_DWAXE = 3452; +const int FEAT_VILE_MARTIAL_MINDBLADE = 3624; +const int FEAT_VILE_MARTIAL_WHIP = 3598; +const int FEAT_VILE_MARTIAL_TRIDENT = 3599; + +// Weapon Focus (Ray) +const int FEAT_WEAPON_FOCUS_RAY = 4819; +const int FEAT_EPIC_WEAPON_FOCUS_RAY = 4820; + +// Battleguard Tempus +const int TEMPUS_ABILITY_ENHANC1 = 1; +const int TEMPUS_ABILITY_ENHANC2 = 2; +const int TEMPUS_ABILITY_ENHANC3 = 3; +const int TEMPUS_ABILITY_FIRE1D6 = 4; +const int TEMPUS_ABILITY_COLD1D6 = 5; +const int TEMPUS_ABILITY_ELEC1D6 = 6; +const int TEMPUS_ABILITY_KEEN = 7; +const int TEMPUS_ABILITY_ANARCHIC = 8; +const int TEMPUS_ABILITY_AXIOMATIC = 9; +const int TEMPUS_ABILITY_HOLY = 10; +const int TEMPUS_ABILITY_UNHOLY = 11; +const int TEMPUS_ABILITY_VICIOUS = 12; +const int TEMPUS_ABILITY_DISRUPTION= 13; +const int TEMPUS_ABILITY_WOUNDING = 14; +const int TEMPUS_ABILITY_BARSKIN = 15; +const int TEMPUS_ABILITY_CONECOLD = 16; +const int TEMPUS_ABILITY_DARKNESS = 17; +const int TEMPUS_ABILITY_FIREBALL = 18; +const int TEMPUS_ABILITY_HASTE = 19; +const int TEMPUS_ABILITY_IMPROVINV = 20; +const int TEMPUS_ABILITY_LIGHTBOLT = 21; +const int TEMPUS_ABILITY_MAGICMISSILE = 22; +const int TEMPUS_ABILITY_WEB = 23; +const int TEMPUS_ABILITY_VAMPIRE = 24; + +// Polymorph +const int POLY_SHAPEDRAGONGOLD = 130; +const int POLY_SHAPEDRAGONRED = 131; +const int POLY_SHAPEDRAGONPRYS = 132; + + +//Active Feats +const int FEAT_FIST_OF_IRON = 3145; +const int FEAT_POWER_ATTACK_SINGLE_RADIAL = 3143; +const int FEAT_POWER_ATTACK_FIVES_RADIAL = 3144; +const int FEAT_POWER_ATTACK_QUICKS_RADIAL = 3179; +const int FEAT_TELEPORT_MANAGEMENT_RADIAL = 4235; +const int FEAT_ZONE_OF_ANIMATION = 2891; + +// Werewolf Template +const int FEAT_PRESTIGE_WOLF_FORM = 3518; +const int FEAT_PRESTIGE_WEREWOLF_FORM = 3519; +const int FEAT_TRUE_LYCANTHROPE = 3520; +const int FEAT_PRESTIGE_WOLF_EMPATHY = 3521; +const int FEAT_PRESTIGE_WOLFCLASS_1 = 3522; +const int FEAT_PRESTIGE_WOLFCLASS_2 = 3523; +const int FEAT_PRESTIGE_WEREWOLFCLASS_1 = 3524; +const int FEAT_PRESTIGE_WEREWOLFCLASS_2 = 3525; +const int FEAT_PRESTIGE_WEREWOLFCLASS_3 = 3526; +const int FEAT_PRESTIGE_LYCANTHROPE = 3527; + +// Arcane Duelist +const int FEAT_AD_CHOSEN_WEAPON = 3530; +const int FEAT_AD_ENCHANT_CHOSEN_WEAPON = 3531; +const int FEAT_AD_APPARENT_DEFENSE = 3532; +const int FEAT_AD_DEX_ATTACK = 3533; +const int FEAT_AD_FLURRY_OF_SWORDS = 3534; +const int FEAT_AD_FALSE_KEENNESS = 3535; +const int FEAT_AD_BLUR = 3536; +const int FEAT_AD_MISLEAD = 3537; + +//:: Blightlord feats +const int FEAT_BLIGHTMIND = 2249; +const int FEAT_BLIGHTBRINGER = 2250; +const int FEAT_BLIGHTBLOOD = 2251; +const int FEAT_ILLMASTER = 2252; +const int FEAT_BLIGHTTOUCH = 2253; +const int FEAT_BLACKGLAIVE = 2257; +const int FEAT_CORRUPTING_BLOW = 2258; +const int FEAT_WINTERHEART = 2259; + +//:: Ravager feats +const int FEAT_PAIN_TOUCH = 2342; +const int FEAT_AURA_OF_FEAR = 2343; +const int FEAT_CRUELEST_CUT = 2344; +const int FEAT_VISAGE_OF_TERROR = 2345; + +//Drow Judicator Feats +const int FEAT_LOLTHS_MEAT = 2441; +const int FEAT_COMMAND_SPIDERS = 2442; +const int FEAT_WAR_STRIKE = 2443; +const int FEAT_SELVETARMS_WRATH = 2446; +const int FEAT_SELVETARMS_BLESSING = 2447; + +//Ranger Feats +const int FEAT_RANGER_DUAL = 374; +const int FEAT_CAMOUFLAGE = 4486; + +//Exalted Feat +const int FEAT_SAC_VOW = 3388; +const int FEAT_VOW_OBED = 3389; +const int FEAT_EXALTED_TURNING = 3168; +const int FEAT_HAND_HEALER = 3167; +const int FEAT_NIMBUSLIGHT = 3165; +const int FEAT_HOLYRADIANCE = 3164; +const int FEAT_STIGMATA = 3163; +const int FEAT_SERVHEAVEN = 3355; +const int FEAT_RANGED_SMITE = 3356; +const int FEAT_VOW_PURITY = 5360; +const int FEAT_VOWOFPOVERTY = 26001; + +//Vile Feat +const int FEAT_LICHLOVED = 3395; +const int FEAT_THRALL_TO_DEMON = 2616; +const int FEAT_DISCIPLE_OF_DARKNESS = 4012; +const int FEAT_EB_HAND = 3390; +const int FEAT_EB_HEAD = 3391; +const int FEAT_EB_CHEST = 3392; +const int FEAT_EB_ARM = 3393; +const int FEAT_EB_NECK = 3394; +const int FEAT_VILE_DEFORM_OBESE = 4406; +const int FEAT_VILE_DEFORM_GAUNT = 4407; +const int FEAT_VILE_WILL_DEFORM = 4408; +const int FEAT_VILE_DEFORM_SKIN = 5270; + +//archmage +const int FEAT_MASTERY_SHAPES = 3006; +const int FEAT_SPELL_POWER_I = 3007; +const int FEAT_SPELL_POWER_II = 3008; +const int FEAT_SPELL_POWER_III = 3009; +const int FEAT_SPELL_POWER_IV = 3010; +const int FEAT_SPELL_POWER_V = 3011; +const int FEAT_ARCANE_FIRE = 3012; +const int FEAT_SPELL_LIKE = 3013; + +//Master Harper +const int FEAT_LYCANBANE = 2404 ; +const int FEAT_DENEIRS_OREL = 2408 ; +const int FEAT_MILILS_EAR = 2406 ; +const int FEAT_INSTRUMENT = 2409; +const int FEAT_MIELIKKI = 2405; + +// Ghost-Faced Killer +const int FEAT_GFKILL_GHOST_STEP = 4490; +const int FEAT_GFKILL_GHOST_STEP2 = 4491; +const int FEAT_GFKILL_GHOST_STEP3 = 4492; +const int FEAT_GFKILL_GHOST_STEP4 = 4493; +const int FEAT_GFKILL_GHOST_STEP5 = 4494; +const int FEAT_GFKILL_GHOST_STEP6 = 4495; +const int FEAT_GFKILL_GHOST_STEP7 = 4496; +const int FEAT_GFKILL_GHOST_STEP8 = 4497; +const int FEAT_GFKILL_GHOST_STEP9 = 4498; +const int FEAT_GFKILL_GHOST_STEP10 = 4499; +const int FEAT_FRIGHTFUL_ATTACK = 4500; +const int FEAT_FRIGHTFUL_ATTACK2 = 4501; +const int FEAT_FRIGHTFUL_ATTACK3 = 4502; +const int FEAT_FRIGHTFUL_ATTACK4 = 4503; +const int FEAT_FRIGHTFUL_ATTACK5 = 4504; +const int FEAT_FRIGHTFUL_ATTACK6 = 4505; +const int FEAT_FRIGHTFUL_ATTACK7 = 4506; +const int FEAT_FRIGHTFUL_ATTACK8 = 4507; +const int FEAT_FRIGHTFUL_ATTACK9 = 4508; +const int FEAT_FRIGHTFUL_ATTACK20 = 4509; +const int FEAT_FRIGHTFUL_CLEAVE = 4510; + +// Thayan Knight +const int FEAT_TK_HORROR_1 = 2640; +const int FEAT_TK_HORROR_2 = 2641; +const int FEAT_TK_ZULKIR_FAVOUR = 2642; +const int FEAT_TK_ZULKIR_DEFEND = 2643; +const int FEAT_TK_ZULKIR_CHAMP = 2644; +const int FEAT_TK_FINAL_STAND = 2645; + +//oozemaster +const int FEAT_MIN_OOZY_TOUCH_BROWN = 3020; +const int FEAT_MIN_OOZY_TOUCH_GRAY = 3021; +const int FEAT_MIN_OOZY_TOUCH_OCHRE = 3022; +const int FEAT_MIN_OOZY_TOUCH_FUNGUS = 3023; +const int FEAT_OOZY_GLOB1 = 3028; +const int FEAT_OOZY_GLOB2 = 3029; +const int FEAT_OOZY_GLOB3 = 3030; +const int FEAT_OOZY_GLOB4 = 3031; +const int FEAT_OOZY_GLOB5 = 3032; +const int FEAT_INDISCERNIBLE_ANATOMY = 3033; +const int FEAT_CHARISMA_PENALITY = 3034; +const int FEAT_ONE_WITH_THE_OOZE = 3035; + +//disciple mephisto +const int FEAT_HELLFIRE_GRASP = 4000; +const int FEAT_FIRE_ADEPT = 4001; +const int FEAT_FIRE_RESISTANCE_10 = 4002; +const int FEAT_FIRE_RESISTANCE_20 = 4007; +const int FEAT_IMP_SUMMON_HAMATULA = 4011; + +//Mage Killer +const int FEAT_AUGMENT_SUMMON = 2510; +const int FEAT_MK_REF_1 = 2500; +const int FEAT_MK_REF_2 = 2501; +const int FEAT_MK_REF_3 = 2502; +const int FEAT_MK_REF_4 = 2503; +const int FEAT_MK_REF_5 = 2504; +const int FEAT_MK_FORT_1 = 2505; +const int FEAT_MK_FORT_2 = 2506; +const int FEAT_MK_FORT_3 = 2507; +const int FEAT_MK_FORT_4 = 2508; +const int FEAT_MK_FORT_5 = 2509; + +//Epic Mage Killer +const int FEAT_MK_FORT_6 = 2647; +const int FEAT_MK_FORT_7 = 2648; +const int FEAT_MK_FORT_8 = 2649; +const int FEAT_MK_FORT_9 = 2650; +const int FEAT_MK_FORT_10 = 2651; +const int FEAT_MK_FORT_11 = 2652; +const int FEAT_MK_FORT_12 = 2653; +const int FEAT_MK_FORT_13 = 2654; +const int FEAT_MK_FORT_14 = 2655; +const int FEAT_MK_FORT_15 = 2656; +const int FEAT_MK_REF_6 = 2657; +const int FEAT_MK_REF_7 = 2658; +const int FEAT_MK_REF_8 = 2659; +const int FEAT_MK_REF_9 = 2660; +const int FEAT_MK_REF_10 = 2661; +const int FEAT_MK_REF_11 = 2662; +const int FEAT_MK_REF_12 = 2663; +const int FEAT_MK_REF_13 = 2664; +const int FEAT_MK_REF_14 = 2665; +const int FEAT_MK_REF_15 = 2666; + +//Spellsword +const int FEAT_SPELLS_1 = 2512; +const int FEAT_SPELLS_2 = 2513; +const int FEAT_SPELLS_3 = 2514; +const int FEAT_SPELLS_4 = 2515; +const int FEAT_SPELLS_5 = 2516; +const int FEAT_CHANNEL_SPELL_1 = 4688; +const int FEAT_CHANNEL_SPELL_2 = 4689; +const int FEAT_CHANNEL_SPELL_3 = 4690; +const int FEAT_CHANNEL_SPELL_4 = 4691; +const int FEAT_CHANNEL_SPELL_5 = 4692; +const int FEAT_CHANNEL_SPELL_6 = 4693; +const int FEAT_CHANNEL_SPELL_7 = 4694; +const int FEAT_CHANNEL_SPELL_8 = 4695; +const int FEAT_CHANNEL_SPELL_9 = 4696; +const int FEAT_MULTIPLE_CHANNEL_SPELL_1 = 4697; +const int FEAT_MULTIPLE_CHANNEL_SPELL_2 = 4698; +const int FEAT_MULTIPLE_CHANNEL_SPELL_3 = 4699; + +//Epic Spellsword +const int FEAT_SPELLS_6 = 2534; +const int FEAT_SPELLS_7 = 2535; +const int FEAT_SPELLS_8 = 2536; +const int FEAT_SPELLS_9 = 2537; +const int FEAT_SPELLS_10 = 2538; +const int FEAT_SPELLS_11 = 2539; +const int FEAT_SPELLS_12 = 2540; +const int FEAT_SPELLS_13 = 2541; +const int FEAT_SPELLS_14 = 2542; +const int FEAT_SPELLS_15 = 2543; + +//Diabolist +const int FEAT_DIABOL_DIABOLISM_1 = 4236; +const int FEAT_DIABOL_DIABOLISM_2 = 4237; +const int FEAT_DIABOL_DIABOLISM_3 = 4238; +const int FEAT_DIABOL_VILEDIAB = 4239; +const int FEAT_DIABOL_IMPFAM = 4240; + +//Red Wizard of Thay +const int FEAT_RW_RES_ABJ = 4210; +const int FEAT_RW_RES_CON = 4211; +const int FEAT_RW_RES_DIV = 4212; +const int FEAT_RW_RES_ENC = 4213; +const int FEAT_RW_RES_EVO = 4214; +const int FEAT_RW_RES_ILL = 4215; +const int FEAT_RW_RES_NEC = 4216; +const int FEAT_RW_RES_TRS = 4217; +const int FEAT_RW_TF_ABJ = 4218; +const int FEAT_RW_TF_CON = 4219; +const int FEAT_RW_TF_DIV = 4220; +const int FEAT_RW_TF_ENC = 4221; +const int FEAT_RW_TF_EVO = 4222; +const int FEAT_RW_TF_ILL = 4223; +const int FEAT_RW_TF_NEC = 4224; +const int FEAT_RW_TF_TRS = 4225; +const int FEAT_RW_SPOWER_1 = 4226; +const int FEAT_RW_SPOWER_2 = 4227; +const int FEAT_RW_SPOWER_3 = 4228; +const int FEAT_RW_SPOWER_4 = 4229; +const int FEAT_RW_SPOWER_5 = 4230; +const int FEAT_RW_SDEF_1 = 4231; +const int FEAT_RW_SDEF_2 = 4232; +const int FEAT_RW_SDEF_3 = 4233; +const int FEAT_RW_SDEF_4 = 4234; + +//Acolyte of the Skin +const int FEAT_WEAR_FIEND = 2517; +const int FEAT_FLAME_RESISTANT = 2518; +const int FEAT_FIENDISH_GLARE = 2519; +const int FEAT_SKIN_ADAPTION = 2521; +const int FEAT_COLD_RESISTANT = 2522; +const int FEAT_PIT_GLARE = 2523; +const int FEAT_SUMMON_FIEND = 2525; +const int FEAT_SYMBIOSIS = 2526; +const int FEAT_EPIC_SYMBIOSIS_1 = 2544; //here and below: epic +const int FEAT_EPIC_SYMBIOSIS_2 = 2545; +const int FEAT_EPIC_SYMBIOSIS_3 = 2546; +const int FEAT_EPIC_SYMBIOSIS_4 = 2547; +const int FEAT_EPIC_CON_1 = 2527; +const int FEAT_EPIC_CON_2 = 2528; +const int FEAT_EPIC_CON_3 = 2529; +const int FEAT_EPIC_INT_1 = 2532; +const int FEAT_EPIC_INT_2 = 2533; +const int FEAT_ACID_RESISTANT = 2530; +const int FEAT_ELEC_RESISTANT = 2531; + +//Elemental Savant +const int FEAT_ES_TRANS_1 = 2550; +const int FEAT_ES_TRANS_2 = 2551; +const int FEAT_ES_TRANS_3 = 2552; +const int FEAT_ES_RESIST_1 = 2553; +const int FEAT_ES_RESIST_2 = 2554; +const int FEAT_ES_RESIST_3 = 2555; +const int FEAT_ES_FOCUS_1 = 2556; +const int FEAT_ES_FOCUS_2 = 2557; +const int FEAT_ES_FOCUS_3 = 2558; +const int FEAT_ES_PEN_1 = 2559; +const int FEAT_ES_PEN_2 = 2560; +const int FEAT_ES_PEN_3 = 2561; +const int FEAT_ES_PERFECTION = 2562; +const int FEAT_ES_FIRE = 2563; +const int FEAT_ES_COLD = 2564; +const int FEAT_ES_ELEC = 2565; +const int FEAT_ES_ACID = 2566; + +//Epic Elemental Savant + +const int FEAT_ES_FOCUS_4 = 2600; +const int FEAT_ES_FOCUS_5 = 2601; +const int FEAT_ES_FOCUS_6 = 2602; +const int FEAT_ES_FOCUS_7 = 2603; +const int FEAT_ES_FOCUS_8 = 2604; +const int FEAT_ES_FOCUS_9 = 2605; +const int FEAT_ES_FOCUS_10 = 2606; +const int FEAT_ES_PEN_4 = 2607; +const int FEAT_ES_PEN_5 = 2608; +const int FEAT_ES_PEN_6 = 2609; +const int FEAT_ES_PEN_7 = 2610; +const int FEAT_ES_PEN_8 = 2611; +const int FEAT_ES_PEN_9 = 2612; +const int FEAT_ES_PEN_10 = 2613; + +//Duelist +const int FEAT_CANNY_DEFENSE = 2800; +const int FEAT_PRECISE_STRIKE = 2801; +const int FEAT_GRACE = 2803; +const int FEAT_ACROBATIC_CHARGE = 2808; +const int FEAT_ELABORATE_PARRY = 2812; +const int FEAT_EPIC_DUELIST = 2813; + +//Hierophant +const int FEAT_BLAST_INFIDEL = 2814; +const int FEAT_FAITH_HEALING = 2815; +const int FEAT_MASTER_OF_ENERGY = 2816; +const int FEAT_SPELLPOWER_2 = 2817; +const int FEAT_SPELLPOWER_4 = 2818; +const int FEAT_SPELLPOWER_6 = 2819; +const int FEAT_SPELLPOWER_8 = 2820; +const int FEAT_SPELLPOWER_10 = 2821; +const int FEAT_SPELL_LIKE_ABILITY_1 = 2825; +const int FEAT_SPELL_LIKE_ABILITY_2 = 2826; +const int FEAT_SPELL_LIKE_ABILITY_3 = 2827; +const int FEAT_SPELL_LIKE_ABILITY_4 = 2828; +const int FEAT_SPELL_LIKE_ABILITY_5 = 2829; + +//Red Avenger +const int FEAT_FREE_KI_2 = 2750; +const int FEAT_FREE_KI_3 = 2751; +const int FEAT_FREE_KI_4 = 2752; + +// Shifter +const int FEAT_PRESTIGE_SHIFTER_NATURALSPELL = 2917; +const int FEAT_PRESTIGE_SHIFTER_EGWSHAPE_1 = 2906; +const int FEAT_PRESTIGE_SHIFTER_GWSHAPE_1 = 2900; +const int SHIFTER_BLACK_LIST = 2918; +const int FEAT_PRESTIGE_SHIFTER_LEARNSHAPE = 24438; + +//sanctify martial strike +const int FEAT_SANCTIFY_MARTIAL_CLUB = 3194; +const int FEAT_SANCTIFY_MARTIAL_DAGGER = 3195; +const int FEAT_SANCTIFY_MARTIAL_MACE = 3196; +const int FEAT_SANCTIFY_MARTIAL_LIGHTMACE = FEAT_SANCTIFY_MARTIAL_MACE; +const int FEAT_SANCTIFY_MARTIAL_MORNINGSTAR = 3197; +const int FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF= 3198; +const int FEAT_SANCTIFY_MARTIAL_SPEAR = 3199; +const int FEAT_SANCTIFY_MARTIAL_SHORTSPEAR = FEAT_SANCTIFY_MARTIAL_SPEAR; +const int FEAT_SANCTIFY_MARTIAL_SHORTSWORD = 3200; +const int FEAT_SANCTIFY_MARTIAL_RAPIER = 3201; +const int FEAT_SANCTIFY_MARTIAL_SCIMITAR = 3202; +const int FEAT_SANCTIFY_MARTIAL_LONGSWORD = 3203; +const int FEAT_SANCTIFY_MARTIAL_GREATSWORD = 3204; +const int FEAT_SANCTIFY_MARTIAL_HANDAXE = 3205; +const int FEAT_SANCTIFY_MARTIAL_BATTLEAXE = 3206; +const int FEAT_SANCTIFY_MARTIAL_GREATAXE = 3207; +const int FEAT_SANCTIFY_MARTIAL_HALBERD = 3208; +const int FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER = 3209; +const int FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL = 3210; +const int FEAT_SANCTIFY_MARTIAL_WARHAMMER = 3211; +const int FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL = 3212; +const int FEAT_SANCTIFY_MARTIAL_SCYTHE = 3213; +const int FEAT_SANCTIFY_MARTIAL_KATANA = 3214; +const int FEAT_SANCTIFY_MARTIAL_BASTARDSWORD = 3215; +const int FEAT_SANCTIFY_MARTIAL_DIREMACE = 3216; +const int FEAT_SANCTIFY_MARTIAL_DOUBLEAXE = 3217; +const int FEAT_SANCTIFY_MARTIAL_TWOBLADED = 3218; +const int FEAT_SANCTIFY_MARTIAL_KAMA = 3219; +const int FEAT_SANCTIFY_MARTIAL_KUKRI = 3220; +const int FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW = 3221; +const int FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW = 3222; +const int FEAT_SANCTIFY_MARTIAL_SLING = 3223; +const int FEAT_SANCTIFY_MARTIAL_LONGBOW = 3224; +const int FEAT_SANCTIFY_MARTIAL_SHORTBOW = 3225; +const int FEAT_SANCTIFY_MARTIAL_SHURIKEN = 3226; +const int FEAT_SANCTIFY_MARTIAL_DART = 3227; +const int FEAT_SANCTIFY_MARTIAL_DWAXE = 3170; +const int FEAT_SANCTIFY_MARTIAL_SICKLE = 3169; +const int FEAT_SANCTIFY_MARTIAL_MINDBLADE = 3623; +const int FEAT_SANCTIFY_MARTIAL_WHIP = 3596; +const int FEAT_SANCTIFY_MARTIAL_TRIDENT = 3597; +const int FEAT_SANCTIFYKISTRIKE = 26002; +const int FEAT_HOLYKISTRIKE = 26003; +const int FEAT_FISTOFHEAVENS = 26004; +const int FEAT_VOWABSTINENCE = 26005; +const int FEAT_VOWCHASTITY = 26006; +const int FEAT_GIFTOFFAITH = 26007; + +//heartwarder +const int FEAT_CHARISMA_INC1 = 3230; +const int FEAT_CHARISMA_INC2 = 3231; +const int FEAT_CHARISMA_INC3 = 3232; +const int FEAT_CHARISMA_INC4 = 3233; +const int FEAT_CHARISMA_INC5 = 3234; +const int FEAT_HEART_PASSION = 3235; +const int FEAT_LIPS_RAPTUR = 3236; +const int FEAT_VOICE_SIREN = 3237; +const int FEAT_TEARS_EVERGOLD = 3238; +const int FEAT_FEY_METAMORPH = 3239; +const int FEAT_ELECTRIC_RES_10 = 3240; +const int FEAT_ELECTRIC_RES_15 = 3241; +const int FEAT_ELECTRIC_RES_20 = 3242; +const int FEAT_ELECTRIC_RES_30 = 3243; +const int FEAT_SHOCK_WEAPON = 3244; +const int FEAT_THUNDER_WEAPON = 3245; +const int FEAT_SHOCKING_WEAPON = 3246; +const int FEAT_ELEMENTAL_CONFLAG = 3247; + +// Fist of Raziel +const int FEAT_SMITE_GOOD_ALIGN = 3248; +const int FEAT_HOLY_MARTIAL_STRIKE = 3228; +const int FEAT_MAGIC_CIRCLE_EVIL = 3229; + +const int FEAT_HEALER_10 = 4500; + +// CW Samurai +const int FEAT_KIAI_SMITE = 2352; + +//Master of Shrouds +const int FEAT_MOS_UNDEAD_1 = 3501; +const int FEAT_MOS_UNDEAD_2 = 3502; +const int FEAT_MOS_UNDEAD_3 = 3503; +const int FEAT_MOS_UNDEAD_4 = 3504; + +//Knight of the Chalice +const int DEMONSLAYING_1 = 2754; +const int DEMONSLAYING_2 = 2755; +const int DEMONSLAYING_3 = 2756; +const int DEMONSLAYING_4 = 2757; + +//Peerless Archer Feats +const int FEAT_PA_FLETCH_1 = 4284; +const int FEAT_PA_FLETCH_2 = 4285; +const int FEAT_PA_FLETCH_3 = 4286; +const int FEAT_PA_FLETCH_4 = 4287; +const int FEAT_PA_FLETCH_5 = 4288; +const int FEAT_PA_SNEAK_1D6 = 4289; +const int FEAT_PA_SNEAK_2D6 = 4290; +const int FEAT_PA_SNEAK_3D6 = 4291; +const int FEAT_PA_SNEAK_4D6 = 4292; +const int FEAT_PA_POWERSHOT = 4293; +const int FEAT_PA_IMP_POWERSHOT = 4294; +const int FEAT_PA_SUP_POWERSHOT = 4295; +const int FEAT_EXPERT_BOWYER = 4296; + +//Fist of Hextor Feats +const int FEAT_BSTRIKE_1 = 2669; +const int FEAT_BSTRIKE_2 = 2670; +const int FEAT_BSTRIKE_3 = 2671; +const int FEAT_BSTRIKE_4 = 2672; +const int FEAT_BSTRIKE_5 = 2673; +const int FEAT_BSTRIKE_6 = 2674; +const int FEAT_BSTRIKE_7 = 2675; +const int FEAT_BSTRIKE_8 = 2676; +const int FEAT_BSTRIKE_9 = 2683; +const int FEAT_BSTRIKE_10 = 2684; +const int FEAT_BSTRIKE_11 = 2685; +const int FEAT_BSTRIKE_12 = 2686; +const int FEAT_HEXTOR_BSTRIKE_MODE = 24439; + +//Thrall of Orcus +const int FEAT_TO_CARRION = 4404; +const int FEAT_TO_FEAR = 4405; +const int FEAT_TO_MNR_UNDEAD = 4409; +const int FEAT_TO_PALLOR = 4410; +const int FEAT_TO_DEATHTOUCH = 4411; +const int FEAT_TO_MJR_UNDEAD = 4412; +const int FEAT_TO_NIGHTWING = 4413; +const int AOE_MOB_CARRION = 117; +const int AOE_MOB_PALLOR = 118; + +//True Necromancer +const int AOE_MOB_DES_20 = 111; +const int AOE_MOB_DES_100 = 112; +const int FEAT_TN_CREATEUNDEAD = 4414; +const int FEAT_TN_CREATEGRUNDEAD = 4403; + +//Black Flame Zealot +const int FEAT_SACRED_FLAME = 4426; +const int FEAT_FATEFUL_STRIDE = 4427; + +// Shadowlord +const int SHADOWLORD_BLINDNESS = 2225; +const int SHADOWLORD_DARKNESS = 2226; +const int SHADOWLORD_INVISIBILITY = 2227; +const int SHADOWLORD_HASTE = 2229; +const int SHADOWLORD_IMPROVINVIS = 2230; +const int SHADOWLORD_VAMPITOUCH = 2231; +const int SHADOWLORD_CONFUSION = 2233; +const int SHADOWLORD_INVISPHERE = 2234; + +// Proper Shadow Pounce feat from Crinti Shadow Marauder +const int FEAT_SHADOW_RIDE = 5325; +const int FEAT_SHADOW_POUNCE = 5327; + +// Frenzied Berserker Feats +const int FEAT_FRENZY = 4300; +const int FEAT_REMOVE_FRENZY = 4309; +const int FEAT_GREATER_FRENZY = 4305; +const int FEAT_INSPIRE_FRENZY = 4306; +const int FEAT_REMAIN_CONSCIOUS = 4313; +const int FEAT_DEATHLESS_FRENZY = 4314; +const int FEAT_FREBZK_IMPROVED_POWER_ATTACK = 4310; +const int FEAT_SUPREME_POWER_ATTACK = 4311; +const int FEAT_INTIMIDATING_RAGE = 4312; + +// Eye of Gruumsh Feats +const int FEAT_COMMAND_THE_HORDE = 4600; +const int FEAT_BLINDING_SPITTLE = 4601; +const int FEAT_SWING_BLINDLY = 4602; +const int FEAT_RITUAL_SCARRING = 4603; +const int FEAT_SIGHT_OF_GRUUMSH = 4604; +const int FEAT_BLINDSIGHT_5_FT = 4605; +const int FEAT_BLINDSIGHT_10_FT = 4606; + +// Orc Warlord +const int FEAT_BLOOD_OF_THE_WARLORD = 4608; +const int FEAT_OW_INSPIRE_COURAGE = 4609; +const int FEAT_FINAL_RAGE = 4610; +const int FEAT_RELEASE_HORDE = 4611; +const int FEAT_GATHER_HORDE_I = 4612; +const int FEAT_GATHER_HORDE_II = 4613; + +// Tempest Feats +const int FEAT_GREATER_TWO_WEAPON_FIGHTING = 4315; +const int FEAT_SUPREME_TWO_WEAPON_FIGHTING = 4316; +const int FEAT_PERFECT_TWO_WEAPON_FIGHTING = 4320; +const int FEAT_TWO_WEAPON_DEFENSE = 4317; +const int FEAT_TWO_WEAPON_DEFENSE2 = 4318; +const int FEAT_TWO_WEAPON_DEFENSE3 = 4319; +const int FEAT_ABSOLUTE_AMBIDEX = 4321; + +// Foe Hunter Feats +const int FEAT_RANCOR = 4322; +const int FEAT_HATED_ENEMY_DR = 4323; +const int FEAT_HATED_ENEMY_SR = 4324; +const int FEAT_FH_DEATH_ATTACK = 4325; + +const int FEAT_HATED_ENEMY_DWARF = 4326; +const int FEAT_HATED_ENEMY_ELF = 4327; +const int FEAT_HATED_ENEMY_GNOME = 4328; +const int FEAT_HATED_ENEMY_HALFLING = 4329; +const int FEAT_HATED_ENEMY_HALFELF = 4330; +const int FEAT_HATED_ENEMY_HALFORC = 4331; +const int FEAT_HATED_ENEMY_HUMAN = 4332; +const int FEAT_HATED_ENEMY_ABERRATION = 4333; +const int FEAT_HATED_ENEMY_ANIMAL = 4334; +const int FEAT_HATED_ENEMY_BEAST = 4335; +const int FEAT_HATED_ENEMY_CONSTRUCT = 4336; +const int FEAT_HATED_ENEMY_DRAGON = 4337; +const int FEAT_HATED_ENEMY_GOBLINOID = 4338; +const int FEAT_HATED_ENEMY_MONSTROUS = 4339; +const int FEAT_HATED_ENEMY_ORC = 4340; +const int FEAT_HATED_ENEMY_REPTILIAN = 4341; +const int FEAT_HATED_ENEMY_ELEMENTAL = 4342; +const int FEAT_HATED_ENEMY_FEY = 4343; +const int FEAT_HATED_ENEMY_GIANT = 4344; +const int FEAT_HATED_ENEMY_MAGICAL_BEAST = 4345; +const int FEAT_HATED_ENEMY_OUTSIDER = 4346; +const int FEAT_HATED_ENEMY_SHAPECHANGER = 4347; +const int FEAT_HATED_ENEMY_UNDEAD = 4348; +const int FEAT_HATED_ENEMY_VERMIN = 4349; +const int FEAT_HATED_ENEMY_OOZE = 3469; +const int FEAT_HATED_ENEMY_PLANT = 3470; + +// Thrall of Graz'zt +const int FEAT_TOG_SUMMON_DEMON = 2889; + +// Shadowlord +const int FEAT_SHADOWBLUR = 3304; +const int FEAT_SHADOWDISCOPOR = 3305; +const int FEAT_DEATHATTACK = 3306; +const int FEAT_SHADOWJUMP = 3307; +const int FEAT_SHADOWJUMP2 = 3308; +const int FEAT_SHADOWJUMP3 = 3309; +const int FEAT_SHADOWPOUNCE = 3310; +const int FEAT_SHADOWPOUNCE2 = 3311; +const int FEAT_SHADOWPOUNCE3 = 3312; + +// Battleguard Tempus +const int TEMPUS_ENCHANT_WEAPON = 2220; + +// Bonded Summoner +const int FEAT_RESISTANCE_ELE5 = 3322; +const int FEAT_RESISTANCE_ELE10 = 3323; +const int FEAT_RESISTANCE_ELE15 = 3324; +const int FEAT_RESISTANCE_ELE20 = 3325; +const int FEAT_IMMUNITY_SNEAKATK = 3326; +const int FEAT_IMMUNITY_ELEMENT = 3327; +const int FEAT_IMMUNITY_CRITIK = 3328; +const int FEAT_BONDED_AIR = 3329; +const int FEAT_BONDED_EARTH = 3330; +const int FEAT_BONDED_FIRE = 3331; +const int FEAT_BONDED_WATER = 3332; +const int FEAT_ELE_COMPANION_MED = 3333; +const int FEAT_ELE_COMPANION_LAR = 3334; +const int FEAT_ELE_COMPANION_HUG = 3335; +const int FEAT_ELE_COMPANION_GRE = 3336; +const int FEAT_ELE_COMPANION_ELD = 3337; +const int FEAT_TYPE_ELEMENTAL = 3338; +const int FEAT_SHARE_SPELL = 3339; + +// Sneak Attack +const int FEAT_ROGUE_SNEAK_ATTACK_1D6 = 221; +const int FEAT_ROGUE_SNEAK_ATTACK_2D6 = 345; +const int FEAT_ROGUE_SNEAK_ATTACK_3D6 = 346; +const int FEAT_ROGUE_SNEAK_ATTACK_4D6 = 347; +const int FEAT_ROGUE_SNEAK_ATTACK_5D6 = 348; +const int FEAT_ROGUE_SNEAK_ATTACK_6D6 = 349; +const int FEAT_ROGUE_SNEAK_ATTACK_7D6 = 350; +const int FEAT_ROGUE_SNEAK_ATTACK_8D6 = 351; +const int FEAT_ROGUE_SNEAK_ATTACK_9D6 = 352; +const int FEAT_ROGUE_SNEAK_ATTACK_10D6 = 353; +const int FEAT_ROGUE_SNEAK_ATTACK_11D6 = 1032; +const int FEAT_ROGUE_SNEAK_ATTACK_12D6 = 1033; +const int FEAT_ROGUE_SNEAK_ATTACK_13D6 = 1034; +const int FEAT_ROGUE_SNEAK_ATTACK_14D6 = 1035; +const int FEAT_ROGUE_SNEAK_ATTACK_15D6 = 1036; +const int FEAT_ROGUE_SNEAK_ATTACK_16D6 = 1037; +const int FEAT_ROGUE_SNEAK_ATTACK_17D6 = 1038; +const int FEAT_ROGUE_SNEAK_ATTACK_18D6 = 1039; +const int FEAT_ROGUE_SNEAK_ATTACK_19D6 = 1040; +const int FEAT_ROGUE_SNEAK_ATTACK_20D6 = 1041; + +//Iaijutsu Master Feats + Skill + +//const int SKILL_IAIJUTSU_FOCUS = 27; //CONSTANT Moved to prc_misc_const +const int FEAT_SKILL_FOCUS_IAI = 2377; +const int FEAT_EPIC_SKILL_FOCUS_IAI = 2378; +const int FEAT_EPIC_IAIJUTSU_FOCUS = 2315; //CONSTANT +const int FEAT_STRIKE_VOID = 2316; //CONSTANT +const int FEAT_TWO_CUTS = 2317; //CONSTANT +const int FEAT_ECHOS_EDGE_1 = 2318; //CONSTANT +const int FEAT_ECHOS_EDGE_2 = 2319; //CONSTANT +const int FEAT_ECHOS_EDGE_3 = 2320; //CONSTANT +const int FEAT_ECHOS_EDGE_4 = 2321; //CONSTANT +const int FEAT_ECHOS_EDGE_5 = 2322; //CONSTANT +const int FEAT_ECHOS_EDGE_6 = 2323; //CONSTANT +const int FEAT_SPIRIT_STRIKE_1 = 2324; //CONSTANT +const int FEAT_SPIRIT_STRIKE_2 = 2325; //CONSTANT +const int FEAT_SPIRIT_STRIKE_3 = 2326; //CONSTANT +const int FEAT_SPIRIT_STRIKE_4 = 2327; //CONSTANT +const int FEAT_SPIRIT_STRIKE_5 = 2328; //CONSTANT +const int FEAT_SPIRIT_STRIKE_6 = 2329; //CONSTANT +const int FEAT_KATANA_FINESSE = 2330; //CONSTANT +const int FEAT_IAIJUTSU_ATTACK = 2331; //CONSTANT +const int FEAT_EPIC_IAIJUTSU = 2332; + +//Legendary Dread Feats +const int FEAT_UNSTOPPABLE_1 = 2300; //CONSTANT +const int FEAT_UNSTOPPABLE_2 = 2301; //CONSTANT +const int FEAT_UNSTOPPABLE_3 = 2302; //CONSTANT +const int FEAT_UNSTOPPABLE_4 = 2303; //CONSTANT +const int FEAT_UNMOVABLE_1 = 2304; //CONSTANT +const int FEAT_UNMOVABLE_2 = 2305; //CONSTANT +const int FEAT_UNMOVABLE_3 = 2306; //CONSTANT +const int FEAT_UNMOVABLE_4 = 2307; //CONSTANT + +//Disciple of Baalzebul Feats +const int FEAT_KING_LIES = 2308; //CONSTANT +const int FEAT_TONGUE_DEVIL = 2309; //CONSTANT +const int FEAT_SUGGESTION = 2310; //CONSTANT +const int FEAT_SUMMON_OSYLUTH = 2311; //CONSTANT +const int FEAT_INSECT_PLAGUE = 2312; //CONSTANT +const int FEAT_SUMMON_CORNUGON = 2313; //CONSTANT +const int FEAT_BEGUILEING_NATURE = 2314; //CONSTANT + +const int FEAT_DEVICE_LORE = 2333; +const int FEAT_IRON_HEWS = 2334; +const int FEAT_RUSTING_GRASP = 2335; +const int FEAT_IRON_POWER_1 = 2336; +const int FEAT_IRON_POWER_2 = 2337; +const int FEAT_SUMMON_ERINYES = 2338; +const int FEAT_GREATER_IRON_HEWS = 2339; +const int FEAT_IRON_SKIN = 2340; +const int FEAT_IRON_BODY = 2341; + +const int FEAT_COFFIN_SANCTUARY = 2343; +const int FEAT_GASEOUS_FORM = 2344; + +const int FEAT_WEAPON_TEMPUS_CLUB = 3268; +const int FEAT_WEAPON_TEMPUS_DAGGER = 3269; +const int FEAT_WEAPON_TEMPUS_MACE = 3270; +const int FEAT_WEAPON_TEMPUS_MORNINGSTAR = 3271; +const int FEAT_WEAPON_TEMPUS_QUARTERSTAFF= 3272; +const int FEAT_WEAPON_TEMPUS_SPEAR = 3273; +const int FEAT_WEAPON_TEMPUS_SHORTSWORD = 3274; +const int FEAT_WEAPON_TEMPUS_RAPIER = 3275; +const int FEAT_WEAPON_TEMPUS_SCIMITAR = 3276; +const int FEAT_WEAPON_TEMPUS_LONGSWORD = 3277; +const int FEAT_WEAPON_TEMPUS_GREATSWORD = 3278; +const int FEAT_WEAPON_TEMPUS_HANDAXE = 3279; +const int FEAT_WEAPON_TEMPUS_BATTLEAXE = 3280; +const int FEAT_WEAPON_TEMPUS_GREATAXE = 3281; +const int FEAT_WEAPON_TEMPUS_HALBERD = 3282; +const int FEAT_WEAPON_TEMPUS_LIGHTHAMMER = 3283; +const int FEAT_WEAPON_TEMPUS_LIGHTFLAIL = 3284; +const int FEAT_WEAPON_TEMPUS_WARHAMMER = 3285; +const int FEAT_WEAPON_TEMPUS_HEAVYFLAIL = 3286; +const int FEAT_WEAPON_TEMPUS_SCYTHE = 3287; +const int FEAT_WEAPON_TEMPUS_KATANA = 3288; +const int FEAT_WEAPON_TEMPUS_BASTARDSWORD= 3289; +const int FEAT_WEAPON_TEMPUS_DIREMACE = 3290; +const int FEAT_WEAPON_TEMPUS_DOUBLEAXE = 3291; +const int FEAT_WEAPON_TEMPUS_TWOBLADED = 3292; +const int FEAT_WEAPON_TEMPUS_KAMA = 3293; +const int FEAT_WEAPON_TEMPUS_KUKRI = 3294; +const int FEAT_ARMY_POWER = 3295; +const int FEAT_BATTLEFORGER = 3296; +const int FEAT_DIEHARD = 3297; +const int FEAT_ENCHANT_WEAPON1 = 3299; +const int FEAT_ENCHANT_WEAPON2 = 3300; +const int FEAT_ENCHANT_WEAPON3 = 3301; +const int FEAT_WEAPON_TEMPUS_SICKLE = 3171; +const int FEAT_WEAPON_TEMPUS_DWARVENAXE = 3172; +const int FEAT_WEAPON_TEMPUS_MINDBLADE = 3625; + + +// Initiate of Draconic +const int FEAT_CLAWDRAGON = 3341; +const int FEAT_INCREASE_DAMAGE1 = 3342; +const int FEAT_INCREASE_DAMAGE2 = 3343; +const int FEAT_INIDR_SPELLRESISTANCE = 3345; +const int FEAT_INIDR_STUNSTRIKE = 3346; +const int FEAT_INIDR_SHAPEDRAGON = 3347; +const int FEAT_CLAWENH2 = 3348; +const int FEAT_CLAWENH3 = 3349; +const int FEAT_EPIC_INCREASE_DAMAGE1 = 3186; +const int FEAT_EPIC_INCREASE_DAMAGE2 = 3187; +const int FEAT_EPIC_INCREASE_DAMAGE3 = 3188; +const int FEAT_EPIC_INCREASE_DAMAGE4 = 3189; +const int FEAT_EPIC_INCREASE_DAMAGE5 = 3177; + +//:: Bladesinger +const int FEAT_BLADESONG_STYLE = 3190; +const int FEAT_LESSER_SPELLSONG = 3191; +const int FEAT_GREATER_SPELLSONG = 3192; +const int FEAT_SONG_OF_FURY = 3193; + +// Archer +const int FEAT_EXTRASHOT = 3180; +const int FEAT_BOWSPEC2 = 3181; +const int FEAT_BOWSPEC3 = 3182; +const int FEAT_BOWSPEC4 = 3183; +const int FEAT_BOWSPEC5 = 3184; +const int FEAT_BOWSPEC6 = 3185; +const int FEAT_BOWSPEC7 = 3107; +const int FEAT_BOWSPEC8 = 3108; +const int FEAT_BOWSPEC9 = 3109; + +const int FEAT_KILLINGSHOT = 3352; +const int FEAT_PERFECTSHOT = 3353; +const int FEAT_PERFECTSHOT2 = 3354; + +const int FEAT_BOWMASTERY = 3174; +const int FEAT_XBOWMASTERY = 3175; +const int FEAT_SHURIKENMASTERY = 3176; + +// Man At Arm + +const int FEAT_GENERAL_SPECIALIZATION = 3100; +const int FEAT_MASTER_CRITICAL = 3101; +const int FEAT_FOCUSED_STRIKE = 3102; +const int FEAT_STRIKE_AT_CORE = 3103; +const int FEAT_FURIOUS_ASSAULT = 3104; +const int FEAT_LEGENDARY_PROWESS = 3105; + + +//Soldier of Light +const int FEAT_SOL_DIVINE_GRACE = 3140; +const int FEAT_SMITE_UNDEAD = 3139; +const int FEAT_POSITIVE_FORTITUDE = 3140; +const int FEAT_ENERGON_COMPANION = 3141; +const int FEAT_POSITIVE_ENERGY_BURST = 3146; +const int FEAT_DIVINE_VENGEANCE = 3142; +const int FEAT_SOL_FAST_HEALING_1 = 3137; +const int FEAT_SOL_FAST_HEALING_2 = 3138; +const int FEAT_SOL_FAST_HEALING_3 = 3314; +const int FEAT_MAXIMIZE_TURNING = 3315; +const int FEAT_SUP_POSITIVE_ENERGY_BURST = 3316; + +// Blood Archer +const int FEAT_BLARCH_POISON_BLOOD = 4200; + +// Henshin Mystic feat.2da spell.2da +const int FEAT_PRESTIGE_RIDDLE_OF_AWARENESS = 4020; // 2260 +const int FEAT_PRESTIGE_HAPPO_ZANSHIN = 4021; // 2261 +const int FEAT_PRESTIGE_RIDDLE_OF_INTERACTION = 4022; // 2262 +const int FEAT_PRESTIGE_BLINDSIGHT = 4023; // 2263 +const int FEAT_PRESTIGE_HITSU_DO = 4024; // 2264 +const int FEAT_PRESTIGE_WALK_THROUGH_THE_MOUNTAINS = 4025; // 2265 +const int FEAT_PRESTIGE_RIDDLE_OF_INVULNERABILITY = 4026; // 2266 + +// Drunken Master +const int FEAT_PRESTIGE_DRINK_LIKE_A_DEMON = 4027; // 2267 +const int FEAT_PRESTIGE_BOTTLE_PROFICIENCY = 4028; // 2268 +const int FEAT_PRESTIGE_STAGGER = 4029; // 2269 +const int FEAT_PRESTIGE_SWAYING_WAIST = 4030; // 2270 +const int FEAT_PRESTIGE_DRUNKEN_RAGE = 4031; // 2271 +const int FEAT_PRESTIGE_LURCH = 4032; // 2272 +const int FEAT_PRESTIGE_DRUNKEN_EMBRACE = 4033; // 2273 +const int FEAT_PRESTIGE_FOR_MEDICINAL_PURPOSES = 4034; // 2274 +const int FEAT_PRESTIGE_BREATH_OF_FLAME = 4035; // 2275 + +//Hathran class feats +const int FEAT_HATH_COHORT = 2571; //CONSTANT +const int FEAT_HATH_COMMAND = 2572; //CONSTANT +const int FEAT_HATH_FEAR1 = 2575; //CONSTANT +const int FEAT_HATH_FEAR2 = 2576; //CONSTANT [DEFUNCT] +const int FEAT_HATH_FEAR3 = 2577; //CONSTANT [DEFUNCT] + +//Vigilant class feats +const int FEAT_VIGIL_ARMOR = 2570; //CONSTANT +const int FEAT_SPRINT = 2573; //CONSTANT +const int FEAT_VIGIL_HEAL = 2574; //CONSTANT + +//Prerequisitie feats (Hathran and Vigilant) +const int FEAT_ETHRAN = 2567; //CONSTANT +const int FEAT_ENDURANCE = 2568; //CONSTANT +const int FEAT_TRACK = 2569; //CONSTANT + +// Sacred Fist +const int FEAT_SF_AC1 = 2933; +const int FEAT_SF_AC2 = 2934; +const int FEAT_SF_AC3 = 2935; +const int FEAT_SF_AC4 = 2936; +const int FEAT_SF_AC5 = 2937; +const int FEAT_SF_AC6 = 2938; +const int FEAT_SF_AC7 = 2939; +const int FEAT_SF_UNARMEDDMG = 2932; +const int FEAT_SF_INNERARMOR = 2940; +const int FEAT_SF_SACREDFLAME1 = 2941; +const int FEAT_SF_SACREDFLAME2 = 2942; +const int FEAT_SF_SACREDFLAME3 = 2943; +const int FEAT_SF_SACREDFLAME4 = 2944; +const int FEAT_SF_SACREDFLAME5 = 2945; +const int FEAT_SF_SACREDFLAME6 = 2946; +const int FEAT_SF_SACREDFLAME7 = 2947; +const int FEAT_SF_SPEED1 = 2948; +const int FEAT_SF_SPEED2 = 2949; +const int FEAT_SF_SPEED3 = 2950; +const int FEAT_SF_CODE = 2951; + +// Shadow Adept +const int FEAT_SA_SHADOWDEF1 = 2952; +const int FEAT_SA_SHADOWDEF2 = 2953; +const int FEAT_SA_SHADOWDEF3 = 2954; +const int FEAT_SA_SHADOWDEPIC = 2955; +const int FEAT_SA_SHIELDSHADOW = 2956; +const int FEAT_SA_GREATSHIELDSHADOW = 2957; +const int FEAT_SA_SHADOWWALK = 2958; +const int FEAT_SA_SHADOWDOUBLE = 2959; + +// Brawler +const int FEAT_BRAWLER_FISTS = 4428; +const int FEAT_EPIC_BRAWLER = 4437; +const int FEAT_BRAWLER_BLOCK_1 = 4438; +const int FEAT_BRAWLER_BLOCK_2 = 4439; +const int FEAT_BRAWLER_BLOCK_3 = 4440; +const int FEAT_BRAWLER_BLOCK_4 = 4441; +const int FEAT_BRAWLER_BLOCK_5 = 4442; +const int FEAT_BRAWLER_EXTRAATT_1 = 4429; +const int FEAT_BRAWLER_EXTRAATT_2 = 4430; +const int FEAT_BRAWLER_EXTRAATT_3 = 4431; +const int FEAT_BRAWLER_DAMAGE_REDUCTION_3 = 4432; +const int FEAT_BRAWLER_DAMAGE_REDUCTION_6 = 4433; +const int FEAT_BRAWLER_DAMAGE_REDUCTION_9 = 4434; + +// Minstrel of the Edge +const int FEAT_MINSTREL_LIGHT_ARMOR_CASTING = 4435; +const int FEAT_MINSTREL_SONG_SLEEP = 4443; +const int FEAT_MINSTREL_SONG_SILENCE = 4444; +const int FEAT_MINSTREL_SONG_HASTE = 4445; +const int FEAT_MINSTREL_SONG_SLOW = 4446; +const int FEAT_MINSTREL_SONG_CHARM = 4447; +const int FEAT_MINSTREL_SONG_IMM_FEAR = 4449; +const int FEAT_MINSTREL_SONG_SHIELD_AC = 4450; +const int FEAT_MINSTREL_SONG_SONIC_WEAP = 4451; +const int FEAT_MINSTREL_SONG_STRENGTH = 4452; +const int FEAT_MINSTREL_SONG_DEXTERITY = 4453; +const int FEAT_MINSTREL_SONG_CONSTITUTION = 4454; +const int FEAT_MINSTREL_SONG_INTELLIGENCE = 4455; +const int FEAT_MINSTREL_SONG_WISDOM = 4456; +const int FEAT_MINSTREL_SONG_CHARISMA = 4457; +const int FEAT_MINSTREL_SONG_WOUND_WHISP = 4458; +const int FEAT_MINSTREL_GREATER_MINSTREL_SONG = 4448; + +// Nightshade +const int FEAT_NS_LIGHT_ADAPTION = 2964; +const int FEAT_NS_WEBWALKER = 2965; +const int FEAT_NS_SNEAKATK_1D6 = 2966; +const int FEAT_NS_SNEAKATK_2D6 = 2967; +const int FEAT_NS_SNEAKATK_3D6 = 2968; +const int FEAT_NS_INVISIBILITY = 2969; +const int FEAT_NS_POISON_IMMUNE = 2970; +const int FEAT_NS_POISON_SPITTLE = 2971; +const int FEAT_NS_WEB = 2972; +const int FEAT_NS_SHADOWWALK = 2973; + +// Runescarred Berserker +const int FEAT_RIT_SCAR = 2369; +const int FEAT_SPAWNFROST = 2371; +const int FEAT_RIT_DR = 2370; +const int FEAT_RIT_SCAR_2 = 2375; +const int FEAT_RIT_SCAR_3 = 2376; + +// Ultimate Ranger +const int FEAT_UR_FE_DWARF = 2974; +const int FEAT_UR_FE_ELF = 2975; +const int FEAT_UR_FE_GNOME = 2976; +const int FEAT_UR_FE_HALFING = 2977; +const int FEAT_UR_FE_HALFELF = 2978; +const int FEAT_UR_FE_HALFORC = 2979; +const int FEAT_UR_FE_HUMAN = 2980; +const int FEAT_UR_FE_ABERRATION = 2981; +const int FEAT_UR_FE_ANIMAL = 2982; +const int FEAT_UR_FE_BEAST = 2983; +const int FEAT_UR_FE_CONSTRUCT = 2984; +const int FEAT_UR_FE_DRAGON = 2985; +const int FEAT_UR_FE_GOBLINOID = 2986; +const int FEAT_UR_FE_MONSTROUS = 2987; +const int FEAT_UR_FE_ORC = 2988; +const int FEAT_UR_FE_REPTILIAN = 2989; +const int FEAT_UR_FE_ELEMENTAL = 2990; +const int FEAT_UR_FE_FEY = 2991; +const int FEAT_UR_FE_GIANT = 2992; +const int FEAT_UR_FE_MAGICAL_BEAST = 2993; +const int FEAT_UR_FE_OUTSIDER = 2994; +const int FEAT_UR_FE_SHAPECHANGER = 2995; +const int FEAT_UR_FE_UNDEAD = 2996; +const int FEAT_UR_FE_VERMIN = 2997; +const int FEAT_UR_SNAREMASTERY = 2998; +const int FEAT_UR_SNEAKATK_3D6 = 2999; +const int FEAT_UR_ARMOREDGRACE = 3430; +const int FEAT_UR_DODGE_FE = 3431; +const int FEAT_UR_RESIST_FE = 3432; +const int FEAT_UR_HAWK_TOTEM = 3433; +const int FEAT_UR_OWL_TOTEM = 3434; +const int FEAT_UR_VIPER_TOTEM = 3435; +const int FEAT_UR_GRACE1 = 3436; +const int FEAT_UR_GRACE2 = 3437; +const int FEAT_UR_GRACE3 = 3438; +const int FEAT_UR_GRACE4 = 3439; +const int FEAT_UR_FAST_MOVEMENT = 3097; +const int FEAT_UNCANNYX_DODGE_1 = 3098; +const int FEAT_LINGERING_DAMAGE = 3099; +const int FEAT_UR_HIPS = 3136; + +// Dragonsong Lyrist +const int FEAT_DRAGONSONG_STRENGTH = 3441; +const int FEAT_DRAGONSONG_COMPULSION = 3442; +const int FEAT_DRAGONSONG_SPEED = 3443; +const int FEAT_DRAGONSONG_FEAR = 3444; +const int FEAT_DRAGONSONG_HEALING = 3445; +const int FEAT_EPIC_DRAGONSONG_STRENGTH = 3496; +const int FEAT_EPIC_DRAGONSONG_COMPULSION = 3497; +const int FEAT_EPIC_DRAGONSONG_SPEED = 3498; +const int FEAT_EPIC_DRAGONSONG_FEAR = 3499; +const int FEAT_EPIC_DRAGONSONG_HEALING = 3500; +const int FEAT_FOCUS_DRAGONSONG = 3318; +const int FEAT_GREATER_FOCUS_DRAGONSONG = 3319; +const int FEAT_EPIC_FOCUS_DRAGONSONG = 3321; + +// Favored of Milil +const int FEAT_FOM_DIVINE_SONG_BLESS = 24181; +const int FEAT_FOM_DIVINE_SONG_CONVICTION = 24182; +const int FEAT_FOM_DIVINE_SONG_CURELIGHT = 24183; +const int FEAT_FOM_DIVINE_SONG_REMOVEFEAR = 24184; +const int FEAT_FOM_DIVINE_SONG_SANCTUARY = 24185; +const int FEAT_FOM_DIVINE_SONG_SHIELDFAITH = 24186; +const int FEAT_FOM_DIVINE_SONG_AID = 24187; +const int FEAT_FOM_DIVINE_SONG_BULLSTRENGTH = 24188; +const int FEAT_FOM_DIVINE_SONG_CUREMODERATE = 24189; +const int FEAT_FOM_DIVINE_SONG_EAGLESPLENDOR = 24190; +const int FEAT_FOM_DIVINE_SONG_ENDURANCE = 24191; +const int FEAT_FOM_DIVINE_SONG_FOXCUNNING = 24192; +const int FEAT_FOM_DIVINE_SONG_LESSRESTORE = 24193; +const int FEAT_FOM_DIVINE_SONG_OWLWISDOM = 24194; +const int FEAT_FOM_DIVINE_SONG_CLAIRVOYANCE = 24195; +const int FEAT_FOM_DIVINE_SONG_CLARITY = 24196; +const int FEAT_FOM_DIVINE_SONG_CURESERIOUS = 24197; +const int FEAT_FOM_DIVINE_SONG_NEGATIVEPROT = 24198; +const int FEAT_FOM_DIVINE_SONG_PRAYER = 24199; +const int FEAT_FOM_DIVINE_SONG_PROTELEMENTS = 24200; +const int FEAT_FOM_DIVINE_SONG_REMOVECURSE = 24201; +const int FEAT_FOM_DIVINE_SONG_CURECRITICAL = 24202; +const int FEAT_FOM_DIVINE_SONG_DEATHWARD = 24203; +const int FEAT_FOM_DIVINE_SONG_FREEDOMMOVEMENT = 24204; +const int FEAT_FOM_DIVINE_SONG_PANACEA = 24205; +const int FEAT_FOM_DIVINE_SONG_RESTORATION = 24206; +const int FEAT_FOM_DIVINE_SONG_STONESKIN = 24207; +const int FEAT_FOM_DIVINE_SONG_TRUESEEING = 24208; +const int FEAT_FOM_DIVINE_SONG_MONSTREGEN = 24209; +const int FEAT_FOM_DIVINE_SONG_RAISEDEAD = 24210; +const int FEAT_FOM_DIVINE_SONG_SPELLRESISTANCE = 24211; +const int FEAT_FOM_ENCORE_PERFORMANCE = 24212; +const int FEAT_FOM_GREATER_DIVINE_SONG = 24213; + +// Evil-Paladin +const int FEAT_HIDDENFAITH = 3484; +const int FEAT_AP_TURN_OUTSIDER = 3486; +const int FEAT_APAL_MOUNT = 24238; + +// Outlaw Crimson Road +const int FEAT_FUGITIVE_LUCK1 = 2798; +const int FEAT_FUGITIVE_LUCK2 = 2799; +const int FEAT_FUGITIVE_LUCK3 = 2877; +const int FEAT_FUGITIVE_LUCKEPIC = 2879; +const int FEAT_OUT_LEADERSHIP = 2880; +const int FEAT_OUT_LEGENDS = 2881; + +// Alaghar +const int FEAT_CLANGEDDINS_STRIKE = 3991; +const int FEAT_CLANGEDDINS_MIGHT = 3992; +const int FEAT_ALAG_SILVERBEARD = 3993; +const int FEAT_ALAG_ROCKBURST = 3994; + +// Soul Eater +const int FEAT_SLEAT_ENERGY_DRAIN = 3995; +const int FEAT_SLEAT_SBLAST = 3996; +const int FEAT_SLEAT_SRADIANCE = 3997; + +// Combat Medic +const int FEAT_HEALING_KICKER_1 = 3540; +const int FEAT_HEALING_KICKER_2 = 3541; +const int FEAT_HEALING_KICKER_3 = 3542; + +// Crafting +const int FEAT_CRAFT_WONDROUS = 2925; +const int FEAT_CRAFT_ARMS_ARMOR = 2926; +const int FEAT_CRAFT_ROD = 2927; +const int FEAT_CRAFT_STAFF = 2928; +const int FEAT_FORGE_RING = 2929; +const int FEAT_CRAFT_CONSTRUCT = 2920; +const int FEAT_CRAFT_ITEM = 2919;//combined with read recepie, was 2899 +const int FEAT_CRAFT_EPIC_WONDROUS_ITEM = 3488; +const int FEAT_CRAFT_EPIC_MAGIC_ARMS_ARMOR = 3489; +const int FEAT_CRAFT_EPIC_ROD = 3490; +const int FEAT_CRAFT_EPIC_STAFF = 3491; +const int FEAT_FORGE_EPIC_RING = 3528; +const int FEAT_EXCEPTIONAL_ARTISAN_I = 2872; +const int FEAT_EXCEPTIONAL_ARTISAN_II = 2873; +const int FEAT_EXCEPTIONAL_ARTISAN_III = 2874; +const int FEAT_EXTRAORDINARY_ARTISAN_I = 2875; +const int FEAT_EXTRAORDINARY_ARTISAN_II = 2876; +const int FEAT_EXTRAORDINARY_ARTISAN_III = 2893; +const int FEAT_LEGENDARY_ARTISAN_I = 2894; +const int FEAT_LEGENDARY_ARTISAN_II = 2895; +const int FEAT_LEGENDARY_ARTISAN_III = 2896; + +// Exalted Companion +const int FEAT_EXALTED_COMPANION = 4199; + +// Dragon Disciple +const int FEAT_RED_DRAGON = 4100; +const int FEAT_SILVER_DRAGON = 4101; +const int FEAT_BLACK_DRAGON = 4102; +const int FEAT_BLUE_DRAGON = 4103; +const int FEAT_GREEN_DRAGON = 4104; +const int FEAT_WHITE_DRAGON = 4105; +const int FEAT_BRASS_DRAGON = 4106; +const int FEAT_BRONZE_DRAGON = 4107; +const int FEAT_COPPER_DRAGON = 4108; +const int FEAT_GOLD_DRAGON = 4109; +const int FEAT_AMETHYST_DRAGON = 4110; +const int FEAT_CRYSTAL_DRAGON = 4111; +const int FEAT_EMERALD_DRAGON = 4112; +const int FEAT_SAPPHIRE_DRAGON = 4113; +const int FEAT_TOPAZ_DRAGON = 4114; +const int FEAT_BATTLE_DRAGON = 4115; +const int FEAT_BROWN_DRAGON = 4116; +const int FEAT_CHAOS_DRAGON = 4117; +const int FEAT_DEEP_DRAGON = 4118; +const int FEAT_ETHEREAL_DRAGON = 4119; +const int FEAT_FANG_DRAGON = 4120; +const int FEAT_HOWLING_DRAGON = 4121; +const int FEAT_OCEANUS_DRAGON = 4122; +const int FEAT_PYROCLASTIC_DRAGON = 4123; +const int FEAT_RADIANT_DRAGON = 4124; +const int FEAT_RUST_DRAGON = 4125; +const int FEAT_SHADOW_DRAGON = 4126; +const int FEAT_SONG_DRAGON = 4127; +const int FEAT_STYX_DRAGON = 4128; +const int FEAT_TARTIAN_DRAGON = 4129; +const int FEAT_DRACONIC_IMMUNITY = 4130; +const int FEAT_CHIANG_LUNG_DRAGON = 4131; +const int FEAT_LI_LUNG_DRAGON = 4132; +const int FEAT_LUNG_WANG_DRAGON = 4133; +const int FEAT_PAN_LUNG_DRAGON = 4134; +const int FEAT_SHEN_LUNG_DRAGON = 4135; +const int FEAT_TIEN_LUNG_DRAGON = 4136; +const int FEAT_TUN_MI_LUNG_DRAGON = 4137; +const int FEAT_YU_LUNG_DRAGON = 4138; +const int FEAT_DRAGON_DIS_BREATH_2 = 4139; +const int FEAT_DRAGON_DIS_BREATH_3 = 4140; +const int FEAT_DRAGON_DIS_BREATH_4 = 4141; +const int FEAT_DRAGON_DIS_BREATH_5 = 4142; +const int FEAT_DRAGON_DIS_BREATH_6 = 4143; +const int FEAT_DRAGON_DIS_BREATH_7 = 4144; +const int DRACONIC_ARMOR_AUG_1 = 4148; +const int DRACONIC_SPELLRESIST = 4149; +const int DRACONIC_ARMOR_AUG_2 = 4150; +const int DRAGON_BLOODED = 4151; +const int FEAT_DRACONIC_SIZE_INCREASE_1 = 4152; +const int FEAT_DRACONIC_SIZE_INCREASE_2 = 4153; +const int DRACONIC_CLAWS = 4162; +const int DRACONIC_BITE = 4163; +const int DRACONIC_WINGSLAMS = 4164; +const int DRACONIC_TAILSLAP = 4165; + +// Complete Adv Ninja +const int FEAT_NINJA_AC_BONUS = 2483; +const int FEAT_EPIC_ACROBATICS_8 = 2484; +const int FEAT_EPIC_ACROBATICS_10 = 2485; +const int FEAT_EPIC_ACROBATICS_12 = 2486; +const int FEAT_EPIC_NINJA_CA = 2487; +const int FEAT_KI_POWER = 2488; +const int FEAT_ACROBATICS_2 = 2489; +const int FEAT_ACROBATICS_4 = 2490; +const int FEAT_ACROBATICS_6 = 2491; +const int FEAT_GHOST_SIGHT = 2492; +const int FEAT_GHOST_STEP = 2493; +const int FEAT_GHOST_STRIKE = 2494; +const int FEAT_GHOST_WALK = 2495; +const int FEAT_GREAT_LEAP = 2496; +const int FEAT_GREATER_KI_DODGE = 2497; +const int FEAT_KI_DODGE = 2498; +const int FEAT_GHOST_STEP_2 = 2499; + + +//:: Forest Master (Faiths & Pantheons, pg. 193) +const int FEAT_FM_TREEBROTHER = 5436; +const int FEAT_FM_NATURESENSE = 5437; +const int FEAT_FM_FOREST_DOMINION = 5438; +const int FEAT_FM_MALLET_MASTER = 5439; +const int FEAT_FM_ICY_MALLET = 5440; +const int FEAT_FM_SHOCK_MALLET = 5441; +const int FEAT_FM_NATURAL_ARMOR = 5442; +const int FEAT_FM_OAK_STRENGTH = 5443; +const int FEAT_FM_SPRUCE_GROWTH = 5444; +const int FEAT_FM_OAKHEART = 5445; +const int FEAT_FM_DEEP_ROOTS = 5446; +const int FEAT_FM_LONGEVITY = 5447; +const int FEAT_FM_FOREST_MIGHT = 5448; + +// Fast Healing +const int FAST_HEALING_1 = 4145; +const int FAST_HEALING_2 = 4146; +const int FAST_HEALING_3 = 4147; + +// General Feats +const int FEAT_STORMMAGIC = 4182; +const int FEAT_MAGICAL_APTITUDE = 4416; +const int FEAT_NEGOTIATOR = 3485; + +// Warmage Feat +const int FEAT_TYPE_EXTRA_EDGE = 2779; +const int FEAT_SUDDEN_EMPOWER = 2830; +const int FEAT_SUDDEN_EXTEND = 2831; +const int FEAT_SUDDEN_MAXIMIZE = 2832; +const int FEAT_SUDDEN_WIDEN = 2833; + +// Size changes +const int FEAT_SIZE_DECREASE_1 = 2287; +const int FEAT_SIZE_DECREASE_2 = 2288; +const int FEAT_SIZE_DECREASE_3 = 2289; +const int FEAT_SIZE_DECREASE_4 = 2290; +const int FEAT_SIZE_DECREASE_5 = 2291; +const int FEAT_SIZE_DECREASE_6 = 2292; +const int FEAT_SIZE_INCREASE_1 = 2293; +const int FEAT_SIZE_INCREASE_2 = 2294; +const int FEAT_SIZE_INCREASE_3 = 2295; +const int FEAT_SIZE_INCREASE_4 = 2296; +const int FEAT_SIZE_INCREASE_5 = 2297; +const int FEAT_SIZE_INCREASE_6 = 2298; + + + +/*////////////////////////////////////////////////// +////////////////CODI STUFF////////////////////////// +//////////////////////////////////////////////////*/ + +//Ninja Spy +const int SPELLABILITY_NS_MEDIUM = 1528; +const int SPELLABILITY_NS_SMALL = 1529; +const int SPELLABILITY_NS_DWARF = 1530; +const int SPELLABILITY_NS_ELF = 1531; +const int SPELLABILITY_NS_HALF_ELF = 1532; +const int SPELLABILITY_NS_HALF_ORC = 1533; +const int SPELLABILITY_NS_HUMAN = 1534; +const int SPELLABILITY_NS_GNOME = 1535; +const int SPELLABILITY_NS_HALFLING = 1536; +const int SPELLABILITY_NS_OFF = 1537; + +const int FEAT_EPIC_NINJA = 3426; +const int FEAT_1000FACES_MEDIUM = 3400; +const int FEAT_1000FACES_SMALL = 3401; +const int FEAT_1000FACES_OFF = 3402; + +//Mystic Theurge +const int FEAT_EPIC_THEURGE = 3427; + +//War Priest +const int SPELLABILITY_WP_RALLY = 1549; +const int SPELLABILITY_WP_INFLAME = 1550; +const int SPELLABILITY_WP_IMPLACABLE_FOE = 1551; + +const int FEAT_EPIC_WARPRIEST = 3428; +const int FEAT_RALLY = 3418; +const int FEAT_INFLAME = 3419; +const int FEAT_IMPLACABLE_FOE = 3420; +const int FEAT_HEALING_CIRCLE = 3421; +const int FEAT_FEAR_AURA = 3422; +const int FEAT_MASS_HASTE = 3423; +const int FEAT_MASS_HEAL = 3424; + +//Ocular Adept +const int SPELLABILITY_OA_CHPERRAY = 1538; +const int SPELLABILITY_OA_SLEEPRAY = 1539; +const int SPELLABILITY_OA_INFRAY = 1540; +const int SPELLABILITY_OA_SLOWRAY = 1541; +const int SPELLABILITY_OA_FEARRAY = 1542; +const int SPELLABILITY_OA_CHMONRAY = 1543; +const int SPELLABILITY_OA_TELERAY = 1544; +const int SPELLABILITY_OA_PETRAY = 1545; +const int SPELLABILITY_OA_DISRAY = 1546; +const int SPELLABILITY_OA_DEATHRAY = 1547; + +const int FEAT_CHPERRAY = 3403; +const int FEAT_SLEEPRAY = 3404; +const int FEAT_INFRAY = 3405; +const int FEAT_SLOWRAY = 3406; +const int FEAT_FEARRAY = 3407; +const int FEAT_CHMONRAY = 3408; +const int FEAT_TELERAY = 3409; +const int FEAT_PETRAY = 3410; +const int FEAT_DISRAY = 3411; +const int FEAT_DEATHRAY = 3412; + +//Samurai +const int SPELLABILITY_SM_ANCESTDAISHO = 1548; + +const int FEAT_EPIC_SAMURAI = 3416; +const int FEAT_ANCESTRAL_DAISHO = 3417; +const int FEAT_EPIC_ANCESTRAL_DAISHO_1 = 1950; +const int FEAT_EPIC_ANCESTRAL_DAISHO_2 = 1951; +const int FEAT_EPIC_ANCESTRAL_DAISHO_3 = 1952; +const int FEAT_EPIC_ANCESTRAL_DAISHO_4 = 1953; +const int FEAT_EPIC_ANCESTRAL_DAISHO_5 = 1954; +const int FEAT_EPIC_ANCESTRAL_DAISHO_6 = 1955; +const int FEAT_EPIC_ANCESTRAL_DAISHO_7 = 1956; +const int FEAT_EPIC_ANCESTRAL_DAISHO_8 = 1957; +const int FEAT_EPIC_ANCESTRAL_DAISHO_9 = 1958; +const int FEAT_EPIC_ANCESTRAL_DAISHO_10 = 1959; + +//Battlerager +const int FEAT_FEROCIOUS_PROW = 3512; +const int FEAT_PRC_EPIC_MIGHT_RAGE = 3516; + +/*////////////////////////////////////////////////////// +//////////////////////END CODI////////////////////////// +//////////////////////////////////////////////////////*/ + + +/*////////////////////////////////////////////////// +//////////////// PSIONICS ////////////////////////// +//////////////////////////////////////////////////*/ + +//Psion +const int FEAT_PSION_DIS_EGOIST = 3554; +const int FEAT_PSION_DIS_KINETICIST = 3555; +const int FEAT_PSION_DIS_NOMAD = 3556; +const int FEAT_PSION_DIS_SEER = 3557; +const int FEAT_PSION_DIS_SHAPER = 3558; +const int FEAT_PSION_DIS_TELEPATH = 3559; + +//Psychic Rogue +const int FEAT_PSY_SNEAK_ATTACK_1d6 = 24275; +const int FEAT_PSY_SNEAK_ATTACK_2d6 = 24276; +const int FEAT_PSY_SNEAK_ATTACK_3d6 = 24277; +const int FEAT_PSY_MIND_CRIPPLE = 24282; + +//Wilder +const int FEAT_WILDER_SURGING_EUPHORIA = 3568; +const int FEAT_WILDER_PSYCHIC_ENERVATION = 3569; +const int FEAT_WILDER_VOLATILE_MIND = 3570; +const int FEAT_WILDER_ELUDE_TOUCH = 3567; +const int FEAT_WILDER_WILD_SURGE_1_5 = 3560; +const int FEAT_WILDER_WILD_SURGE_6_10 = 3561; +const int FEAT_WILDER_WILD_SURGE_11 = 3562; + +// Soulknife +const int FEAT_MINDBLADE = 3600; +const int FEAT_MANIFEST_MINDBLADE = 3601; +const int FEAT_WEAPON_FOCUS_MINDBLADE = 3602; +const int FEAT_GREATER_WEAPON_FOCUS_MINDBLADE = 3603; +const int FEAT_THROW_MINDBLADE = 3604; +const int FEAT_PSYCHIC_STRIKE = 3605; +const int FEAT_FREE_DRAW = 3606; +const int FEAT_SHAPE_MINDBLADE = 3607; +const int FEAT_MINDBLADE_ENHANCEMENT = 3608; +const int FEAT_BLADEWIND = 3609; +const int FEAT_KNIFE_TO_THE_SOUL = 3610; +const int FEAT_KNIFE_TO_THE_SOUL_RADIAL1 = 3611; +const int FEAT_KNIFE_TO_THE_SOUL_RADIAL2 = 3612; +const int FEAT_EPIC_SOULKNIFE = 3613; +const int FEAT_IMPROVED_CRITICAL_MINDBLADE = 3614; +const int FEAT_OVERWHELMING_CRITICAL_MINDBLADE = 3615; +const int FEAT_DEVASTATING_CRITICAL_MINDBLADE = 3616; +const int FEAT_WEAPON_SPECIALIZATION_MINDBLADE = 3617; +const int FEAT_EPIC_WEAPON_FOCUS_MINDBLADE = 3618; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_MINDBLADE = 3619; +const int FEAT_MULTIPLE_THROW = 3620; +const int FEAT_CHANGE_HANDEDNESS = 3621; +const int FEAT_WEAPON_OF_CHOICE_MINDBLADE = 3622; +const int FEAT_MIND_CLEAVE = 24414; + + +//:: Psionic Feats +const int FEAT_AUGMENT_PSIONICS_QUICKSELECTS = 3550; +const int FEAT_AUGMENT_QUICKSELECTS_2 = 3563; +const int FEAT_AUGMENT_PSIONICS_DIGITS_0_4 = 3551; +const int FEAT_AUGMENT_PSIONICS_DIGITS_5_9 = 3552; +const int FEAT_AUGMENT_PSIONICS_TENS = 3553; + +const int FEAT_MENTAL_RESISTANCE = 4812; +const int FEAT_HOSTILE_MIND = 4813; +const int FEAT_FORCE_OF_WILL = 4814; +const int FEAT_CLOSED_MIND = 4815; +const int FEAT_RECKLESS_OFFENSE = 4816; +const int FEAT_CLOAK_DANCE = 4817; +const int FEAT_PSIONIC_FOCUS = 4818; +const int FEAT_PSIONIC_HOLE = 4811; +const int FEAT_COMBAT_MANIFESTATION = 4810; +const int FEAT_MENTAL_LEAP = 4809; +const int FEAT_NARROW_MIND = 4808; +const int FEAT_POWER_PENETRATION = 4807; +const int FEAT_GREATER_POWER_PENETRATION = 4806; +const int FEAT_POWER_SPECIALIZATION = 4805; +const int FEAT_GREATER_POWER_SPECIALIZATION = 4804; +const int FEAT_PSIONIC_DODGE = 4803; +const int FEAT_PSIONIC_ENDOWMENT = 4802; +const int FEAT_GREATER_PSIONIC_ENDOWMENT = 4801; +const int FEAT_PSIONIC_FIST = 4800; +const int FEAT_GREATER_PSIONIC_FIST = 4821; +const int FEAT_PSIONIC_WEAPON = 4822; +const int FEAT_GREATER_PSIONIC_WEAPON = 4823; +const int FEAT_PSIONIC_SHOT = 4824; +const int FEAT_GREATER_PSIONIC_SHOT = 4825; +const int FEAT_OVERCHANNEL = 4826; +const int FEAT_PSIONIC_MEDITATION = 4827; +const int FEAT_RAPID_METABOLISM = 4828; +const int FEAT_TALENTED = 4829; +const int FEAT_UNAVOIDABLE_STRIKE = 4830; +const int FEAT_WILD_TALENT = 4831; +const int FEAT_WOUNDING_ATTACK = 4832; +const int FEAT_BOOST_CONSTRUCT = 4833; +const int FEAT_SPEED_OF_THOUGHT = 4834; +const int FEAT_PSIONIC_TALENT_1 = 4835; +const int FEAT_PSIONIC_TALENT_2 = 4836; +const int FEAT_PSIONIC_TALENT_3 = 4837; +const int FEAT_PSIONIC_TALENT_4 = 4838; +const int FEAT_PSIONIC_TALENT_5 = 4839; +const int FEAT_PSIONIC_TALENT_6 = 4840; +const int FEAT_PSIONIC_TALENT_7 = 4841; +const int FEAT_PSIONIC_TALENT_8 = 4842; +const int FEAT_PSIONIC_TALENT_9 = 4843; +const int FEAT_PSIONIC_TALENT_10 = 4844; +const int FEAT_METAMORPHIC_TRANSFER_1 = 4845; +const int FEAT_METAMORPHIC_TRANSFER_2 = 4846; +const int FEAT_METAMORPHIC_TRANSFER_3 = 4847; +const int FEAT_METAMORPHIC_TRANSFER_4 = 4848; +const int FEAT_METAMORPHIC_TRANSFER_5 = 4849; +const int FEAT_METAMORPHIC_TRANSFER_6 = 4850; +const int FEAT_METAMORPHIC_TRANSFER_7 = 4851; +const int FEAT_DEEP_IMPACT = 4852; +const int FEAT_FELL_SHOT = 4853; +const int FEAT_EXPANDED_KNOWLEDGE_1 = 4867; +const int FEAT_EXPANDED_KNOWLEDGE_2 = 4868; +const int FEAT_EXPANDED_KNOWLEDGE_3 = 4869; +const int FEAT_EXPANDED_KNOWLEDGE_4 = 4870; +const int FEAT_EXPANDED_KNOWLEDGE_5 = 4871; +const int FEAT_EXPANDED_KNOWLEDGE_6 = 4872; +const int FEAT_EXPANDED_KNOWLEDGE_7 = 4873; +const int FEAT_EXPANDED_KNOWLEDGE_8 = 4874; +const int FEAT_EXPANDED_KNOWLEDGE_9 = 4875; +const int FEAT_EXPANDED_KNOWLEDGE_10 = 4876; +const int FEAT_INVEST_ARMOUR = 3643; + +// Epic psionic feats +const int FEAT_EPIC_PSIONIC_FOCUS_1 = 4857; +const int FEAT_EPIC_PSIONIC_FOCUS_2 = 4858; +const int FEAT_EPIC_PSIONIC_FOCUS_3 = 4859; +const int FEAT_EPIC_PSIONIC_FOCUS_4 = 4860; +const int FEAT_EPIC_PSIONIC_FOCUS_5 = 4861; +const int FEAT_EPIC_PSIONIC_FOCUS_6 = 4862; +const int FEAT_EPIC_PSIONIC_FOCUS_7 = 4863; +const int FEAT_EPIC_PSIONIC_FOCUS_8 = 4864; +const int FEAT_EPIC_PSIONIC_FOCUS_9 = 4865; +const int FEAT_EPIC_PSIONIC_FOCUS_10 = 4866; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_1 = 4877; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_2 = 4878; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_3 = 4879; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_4 = 4880; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_5 = 4881; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_6 = 4882; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_7 = 4883; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_8 = 4884; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_9 = 4885; +const int FEAT_EPIC_EXPANDED_KNOWLEDGE_10 = 4886; +const int FEAT_IMPROVED_METAPSIONICS_1 = 4887; +const int FEAT_IMPROVED_METAPSIONICS_2 = 4888; +const int FEAT_IMPROVED_METAPSIONICS_3 = 4889; +const int FEAT_IMPROVED_METAPSIONICS_4 = 4890; +const int FEAT_IMPROVED_METAPSIONICS_5 = 4891; +const int FEAT_IMPROVED_METAPSIONICS_6 = 4892; +const int FEAT_IMPROVED_METAPSIONICS_7 = 4893; +const int FEAT_IMPROVED_METAPSIONICS_8 = 4894; +const int FEAT_IMPROVED_METAPSIONICS_9 = 4895; +const int FEAT_IMPROVED_METAPSIONICS_10 = 4896; +const int FEAT_IMPROVED_MANIFESTATION_1 = 4897; +const int FEAT_IMPROVED_MANIFESTATION_2 = 4898; +const int FEAT_IMPROVED_MANIFESTATION_3 = 4899; +const int FEAT_IMPROVED_MANIFESTATION_4 = 4900; +const int FEAT_IMPROVED_MANIFESTATION_5 = 4901; +const int FEAT_IMPROVED_MANIFESTATION_6 = 4902; +const int FEAT_IMPROVED_MANIFESTATION_7 = 4903; +const int FEAT_IMPROVED_MANIFESTATION_8 = 4904; +const int FEAT_IMPROVED_MANIFESTATION_9 = 4905; +const int FEAT_IMPROVED_MANIFESTATION_10 = 4906; +const int FEAT_POWER_KNOWLEDGE_PSION_1 = 4907; +const int FEAT_POWER_KNOWLEDGE_PSION_2 = 4908; +const int FEAT_POWER_KNOWLEDGE_PSION_3 = 4909; +const int FEAT_POWER_KNOWLEDGE_PSION_4 = 4910; +const int FEAT_POWER_KNOWLEDGE_PSION_5 = 4911; +const int FEAT_POWER_KNOWLEDGE_PSION_6 = 4912; +const int FEAT_POWER_KNOWLEDGE_PSION_7 = 4913; +const int FEAT_POWER_KNOWLEDGE_PSION_8 = 4914; +const int FEAT_POWER_KNOWLEDGE_PSION_9 = 4915; +const int FEAT_POWER_KNOWLEDGE_PSION_10 = 4916; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_1 = 4917; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_2 = 4918; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_3 = 4919; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_4 = 4920; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_5 = 4921; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_6 = 4922; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_7 = 4923; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_8 = 4924; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_9 = 4925; +const int FEAT_POWER_KNOWLEDGE_PSYWAR_10 = 4926; +const int FEAT_POWER_KNOWLEDGE_WILDER_1 = 4927; +const int FEAT_POWER_KNOWLEDGE_WILDER_2 = 4928; +const int FEAT_POWER_KNOWLEDGE_WILDER_3 = 4929; +const int FEAT_POWER_KNOWLEDGE_WILDER_4 = 4930; +const int FEAT_POWER_KNOWLEDGE_WILDER_5 = 4931; +const int FEAT_POWER_KNOWLEDGE_WILDER_6 = 4932; +const int FEAT_POWER_KNOWLEDGE_WILDER_7 = 4933; +const int FEAT_POWER_KNOWLEDGE_WILDER_8 = 4934; +const int FEAT_POWER_KNOWLEDGE_WILDER_9 = 4935; +const int FEAT_POWER_KNOWLEDGE_WILDER_10 = 4936; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_1 = 4945; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_2 = 4946; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_3 = 4947; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_4 = 4948; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_5 = 4949; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_6 = 4950; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_7 = 4951; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_8 = 4952; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_9 = 4953; +const int FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_10 = 4954; +const int FEAT_POWER_KNOWLEDGE_WARMIND_1 = 4955; +const int FEAT_POWER_KNOWLEDGE_WARMIND_2 = 4956; +const int FEAT_POWER_KNOWLEDGE_WARMIND_3 = 4957; +const int FEAT_POWER_KNOWLEDGE_WARMIND_4 = 4958; +const int FEAT_POWER_KNOWLEDGE_WARMIND_5 = 4959; +const int FEAT_POWER_KNOWLEDGE_WARMIND_6 = 4960; +const int FEAT_POWER_KNOWLEDGE_WARMIND_7 = 4961; +const int FEAT_POWER_KNOWLEDGE_WARMIND_8 = 4962; +const int FEAT_POWER_KNOWLEDGE_WARMIND_9 = 4963; +const int FEAT_POWER_KNOWLEDGE_WARMIND_10 = 4964; +const int FEAT_POWER_KNOWLEDGE_PSYROG_1 = 24265; +const int FEAT_POWER_KNOWLEDGE_PSYROG_2 = 24266; +const int FEAT_POWER_KNOWLEDGE_PSYROG_3 = 24267; +const int FEAT_POWER_KNOWLEDGE_PSYROG_4 = 24268; +const int FEAT_POWER_KNOWLEDGE_PSYROG_5 = 24269; +const int FEAT_POWER_KNOWLEDGE_PSYROG_6 = 24270; +const int FEAT_POWER_KNOWLEDGE_PSYROG_7 = 24271; +const int FEAT_POWER_KNOWLEDGE_PSYROG_8 = 24272; +const int FEAT_POWER_KNOWLEDGE_PSYROG_9 = 24273; +const int FEAT_POWER_KNOWLEDGE_PSYROG_10 = 24274; + +// Metapsionic feats +const int FEAT_CHAIN_POWER = 4937; +const int FEAT_EMPOWER_POWER = 4938; +const int FEAT_EXTEND_POWER = 4939; +const int FEAT_MAXIMIZE_POWER = 4940; +const int FEAT_SPLIT_PSIONIC_RAY = 4941; +const int FEAT_TWIN_POWER = 4942; +const int FEAT_WIDEN_POWER = 4943; +const int FEAT_QUICKEN_POWER = 4944; + +// Sanctified Mind +const int FEAT_SANCMIND_PARTITION_MIND = 2231; + +// Fist of Dal Quor +const int FEAT_FIST_DAL_QUOR_STUNNING_STRIKE = 2475; + +// Pyrokineticist +const int FEAT_PYRO_PYROKINETICIST = 2846; +const int FEAT_PYRO_CRYOKINETICIST = 2847; +const int FEAT_PYRO_SONOKINETICIST = 2848; +const int FEAT_PYRO_ELECTROKINETICIST = 2849; +const int FEAT_PYRO_ACETOKINETICIST = 2850; +const int FEAT_PYRO_FIRE_LASH = 2851; +const int FEAT_PYRO_FIRE_ADAPTATION = 2852; +const int FEAT_PYRO_HAND_AFIRE = 2853; +const int FEAT_PYRO_BOLT_OF_FIRE = 2854; +const int FEAT_PYRO_WEAPON_AFIRE = 2855; +const int FEAT_PYRO_NIMBUS = 2856; +const int FEAT_PYRO_NIMBUS_TOUCH_ATTACK = 2857; +const int FEAT_PYRO_FIREWALK = 2858; +const int FEAT_PYRO_FEAR_NO_FIRE = 2859; +const int FEAT_PYRO_GREATER_WEAPON_AFIRE = 2860; +const int FEAT_PYRO_HEAT_DEATH = 2861; +const int FEAT_PYRO_CONFLAGRATION = 2862; + +/*////////////////////////////////////////////////// +//////////////// END PSIONICS ////////////////////// +//////////////////////////////////////////////////*/ + +// Swashbuckler +const int FEAT_SWASH_GRACE1 = 1900; +const int FEAT_SWASH_GRACE2 = 1901; +const int FEAT_SWASH_GRACE3 = 1902; +const int FEAT_SWASH_GRACE4 = 1903; +const int FEAT_SWASH_GRACE5 = 1904; +const int INSIGHTFUL_STRIKE = 1905; +const int SWASH_DODGE_1 = 1906; +const int SWASH_DODGE_2 = 1907; +const int SWASH_DODGE_3 = 1908; +const int SWASH_DODGE_4 = 1909; +const int SWASH_DODGE_5 = 1910; +const int SWASH_DODGE_6 = 1911; +const int SWASH_DODGE_7 = 1912; +const int SWASH_DODGE_8 = 1913; +const int SWASH_LUCKY = 1914; +const int WEAKENING_CRITICAL = 1915; +const int WOUNDING_CRITICAL = 1916; + +//Marshal +const int MIN_AUR_FORT = 1919; +const int MIN_AUR_WILL = 1920; +const int MIN_AUR_REF = 1921; +const int MIN_AUR_CHA = 1922; +const int MIN_AUR_CON = 1923; +const int MIN_AUR_DEX = 1924; +const int MIN_AUR_INT = 1925; +const int MIN_AUR_STR = 1926; +const int MIN_AUR_WIS = 1927; +const int MIN_AUR_CAST = 1928; +const int MIN_AUR_AOW = 1929; +const int MAJ_AUR_MOT_ARDOR = 1930; +const int MAJ_AUR_MOT_CARE = 1931; +const int MAJ_AUR_RES_TROOPS = 1932; +const int MAJ_AUR_MOT_URGE = 1933; +const int MAJ_AUR_HARD_SOLDIER = 1934; +const int MAJ_AUR_MOT_ATTACK = 1935; +const int MAJ_AUR_STEAD_HAND = 1936; +const int GRANT_MOVE_ACTION = 1937; +const int MINOR_AURA = 1938; +const int MAJOR_AURA = 1939; + +//ACP feat +const int FEAT_ACP_QUICK_FEAT = 3479; +const int FEAT_ACP_HEAVY_FEAT = 3480; +const int FEAT_ACP_UNARMED_FEAT = 3481; + +//Baelnorn +const int FEAT_END_PROJECTION = 3575; +const int FEAT_BAELN_PROP = 3576; +const int FEAT_BAELN_TOUCH = 3577; +const int FEAT_BAELN_ABIL = 3578; +const int FEAT_PROJECTION = 3579; +const int FEAT_BAELN_DEF = 3580; +const int FEAT_UNDEAD_HD = 3581; +const int FEAT_TURN_RESISTANCE = 3582; +const int FEAT_IMPROVED_TURN_RESISTANCE = 3583; +const int FEAT_IMMUNITY_ABILITY_DECREASE = 3584; +const int FEAT_IMMUNITY_CRITICAL = 3585; +const int FEAT_IMMUNITY_DEATH = 3586; +const int FEAT_IMMUNITY_DISEASE = 3587; +const int FEAT_IMMUNITY_MIND_SPELLS = 3588; +const int FEAT_IMMUNITY_PARALYSIS = 3589; +const int FEAT_IMMUNITY_POISON = 3590; +const int FEAT_IMMUNITY_SNEAKATTACK = 3591; +const int FEAT_POSITIVE_ENERGY_RESISTANCE = 3592; +const int FEAT_TURNING_IMMUNITY = 3593; + +const int FEAT_TURN_SUBMISSION = 23517; +const int FEAT_IMMUNITY_TO_REBUKING = 23518; + +//Mother Cyst +const int FEAT_MOTHER_CYST = 3594; + +//Tenjac's spell related +const int FEAT_DETECT_GOOD_AT_WILL = 3595; +const int FEAT_RAY_CHARM_PERSON = 2220; +const int FEAT_RAY_CHARM_MONSTER = 2221; +const int FEAT_RAY_SLEEP = 2222; +const int FEAT_RAY_FLESH_TO_STONE = 2223; +const int FEAT_RAY_DISINTEGRATE = 2224; +const int FEAT_RAY_FEAR = 2225; +const int FEAT_RAY_SLOW = 2226; +const int FEAT_RAY_INFLICT_MODERATE_WOUNDS = 2227; +const int FEAT_RAY_FINGER_OF_DEATH = 2228; + +//Lasher +const int FEAT_LASHER_SNEAK1D6 = 4075; +const int FEAT_LASHER_SNEAK2D6 = 4076; +const int FEAT_LASHER_SNEAK3D6 = 4077; +const int FEAT_LASHER_IMPROVED_KNOCKDOWN_WHIP = 4078; +const int FEAT_LASHER_THIRD_HAND = 4079; +const int FEAT_LASHER_CRACK_FATE = 4080; +const int FEAT_LASHER_LASHING_WHIP = 4081; +const int FEAT_LASHER_IMPROVED_DISARM_WHIP = 4082; +const int FEAT_LASHER_STUNNING_SNAP = 4083; +const int FEAT_LASHER_CRACK_DOOM = 4084; +const int FEAT_LASHER_DEATH_SPIRAL = 4085; + +// PnP Spell Schools +const int FEAT_PNP_SPELL_SCHOOL_GENERAL = 2273; +const int FEAT_PNP_SPELL_SCHOOL_ABJURATION = 2274; +const int FEAT_PNP_SPELL_SCHOOL_CONJURATION = 2275; +const int FEAT_PNP_SPELL_SCHOOL_DIVINATION = 2276; +const int FEAT_PNP_SPELL_SCHOOL_ENCHANTMENT = 2277; +const int FEAT_PNP_SPELL_SCHOOL_EVOCATION = 2278; +const int FEAT_PNP_SPELL_SCHOOL_ILLUSION = 2279; +const int FEAT_PNP_SPELL_SCHOOL_NECROMANCY = 2280; +const int FEAT_PNP_SPELL_SCHOOL_TRANSMUTATION = 2281; + +//Spellfire +const int FEAT_SPELLFIRE_WIELDER = 4368; +const int FEAT_SPELLFIRE_INCREASE = 4369; +const int FEAT_SPELLFIRE_DECREASE = 4370; +const int FEAT_SPELLFIRE_QUICKSELECT = 4371; +const int FEAT_WEAPON_FOCUS_SPELLFIRE = 4372; +const int FEAT_EPIC_WEAPON_FOCUS_SPELLFIRE = 4373; +const int FEAT_SPELLFIRE_DRAIN_CHARGED = 4374; +const int FEAT_SPELLFIRE_INCREASED_STORAGE = 4375; +const int FEAT_SPELLFIRE_IMPROVED_HEALING = 4376; +const int FEAT_SPELLFIRE_RAPID_BLAST = 4377; +const int FEAT_SPELLFIRE_CHARGE_ITEM = 4378; +const int FEAT_SPELLFIRE_CROWN = 4379; +const int FEAT_SPELLFIRE_MAELSTROM = 4380; + +// Favoured Soul +const int FEAT_FAVOURED_SOUL_ACID = 2237; +const int FEAT_FAVOURED_SOUL_COLD = 2238; +const int FEAT_FAVOURED_SOUL_ELEC = 2239; +const int FEAT_FAVOURED_SOUL_FIRE = 2240; +const int FEAT_FAVOURED_SOUL_SONIC = 2241; +const int FEAT_FAVOURED_SOUL_WINGS = 2242; +const int FEAT_FAVOURED_SOUL_DAMRED = 2243; + +//Healer +const int FEAT_CELESTIAL_COMPANION = 3795; +const int FEAT_EFFORTLESS_HEALING = 3642; + +// War Wizard of Cormyr +const int FEAT_WWOC_WIDEN_SPELL = 2473; +const int FEAT_WWOC_ENLARGE_SPELL_AREA = 2474; + +// Complete Warrior Samurai +const int FEAT_CWSM_STAREDOWN = 2356; +const int FEAT_CWSM_MASS_STAREDOWN = 2357; +const int FEAT_CWSM_IMPROVED_STAREDOWN = 2358; +const int FEAT_CWSM_FRIGHTFUL_PRESENCE = 2359; + + // Virtuoso Feats + const int FEAT_VIRTUOSO_SUSTAINING_SONG = 4168; + const int FEAT_VIRTUOSO_CALUMNY = 4169; + const int FEAT_VIRTUOSO_JARRING_SONG = 4170; + const int FEAT_VIRTUOSO_SHARP_NOTE = 4171; + const int FEAT_VIRTUOSO_MINDBENDING_MELODY = 4172; + const int FEAT_VIRTUOSO_GREATER_CALUMNY = 4173; + const int FEAT_VIRTUOSO_MAGICAL_MELODY = 4174; + const int FEAT_VIRTUOSO_SONG_OF_FURY = 4175; + const int FEAT_VIRTUOSO_REVEALING_MELODY = 4176; + const int FEAT_VIRTUOSO_PERFORMANCE = 4177; + +// hexblade feats +const int FEAT_HEXCURSE = 3664; +const int FEAT_SWIFT_CAST = 3827; + +// Status markers +const int FEAT_INCORPOREAL = 4166; +const int FEAT_ETHEREAL = 4167; + +// Template Feats +const int FEAT_TEMPLATE_ARCHLICH_MARKER = 22700; +const int FEAT_TEMPLATE_ARCHLICH_TURN_UNDEAD = 22701; + +const int FEAT_TEMPLATE_CELESTIAL_SMITE_EVIL = 22601; +const int FEAT_TEMPLATE_CELESTIAL_MARKER = 22602; +const int FEAT_TEMPLATE_FIENDISH_SMITE_GOOD = 22603; +const int FEAT_TEMPLATE_FIENDISH_MARKER = 22604; +const int FEAT_TEMPLATE_HALF_CELESTIAL_SMITE_EVIL = 22605; +const int FEAT_TEMPLATE_HALF_CELESTIAL_PROTECTION = 22606; +const int FEAT_TEMPLATE_HALF_CELESTIAL_BLESS = 22607; +const int FEAT_TEMPLATE_HALF_CELESTIAL_AID = 22608; +const int FEAT_TEMPLATE_HALF_CELESTIAL_DETECT = 22609; +const int FEAT_TEMPLATE_HALF_CELESTIAL_CURE_SERIOUS = 22610; +const int FEAT_TEMPLATE_HALF_CELESTIAL_NEUTRALIZE_POISON = 22611; +const int FEAT_TEMPLATE_HALF_CELESTIAL_HOLY_SMITE = 22612; +const int FEAT_TEMPLATE_HALF_CELESTIAL_REMOVE_DISEASE = 22613; +const int FEAT_TEMPLATE_HALF_CELESTIAL_DISPEL_EVIL = 22614; +const int FEAT_TEMPLATE_HALF_CELESTIAL_HOLY_WORD = 22615; +const int FEAT_TEMPLATE_HALF_CELESTIAL_HOLY_AURA = 22616; +const int FEAT_TEMPLATE_HALF_CELESTIAL_HALLOW = 22617; +const int FEAT_TEMPLATE_HALF_CELESTIAL_MASS_CHARM = 22618; +const int FEAT_TEMPLATE_HALF_CELESTIAL_SUMMON_IX = 22619; +const int FEAT_TEMPLATE_HALF_CELESTIAL_RESURRECTION = 22620; +const int FEAT_TEMPLATE_HALF_CELESTIAL_DAYLIGHT = 22621; +const int FEAT_TEMPLATE_HALF_CELESTIAL_MARKER = 22622; +const int FEAT_TEMPLATE_NECROPOLITAN_MARKER = 22623; +const int FEAT_TEMPLATE_HALF_FIENDISH_SMITE_GOOD = 22624; +const int FEAT_TEMPLATE_HALF_FIENDISH_DARKNESS = 22625; +const int FEAT_TEMPLATE_HALF_FIENDISH_DESECRATE = 22626; +const int FEAT_TEMPLATE_HALF_FIENDISH_UNHOLY_BLIGHT = 22627; +const int FEAT_TEMPLATE_HALF_FIENDISH_POISON = 22628; +const int FEAT_TEMPLATE_HALF_FIENDISH_CONTAGION = 22629; +const int FEAT_TEMPLATE_HALF_FIENDISH_BLASPHEMY = 22630; +const int FEAT_TEMPLATE_HALF_FIENDISH_UNHOLY_AURA = 22631; +const int FEAT_TEMPLATE_HALF_FIENDISH_UNHALLOW = 22632; +const int FEAT_TEMPLATE_HALF_FIENDISH_HORRID_WILTING = 22633; +const int FEAT_TEMPLATE_HALF_FIENDISH_SUMMON_IX = 22634; +const int FEAT_TEMPLATE_HALF_FIENDISH_DESTRUCTION = 22635; +const int FEAT_TEMPLATE_HALF_FIENDISH_MARKER = 22636; +const int FEAT_TEMPLATE_LICH_FEAR_AURA = 22637; +const int FEAT_TEMPLATE_LICH_PARALYZING_TOUCH = 22638; +const int FEAT_TEMPLATE_LICH_APPEARANCE = 22639; +const int FEAT_TEMPLATE_LICH_MARKER = 22640; +const int FEAT_TEMPLATE_DEMILICH_ALTER_SELF = 22641; +const int FEAT_TEMPLATE_DEMILICH_ASTRAL_PROJECTION = 22642; +const int FEAT_TEMPLATE_DEMILICH_CREATE_GREATER_UNDEAD = 22643; +const int FEAT_TEMPLATE_DEMILICH_CREATE_UNDEAD = 22644; +const int FEAT_TEMPLATE_DEMILICH_DEATH_KNELL = 22645; +const int FEAT_TEMPLATE_DEMILICH_ENERVATION = 22646; +const int FEAT_TEMPLATE_DEMILICH_GREATER_DISPEL_MAGIC = 22647; +const int FEAT_TEMPLATE_DEMILICH_HARM = 22648; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_I = 22649; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_II = 22650; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_III = 22651; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_IV = 22652; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_V = 22653; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_VI = 22654; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_VII = 22655; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_VIII = 22656; +const int FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_IX = 22657; +const int FEAT_TEMPLATE_DEMILICH_TELEKINESIS = 22658; +const int FEAT_TEMPLATE_DEMILICH_WEIRD = 22659; +const int FEAT_TEMPLATE_DEMILICH_GREATER_PLANAR_ALLY = 22660; +const int FEAT_TEMPLATE_DEMILICH_MARKER = 22661; +const int FEAT_TEMPLATE_HALF_DRAGON_BREATH = 22662; +const int FEAT_HD_CHROMATICBLACK_MARKER = 22663; +const int FEAT_HD_CHROMATICBLUE_MARKER = 22664; +const int FEAT_HD_CHROMATICGREEN_MARKER = 22665; +const int FEAT_HD_CHROMATICRED_MARKER = 22666; +const int FEAT_HD_CHROMATICWHITE_MARKER = 22667; +const int FEAT_HD_GEMAMETHYST_MARKER = 22668; +const int FEAT_HD_GEMCRYSTAL_MARKER = 22669; +const int FEAT_HD_GEMEMERALD_MARKER = 22670; +const int FEAT_HD_GEMSAPPHIRE_MARKER = 22671; +const int FEAT_HD_GEMTOPAZ_MARKER = 22672; +const int FEAT_HD_LUNGCHIANGLUNG_MARKER = 22673; +const int FEAT_HD_LUNGLILUNG_MARKER = 22674; +const int FEAT_HD_LUNGLUNGWANG_MARKER = 22675; +const int FEAT_HD_LUNGPANLUNG_MARKER = 22676; +const int FEAT_HD_LUNGSHENLUNG_MARKER = 22677; +const int FEAT_HD_LUNGTIENLUNG_MARKER = 22678; +const int FEAT_HD_LUNGTUNMILUNG_MARKER = 22679; +const int FEAT_HD_METALLICBRASS_MARKER = 22680; +const int FEAT_HD_METALLICBRONZE_MARKER = 22681; +const int FEAT_HD_METALLICCOPPER_MARKER = 22682; +const int FEAT_HD_METALLICGOLD_MARKER = 22683; +const int FEAT_HD_METALLICSILVER_MARKER = 22684; +const int FEAT_HD_OBSCUREBATTLE_MARKER = 22685; +const int FEAT_HD_OBSCUREBROWN_MARKER = 22686; +const int FEAT_HD_OBSCURECHAOS_MARKER = 22687; +const int FEAT_HD_OBSCUREDEEP_MARKER = 22688; +const int FEAT_HD_OBSCUREETHEREAL_MARKER = 22689; +const int FEAT_HD_OBSCUREFANG_MARKER = 22690; +const int FEAT_HD_OBSCUREHOWLING_MARKER = 22691; +const int FEAT_HD_OBSCUREOCEANUS_MARKER = 22692; +const int FEAT_HD_OBSCUREPYROCLASTIC_MARKER = 22693; +const int FEAT_HD_OBSCURERADIANT_MARKER = 22694; +const int FEAT_HD_OBSCURERUST_MARKER = 22695; +const int FEAT_HD_OBSCURESHADOW_MARKER = 22696; +const int FEAT_HD_OBSCURESONG_MARKER = 22697; +const int FEAT_HD_OBSCURESTYX_MARKER = 22698; +const int FEAT_HD_OBSCURETARTERIAN_MARKER = 22699; + +const int FEAT_TEMPLATE_SAINT_SLA_BLESS = 22702; +//const int FEAT_TEMPLATE_SAINT_SLA_GUIDANCE = 22703; +const int FEAT_TEMPLATE_SAINT_SLA_RESISTANCE = 22704; +const int FEAT_TEMPLATE_SAINT_SLA_VIRTUE = 22705; +const int FEAT_TEMPLATE_SAINT_PROTECTIVE_AURA = 22706; +const int FEAT_TEMPLATE_SAINT_HOLY_POWER = 22707; + + // PRC Extra Stunning Feat + const int FEAT_PRC_EXTRA_STUNNING = 4387; + + // Enlightened Fist Feats + const int FEAT_EF_FIST_OF_ENERGY = 4388; + const int FEAT_EF_SPELL_SELECT = 4389; + const int FEAT_EF_ARCANE_FIST = 4390; + const int FEAT_EF_ARCANE_REJUVENATION = 4391; + const int FEAT_EF_HOLD_RAY = 4392; + const int FEAT_EF_DIAMOND_SOUL = 4393; + const int FEAT_EF_UNARMEDDMG = 4394; + const int FEAT_EF_SPEED = 4395; + const int FEAT_EF_EXTRA_STUNNING = 4396; + const int FEAT_EF_FIST_OF_ENERGY_BURST = 4397; + +// Knight Feats +const int FEAT_FIGHT_CHALLENGE = 2834; + +// Champion of Corellon Feats +const int FEAT_COC_ELEGANT_STRIKE = 3812; +const int FEAT_COC_WRATH = 3815; + +// Dragon Shaman +const int FEAT_DRAGONSHAMAN_RESOLVE = 3965; +const int FEAT_DRAGONSHAMAN_TOUCHVITALITY = 3961; +const int FEAT_DRAGONSHAMAN_TOUCHVITALITY_MAJOR = 3962; +const int FEAT_DRAGONSHAMAN_BREATH = 3967; +const int FEAT_DRAGONSHAMAN_AURA_POWER = 3953; +const int FEAT_DRAGONSHAMAN_AURA_SENSES = 3956; +const int FEAT_DRAGONSHAMAN_AURA_PRESENCE = 3959; +const int FEAT_DRAGONSHAMAN_AURA_RESISTANCE = 3958; +const int FEAT_DRAGONSHAMAN_AURA_ENERGYSHLD = 3954; +const int FEAT_DRAGONSHAMAN_AURA_VIGOR = 3957; +const int FEAT_DRAGONSHAMAN_AURA_TOUGHNESS = 3955; +const int FEAT_DRAGONSHAMAN_AURA_INSIGHT = 1895; +const int FEAT_DRAGONSHAMAN_AURA_RESOLVE = 1896; +const int FEAT_DRAGONSHAMAN_AURA_STAMINA = 1897; +const int FEAT_DRAGONSHAMAN_AURA_SWIFTNESS = 1898; +const int FEAT_DRAGONSHAMAN_AURA_MAGICPOWER = 1838; +const int FEAT_DRAGONSHAMAN_AURA_ENERGY = 1837; +const int FEAT_DRAGONSHAMAN_ARMOR = 3960; +const int FEAT_DRAGONSHAMAN_ENERGY_IMMUNITY = 3966; +const int FEAT_DRAGONSHAMAN_WINGS = 3978; +const int FEAT_DRAGONSHAMAN_RED = 3968; +const int FEAT_DRAGONSHAMAN_BLACK = 3971; +const int FEAT_DRAGONSHAMAN_BLUE = 3969; +const int FEAT_DRAGONSHAMAN_SILVER = 3973; +const int FEAT_DRAGONSHAMAN_BRASS = 3975; +const int FEAT_DRAGONSHAMAN_GOLD = 3974; +const int FEAT_DRAGONSHAMAN_GREEN = 3970; +const int FEAT_DRAGONSHAMAN_COPPER = 3977; +const int FEAT_DRAGONSHAMAN_WHITE = 3972; +const int FEAT_DRAGONSHAMAN_BRONZE = 3976; +const int FEAT_DRACONIC_AURA_LEVEL_1 = 3979; +const int FEAT_DRACONIC_AURA_LEVEL_2 = 3980; +const int FEAT_DRACONIC_AURA_LEVEL_3 = 3981; +const int FEAT_DRACONIC_AURA_LEVEL_4 = 3982; +const int FEAT_DRACONIC_AURA_LEVEL_5 = 3983; +const int FEAT_DRACONIC_AURA_LEVEL_6 = 3984; +const int FEAT_DRACONIC_AURA_LEVEL_7 = 3985; +const int FEAT_DRACONIC_AURA_LEVEL_8 = 3986; +const int FEAT_DRACONIC_AURA_LEVEL_9 = 3987; +const int FEAT_SHAMANIC_INVOCATION = 3963; + +//Other Draconic Aura Feats +const int FEAT_DOUBLE_DRACONIC_AURA = 1883; +const int FEAT_BONUS_AURA_SENSES = 1891; +const int FEAT_BONUS_AURA_PRESENCE = 1885; +const int FEAT_BONUS_AURA_RESISTACID = 1886; +const int FEAT_BONUS_AURA_RESISTCOLD = 1887; +const int FEAT_BONUS_AURA_RESISTELEC = 1888; +const int FEAT_BONUS_AURA_RESISTFIRE = 1889; +const int FEAT_BONUS_AURA_TOUGHNESS = 1894; +const int FEAT_BONUS_AURA_INSIGHT = 1884; +const int FEAT_BONUS_AURA_RESOLVE = 1890; +const int FEAT_BONUS_AURA_STAMINA = 1892; +const int FEAT_BONUS_AURA_SWIFTNESS = 1893; +const int FEAT_BONUS_AURA_MAGICPOWER = 1840; +const int FEAT_BONUS_AURA_ENERGYACID = 1828; +const int FEAT_BONUS_AURA_ENERGYCOLD = 1829; +const int FEAT_BONUS_AURA_ENERGYELEC = 1830; +const int FEAT_BONUS_AURA_ENERGYFIRE = 1831; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_1 = 1845; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_2 = 1846; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_3 = 1847; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_4 = 1848; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_5 = 1849; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_6 = 1850; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_7 = 1851; +const int FEAT_MARSHAL_DRACONIC_AURA_LEVEL_8 = 1852; +const int FEAT_MARSHAL_AURA_SENSES = 1879; +const int FEAT_MARSHAL_AURA_PRESENCE = 1873; +const int FEAT_MARSHAL_AURA_RESISTACID = 1874; +const int FEAT_MARSHAL_AURA_RESISTCOLD = 1875; +const int FEAT_MARSHAL_AURA_RESISTELEC = 1876; +const int FEAT_MARSHAL_AURA_RESISTFIRE = 1877; +const int FEAT_MARSHAL_AURA_TOUGHNESS = 1882; +const int FEAT_MARSHAL_AURA_INSIGHT = 1872; +const int FEAT_MARSHAL_AURA_RESOLVE = 1878; +const int FEAT_MARSHAL_AURA_STAMINA = 1880; +const int FEAT_MARSHAL_AURA_SWIFTNESS = 1881; +const int FEAT_MARSHAL_AURA_MAGICPOWER = 1839; +const int FEAT_MARSHAL_AURA_ENERGYACID = 1832; +const int FEAT_MARSHAL_AURA_ENERGYCOLD = 1833; +const int FEAT_MARSHAL_AURA_ENERGYELEC = 1834; +const int FEAT_MARSHAL_AURA_ENERGYFIRE = 1835; + +//Warlock feats +const int FEAT_IMBUE_ITEM = 4459; +const int FEAT_ELDRITCH_BLAST = 4460; +const int FEAT_FIENDISH_RESILIENCE = 4466; +const int FEAT_WARLOCK_RESIST_ACID = 4461; +const int FEAT_WARLOCK_RESIST_COLD = 4462; +const int FEAT_WARLOCK_RESIST_ELEC = 4463; +const int FEAT_WARLOCK_RESIST_FIRE = 4464; +const int FEAT_WARLOCK_RESIST_SONIC = 4465; +const int FEAT_WARLOCK_REDUCTION = 4467; +const int FEAT_DECEIVE_ITEM = 4468; +const int FEAT_EXTRA_INVOCATION_I = 4474; +const int FEAT_EXTRA_INVOCATION_II = 4475; +const int FEAT_EXTRA_INVOCATION_III = 4476; +const int FEAT_EXTRA_INVOCATION_IV = 4477; +const int FEAT_EXTRA_INVOCATION_V = 4478; +const int FEAT_EXTRA_INVOCATION_VI = 4479; +const int FEAT_EXTRA_INVOCATION_VII = 4480; +const int FEAT_EXTRA_INVOCATION_VIII = 4481; +const int FEAT_EXTRA_INVOCATION_IX = 4482; +const int FEAT_EXTRA_INVOCATION_X = 4483; +const int FEAT_ELDRITCH_SCULPTOR = 4469; +const int FEAT_LORD_OF_ALL_ESSENCES = 4470; +const int FEAT_MASTER_OF_THE_ELEMENTS = 4484; +const int FEAT_MORPHEME_SAVANT = 4471; +const int FEAT_PARAGON_VISIONARY = 4472; +const int FEAT_WARLOCK_SHADOWMASTER = 4473; +const int FEAT_EPIC_EXTRA_INVOCATION_I = 7700; +const int FEAT_EPIC_EXTRA_INVOCATION_II = 7701; +const int FEAT_EPIC_EXTRA_INVOCATION_III = 7702; +const int FEAT_EPIC_EXTRA_INVOCATION_IV = 7703; +const int FEAT_EPIC_EXTRA_INVOCATION_V = 7704; +const int FEAT_EPIC_EXTRA_INVOCATION_VI = 7705; +const int FEAT_EPIC_EXTRA_INVOCATION_VII = 7706; +const int FEAT_EPIC_EXTRA_INVOCATION_VIII = 7707; +const int FEAT_EPIC_EXTRA_INVOCATION_IX = 7708; +const int FEAT_EPIC_EXTRA_INVOCATION_X = 7709; +const int FEAT_EPIC_ELDRITCH_BLAST_I = 7710; +const int FEAT_EPIC_ELDRITCH_BLAST_II = 7711; +const int FEAT_EPIC_ELDRITCH_BLAST_III = 7712; +const int FEAT_EPIC_ELDRITCH_BLAST_IV = 7713; +const int FEAT_EPIC_ELDRITCH_BLAST_V = 7714; +const int FEAT_EPIC_ELDRITCH_BLAST_VI = 7715; +const int FEAT_EPIC_ELDRITCH_BLAST_VII = 7716; +const int FEAT_EPIC_ELDRITCH_BLAST_VIII = 7717; +const int FEAT_EPIC_ELDRITCH_BLAST_IX = 7718; +const int FEAT_EPIC_ELDRITCH_BLAST_X = 7719; +const int FEAT_EPIC_FIENDISH_RESILIENCE_I = 7720; +const int FEAT_EPIC_FIENDISH_RESILIENCE_II = 7721; +const int FEAT_EPIC_FIENDISH_RESILIENCE_III = 7722; +const int FEAT_EPIC_FIENDISH_RESILIENCE_IV = 7723; +const int FEAT_EPIC_FIENDISH_RESILIENCE_V = 7724; +const int FEAT_EPIC_FIENDISH_RESILIENCE_VI = 7725; +const int FEAT_EPIC_FIENDISH_RESILIENCE_VII = 7726; +const int FEAT_EPIC_FIENDISH_RESILIENCE_VIII = 7727; +const int FEAT_EPIC_FIENDISH_RESILIENCE_IX = 7728; +const int FEAT_EPIC_FIENDISH_RESILIENCE_X = 7729; +const int FEAT_WARLOCK_SHADOWMASTER_SHADES = 4485; +const int FEAT_VERMINLORD = 5323; + +//Master Alchemist and various crafting + +const int FEAT_SKILL_FOCUS_ALCHEMY = 24000; +const int FEAT_EPIC_SKILL_FOCUS_ALCHEMY = 24001; +const int FEAT_MAGICAL_ARTISAN_CRAFT_MAGIC_ARMS = 24002; +const int FEAT_MAGICAL_ARTISAN_CRAFT_ROD = 24003; +const int FEAT_MAGICAL_ARTISAN_CRAFT_STAFF = 24004; +const int FEAT_MAGICAL_ARTISAN_CRAFT_WAND = 24005; +const int FEAT_MAGICAL_ARTISAN_CRAFT_WONDROUS = 24006; +const int FEAT_MAGICAL_ARTISAN_FORGE_RING = 24007; +const int FEAT_MAGICAL_ARTISAN_SCRIBE_SCROLL = 24008; +const int FEAT_MAGICAL_ARTISAN_ATTUNE_GEM = 24009; +const int FEAT_MAGICAL_ARTISAN_INSCRIBE_RUNE = 24010; +const int FEAT_MAGICAL_ARTISAN_BREW_POTION = 24011; +const int FEAT_BREW_2PERDAY = 24012; +const int FEAT_BREW_3PERDAY = 24013; +const int FEAT_BREW_4PERDAY = 24014; +const int FEAT_BREW_POTION_4TH = 24015; +const int FEAT_BREW_POTION_5TH = 24016; +const int FEAT_BREW_POTION_6TH = 24017; +const int FEAT_BREW_POTION_7TH = 24018; +const int FEAT_BREW_POTION_8TH = 24019; +const int FEAT_BREW_POTION_9TH = 24020; +const int FEAT_MAGICAL_ARTISAN_CRAFT_SKULL_TALISMAN = 24022; + +//Tenjac's 3.3 feats +const int FEAT_DAUNTING_PRESENCE = 24025; +const int FEAT_PROFANE_LIFELEECH = 24026; +const int FEAT_SACRED_VITALITY = 24027; +const int FEAT_SACRED_VENGEANCE = 24028; +const int FEAT_DEFORM_EYES = 24029; +const int FEAT_DEFORM_FACE = 24030; +const int FEAT_DEFORM_PARASITE = 24031; +const int FEAT_APOSTATE = 24032; +const int FEAT_DARK_SPEECH = 24033; +const int FEAT_DARK_WHISPERS = 24034; +const int FEAT_MASTERS_WILL = 24035; +const int FEAT_DEFORM_MADNESS = 24036; +const int FEAT_REFLEXIVE_PSYCHOSIS = 24037; +const int FEAT_CHOSEN_OF_EVIL = 24038; + +//skullclan hunter +const int FEAT_SH_IMMUNITY_DISEASE = 2216; +const int FEAT_SH_IMMUNITY_PARALYSIS = 2217; +const int FEAT_SH_IMMUNITY_ABILITY_DECREASE = 2218; +const int FEAT_SH_IMMUNITY_LEVEL_DRAIN = 2219; + + +/*////////////////////////////////////////////////// +//////////////// TRUENAMING //////////////////////// +//////////////////////////////////////////////////*/ + +// General Feats +const int FEAT_TRUENAME_TRAINING = 2175; +const int FEAT_SKILL_FOCUS_TRUESPEAK = 2182; +const int FEAT_EPIC_SKILL_FOCUS_TRUESPEAK = 2183; + +// Metautterance feats +const int FEAT_EMPOWER_UTTERANCE = 2172; +const int FEAT_EXTEND_UTTERANCE = 2173; +const int FEAT_QUICKEN_UTTERANCE = 2174; + +// Recitation feats +const int FEAT_RECITATION_FORTIFIED = 2167; +const int FEAT_RECITATION_MEDITATIVE = 2168; +const int FEAT_RECITATION_MINDFUL = 2169; +const int FEAT_RECITATION_SANGUINE = 2170; +const int FEAT_RECITATION_VITAL = 2171; + +// Utterance Focus +const int FEAT_UTTERANCE_FOCUS_BREATH_CLEANSING = 2155; +const int FEAT_UTTERANCE_FOCUS_BREATH_RECOVERY = 2156; +const int FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION = 2157; +const int FEAT_UTTERANCE_FOCUS_MORALE_BOOST = 2158; +const int FEAT_UTTERANCE_FOCUS_PRETERNATURAL_CLARITY = 2159; +const int FEAT_UTTERANCE_FOCUS_SENSORY_FOCUS = 2160; +const int FEAT_UTTERANCE_FOCUS_SILENT_CASTER = 2161; +const int FEAT_UTTERANCE_FOCUS_SINGULAR_MIND = 2162; +const int FEAT_UTTERANCE_FOCUS_TEMPORAL_SPIRAL = 2163; +const int FEAT_UTTERANCE_FOCUS_TEMPORAL_TWIST = 2164; +const int FEAT_UTTERANCE_FOCUS_WARD_PEACE = 2165; +const int FEAT_UTTERANCE_FOCUS_SHOCKWAVE = 2166; + +// Focused Lexicon +const int FEAT_FOCUSED_LEXICON_ABERRATION = 2130; +const int FEAT_FOCUSED_LEXICON_ANIMAL = 2131; +const int FEAT_FOCUSED_LEXICON_BEAST = 2132; +const int FEAT_FOCUSED_LEXICON_CONSTRUCT = 2133; +const int FEAT_FOCUSED_LEXICON_DRAGON = 2134; +const int FEAT_FOCUSED_LEXICON_DWARF = 2135; +const int FEAT_FOCUSED_LEXICON_ELEMENTAL = 2136; +const int FEAT_FOCUSED_LEXICON_ELF = 2137; +const int FEAT_FOCUSED_LEXICON_FEY = 2138; +const int FEAT_FOCUSED_LEXICON_GIANT = 2139; +const int FEAT_FOCUSED_LEXICON_GNOME = 2140; +const int FEAT_FOCUSED_LEXICON_HALFELF = 2141; +const int FEAT_FOCUSED_LEXICON_HALFLING = 2142; +const int FEAT_FOCUSED_LEXICON_HALFORC = 2143; +const int FEAT_FOCUSED_LEXICON_HUMAN = 2144; +const int FEAT_FOCUSED_LEXICON_GOBLINOID = 2145; +const int FEAT_FOCUSED_LEXICON_MONSTROUS = 2146; +const int FEAT_FOCUSED_LEXICON_ORC = 2147; +const int FEAT_FOCUSED_LEXICON_REPTILIAN = 2148; +const int FEAT_FOCUSED_LEXICON_MAGICALBEAST = 2149; +const int FEAT_FOCUSED_LEXICON_OOZE = 2150; +const int FEAT_FOCUSED_LEXICON_OUTSIDER = 2151; +const int FEAT_FOCUSED_LEXICON_SHAPECHANGER = 2152; +const int FEAT_FOCUSED_LEXICON_UNDEAD = 2153; +const int FEAT_FOCUSED_LEXICON_VERMIN = 2154; + +// Recitation feats +const int FEAT_EGO_BULL = 1853; +const int FEAT_EGO_IRON = 1854; +const int FEAT_EGO_HEART = 1855; +const int FEAT_EGO_SWALLOW = 1856; +const int FEAT_EGO_WOUND = 1857; +const int FEAT_EGO_FOOL = 1858; +const int FEAT_EGO_FORT = 1859; +const int FEAT_EGO_FRIGHT = 1860; +const int FEAT_EGO_DRAKE = 1861; +const int FEAT_EGO_STEP = 1862; + +/*////////////////////////////////////////////////// +//////////////// END TRUENAMING///////////////////// +//////////////////////////////////////////////////*/ + +/*////////////////////////////////////////////////// +//////////////// BEGIN TOME OF BATTLE/////////////// +//////////////////////////////////////////////////*/ + +const int FEAT_EXTRA_GRANTED_MANEUVER = 4096; +const int FEAT_EXTRA_READIED_MANEUVER = 4097; +const int FEAT_ADAPTIVE_STYLE = 24075; +const int FEAT_DESERT_FIRE = 4178; +const int FEAT_DESERT_WIND_DODGE = 4179; +const int FEAT_DEVOTED_BULWARK = 4180; +const int FEAT_AVENGING_STRIKE = 4297; +const int FEAT_RAPID_ASSAULT = 4299; +const int FEAT_SUPERIOR_UNARMED_STRIKE = 4386; +const int FEAT_SNAP_KICK = 4398; +const int FEAT_WEAPON_FOCUS_APTITUDE_1 = 2123; +const int FEAT_EPIC_WEAPON_FOCUS_APTITUDE_1 = 2124; +const int FEAT_WEAPON_SPECIALIZATION_APTITUDE_1 = 2125; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_APTITUDE_1 = 2126; +const int FEAT_IMPROVED_CRITICAL_APTITUDE_1 = 2127; +const int FEAT_SANCTIFY_MARTIAL_STRIKE_APTITUDE_1 = 2128; +const int FEAT_VILE_MARTIAL_STRIKE_APTITUDE_1 = 2129; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_APTITUDE_1 = 24424; +const int FEAT_EPIC_DEVASTATING_CRITICAL_APTITUDE_1 = 24425; +const int FEAT_WEAPON_OF_CHOICE_APTITUDE_1 = 24426; +const int FEAT_WEAPON_FOCUS_APTITUDE_2 = 24427; +const int FEAT_EPIC_WEAPON_FOCUS_APTITUDE_2 = 24428; +const int FEAT_WEAPON_SPECIALIZATION_APTITUDE_2 = 24429; +const int FEAT_EPIC_WEAPON_SPECIALIZATION_APTITUDE_2 = 24430; +const int FEAT_IMPROVED_CRITICAL_APTITUDE_2 = 24431; +const int FEAT_SANCTIFY_MARTIAL_STRIKE_APTITUDE_2 = 24432; +const int FEAT_VILE_MARTIAL_STRIKE_APTITUDE_2 = 24433; +const int FEAT_EPIC_OVERWHELMING_CRITICAL_APTITUDE_2 = 24434; +const int FEAT_EPIC_DEVASTATING_CRITICAL_APTITUDE_2 = 24435; +const int FEAT_WEAPON_OF_CHOICE_APTITUDE_2 = 24436; +const int FEAT_VITAL_RECOVERY = 3636; +const int FEAT_SHADOW_BLADE = 3828; +const int FEAT_BLADE_MEDITATION_DESERT_WIND = 4099; +const int FEAT_BLADE_MEDITATION_DEVOTED_SPIRIT = 3829; +const int FEAT_BLADE_MEDITATION_DIAMOND_MIND = 3830; +const int FEAT_BLADE_MEDITATION_IRON_HEART = 3831; +const int FEAT_BLADE_MEDITATION_SETTING_SUN = 3832; +const int FEAT_BLADE_MEDITATION_SHADOW_HAND = 3833; +const int FEAT_BLADE_MEDITATION_STONE_DRAGON = 3834; +const int FEAT_BLADE_MEDITATION_TIGER_CLAW = 3835; +const int FEAT_BLADE_MEDITATION_WHITE_RAVEN = 3836; +const int FEAT_DIVINE_SPIRIT = 3964; +const int FEAT_IRONHEART_AURA = 3990; +const int FEAT_SHADOW_TRICKSTER = 3999; +const int FEAT_WHITE_RAVEN_DEFENSE = 4074; +const int FEAT_INSTANT_CLARITY = 4098; +const int FEAT_SUDDEN_RECOVERY = 4242; +const int FEAT_SONG_WHITE_RAVEN = 4243; +const int FEAT_PSYCHIC_RENEWAL = 2890; +const int FEAT_TIGER_BLOODED = 5295; +const int FEAT_STONE_POWER = 5294; + +const int FEAT_BATTLE_CLARITY = 3529; +const int FEAT_WEAPON_APTITUDE = 3538; +const int FEAT_BATTLE_ARDOR = 3539; +const int FEAT_BATTLE_CUNNING = 3546; +const int FEAT_BATTLE_SKILL = 3547; +const int FEAT_BATTLE_MASTERY = 3564; +const int FEAT_STANCE_MASTERY = 3565; + +const int FEAT_SS_DF_WF_DW = 2195; +const int FEAT_SS_DF_WF_DM = 2196; +const int FEAT_SS_DF_WF_SS = 2197; +const int FEAT_SS_DF_WF_SH = 2198; +const int FEAT_SS_DF_WF_SD = 2199; +const int FEAT_SS_DF_WF_TC = 2200; + +const int FEAT_SS_DF_IS_DW = 2201; +const int FEAT_SS_DF_IS_DM = 2202; +const int FEAT_SS_DF_IS_SS = 2203; +const int FEAT_SS_DF_IS_SH = 2204; +const int FEAT_SS_DF_IS_SD = 2205; +const int FEAT_SS_DF_IS_TC = 2206; + +const int FEAT_SS_DF_DS_DW = 2207; +const int FEAT_SS_DF_DS_DM = 2208; +const int FEAT_SS_DF_DS_SS = 2209; +const int FEAT_SS_DF_DS_SH = 2210; +const int FEAT_SS_DF_DS_SD = 2211; +const int FEAT_SS_DF_DS_TC = 2212; + +/*////////////////////////////////////////////////// +//////////////// END TOME OF BATTLE///////////////// +//////////////////////////////////////////////////*/ + +/*////////////////////////////////////////////////// +//////////////// BEGIN INFUSIONS /////////////// +//////////////////////////////////////////////////*/ + +const int FEAT_ARTISAN_BONUS = 4065; +const int FEAT_ITEM_CREATION = 4066; +const int FEAT_CRAFT_HOMUNCULUS = 4067; +const int FEAT_RETAIN_ESSENCE = 4068; +const int FEAT_METAMAGIC_SPELL_TRIGGER = 4069; +const int FEAT_METAMAGIC_SPELL_COMPLETION = 4070; +const int FEAT_SKILL_MASTERY_ARTIFICER = 4071; +const int FEAT_EPIC_ARTIFICER = 4072; + +/*////////////////////////////////////////////////// +//////////////// END INFUSIONS ///////////////// +//////////////////////////////////////////////////*/ + +//Justice of Weald and Woe +const int FEAT_LUCKY_SHOT = 24021; + +//:: Bloodclaw Master +const int FEAT_BCM_RENDING_CLAWS = 24065; + +//:: Ironmind +const int FEAT_ARMOURED_MIND = 3644; +const int FEAT_MIND_OVER_BODY = 3645; + +// Dread Necromancer +const int FEAT_DN_SCABROUS_TOUCH = 24145; +const int FEAT_DN_NEG_NRG_BURST = 24149; +const int FEAT_DN_ENERVATING_TOUCH = 24153; + +// Knight of the Middle Circle +const int FEAT_KOTMC_TRUE_STRIKE = 2579; + +// Noble +const int FEAT_NOBLE_GREATNESS = 24163; +const int FEAT_NOBLE_CONFIDENCE = 24164; + +//Morninglord feats +const int FEAT_GREATER_TURNING_1 = 24165; +const int FEAT_GREATER_TURNING_2 = 24166; +const int FEAT_GREATER_TURNING_3 = 24167; +const int FEAT_GREATER_TURNING_4 = 24168; +const int FEAT_GREATER_TURNING_5 = 24169; +const int FEAT_GREATER_TURNING_6 = 24170; +const int FEAT_GREATER_TURNING_7 = 24171; +const int FEAT_ML_MAXIMIZE_TURNING = 24172; +const int FEAT_BANE_OF_THE_RESTLESS = 24173; +const int FEAT_CREATIVE_FIRE = 24174; +const int FEAT_SEARING_RAY = 24175; +const int FEAT_BLESSING_OF_DAWN = 24176; +const int FEAT_REJUVENATION_OF_MORN = 24177; +const int FEAT_ML_AURA_OF_RADIANCE = 24178; +const int FEAT_EPIC_MORNINGLORD = 24179; +const int FEAT_ML_DAYLIGHT = 24180; + +//Alienist feats +const int FEAT_SUMMON_ALIEN = 24214; +const int FEAT_ALIEN_BLESSING = 24215; +const int FEAT_MAD_CERTAINTY = 24216; +const int FEAT_PSEUDONATURAL_FAMILIAR = 24217; +const int FEAT_EXTRA_SUMMONING = 24218; +const int FEAT_INSANE_CERTAINTY = 24219; +const int FEAT_TIMELESS_BODY = 24220; +const int FEAT_TRANSCENDENCE = 24221; +const int FEAT_PHOBIA_ABERRATION = 24222; +const int FEAT_PHOBIA_ANIMAL = 24223; +const int FEAT_PHOBIA_BEAST = 24224; +const int FEAT_PHOBIA_CONSTRUCT = 24225; +const int FEAT_PHOBIA_DRAGON = 24226; +const int FEAT_PHOBIA_GOBLINOID = 24227; +const int FEAT_PHOBIA_MONSTROUS = 24228; +const int FEAT_PHOBIA_ORC = 24229; +const int FEAT_PHOBIA_REPTILIAN = 24230; +const int FEAT_PHOBIA_ELEMENTAL = 24231; +const int FEAT_PHOBIA_FEY = 24232; +const int FEAT_PHOBIA_GIANT = 24233; +const int FEAT_PHOBIA_MAGICAL_BEAST = 24234; +const int FEAT_PHOBIA_SHAPECHANGER = 24235; +const int FEAT_PHOBIA_UNDEAD = 24236; +const int FEAT_PHOBIA_VERMIN = 24237; + +//PnP Familiars +const int FEAT_PNP_FAMILIAR = 24243; + +//Material components +const int FEAT_ESCHEW_MATERIALS = 2596; +const int FEAT_IGNORE_MATERIALS = 2597; + +//Rage Mage +const int FEAT_GREATER_RAGE = 329; +const int FEAT_SPELL_RAGE = 24244; +const int FEAT_SPELL_FURY = 24247; +const int FEAT_TIRELESS_RAGE = 24248; +const int FEAT_WARRIOR_CRY = 24249; + +//Shining Blade of Heironius +const int FEAT_SHOCK_BLADE = 4184; +const int FEAT_HOLY_BLADE = 4186; +const int FEAT_BRILLIANT_BLADE = 4188; + +// Metamagic Feat Abilities +const int FEAT_EXTEND_SPELL_ABILITY = 23595; +const int FEAT_SILENT_SPELL_ABILITY = 23596; +const int FEAT_STILL_SPELL_ABILITY = 23597; +const int FEAT_EMPOWER_SPELL_ABILITY = 23598; +const int FEAT_MAXIMIZE_SPELL_ABILITY = 23599; +const int FEAT_QUICKEN_SPELL_ABILITY = 23600; + +//contemplative +const int FEAT_DIVINE_SOUL = 2247; + +const int FEAT_BATTLE_CASTER = 23589; +const int FEAT_TOMB_TAINTED_SOUL = 23555; + +//crusader +const int FEAT_INDOMITABLE_SOUL = 2867; +const int FEAT_CRUSADER_SMITE = 2863; + +//Jade Phoenix Mage +const int FEAT_JPM_RITE_WAKING = 24077; + +//Templar +const int FEAT_SECULAR_AUTHORITY = 23545; + +// Blighter +const int FEAT_UNDEAD_WILD_SHAPE = 5262; +const int FEAT_CONTAGIOUS_TOUCH = 5261; + +// Nentyar Hunter +const int FEAT_SWIFT_TRACKER = 5254; +const int FEAT_SWIFTNESS_STAG = 5255; +const int FEAT_EYES_HAWK = 5254; +const int FEAT_HEART_LION = 5255; + +//Archivist +const int FEAT_LORE_MASTERY = 23580; +const int FEAT_ARCHIVIST_OF_NATURE = 23581; +const int FEAT_DRACONIC_ARCHIVIST = 23582; +const int FEAT_DK_TACTICS = 23583; +const int FEAT_DK_PUISSANCE = 23584; +const int FEAT_DK_FOE = 23585; +const int FEAT_DK_DREADSECRET = 23586; +const int FEAT_DK_FOREKNOWLEDGE = 23587; +const int FEAT_EPIC_DARK_KNOWLEDGE = 23546; +const int FEAT_EPIC_LORE_MASTERY = 23547; + + +const int FEAT_SKILL_FOCUS_RIDE = 3037; +const int FEAT_EPIC_SKILL_FOCUS_RIDE = 3038; +const int FEAT_SKILL_FOCUS_JUMP = 3039; +const int FEAT_EPIC_SKILL_FOCUS_JUMP = 3040; +const int FEAT_SKILL_FOCUS_SENSE_MOTIVE = 3041; +const int FEAT_EPIC_SKILL_FOCUS_SENSE_MOTIVE = 3042; +const int FEAT_SKILL_FOCUS_MARTIAL_LORE = 3043; +const int FEAT_EPIC_SKILL_FOCUS_MARTIAL_LORE = 3044; +const int FEAT_SKILL_FOCUS_BALANCE = 3045; +const int FEAT_EPIC_SKILL_FOCUS_BALANCE = 3046; +const int FEAT_SKILL_FOCUS_CRAFT_POISON = 3047; +const int FEAT_EPIC_SKILL_FOCUS_CRAFT_POISON = 3048; +const int FEAT_SKILL_FOCUS_PSICRAFT = 3049; +const int FEAT_EPIC_SKILL_FOCUS_PSICRAFT = 3050; +const int FEAT_SKILL_FOCUS_CLIMB = 3051; +const int FEAT_EPIC_SKILL_FOCUS_CLIMB = 3052; +const int FEAT_SKILL_FOCUS_CRAFT_GENERAL = 3053; +const int FEAT_EPIC_SKILL_FOCUS_CRAFT_GENERAL = 3054; + +//hellfire warlock +const int FEAT_HELLFIRE_SPEAR = 23533; +const int FEAT_HELLFIRE_GLAIVE = 23534; +const int FEAT_HELLFIRE_BLOW = 23535; +const int FEAT_HELLFIRE_CHAIN = 23536; +const int FEAT_HELLFIRE_CONE = 23537; +const int FEAT_HELLFIRE_LINE = 23538; +const int FEAT_HELLFIRE_DOOM = 23539; +const int FEAT_HELLFIRE_BLAST = 23540; +const int FEAT_HELLFIRE_INFUSION = 23541; +const int FEAT_HELLFIRE_SHIELD = 23542; +const int FEAT_RESISTANCE_TO_FIRE = 23543; + +const int FEAT_ELDRITCH_SPELLWEAVE = 23529; +const int FEAT_TRAPFINDING = 23591; + +// Complete Scoundrel Feats +const int FEAT_DARING_OUTLAW = 2374; +const int FEAT_SWIFT_AMBUSHER = 2548; +const int FEAT_ASCETIC_STALKER = 2598; +const int FEAT_EXPANDED_KI_POOL = 2687; +const int FEAT_IMPROVED_SKIMISH = 2688; + +//:: Complete Adventurer Feats +const int FEAT_BRUTAL_THROW = 2689; +const int FEAT_MARTIAL_STALKER = 25998; + +// Complete Warrior Feats +const int FEAT_EXTRA_RAGE = 2690; +const int FEAT_FLYING_KICK = 2802; +const int FEAT_ANVIL_OF_THUNDER = 5290; +const int FEAT_HAMMERS_EDGE = 5289; +const int FEAT_HIGH_SWORD_LOW_AXE = 5288; +const int FEAT_SPINNING_HALBERD = 5287; +const int FEAT_THREE_MOUNTAINS = 5286; +const int FEAT_MONKEY_GRIP = 5197; +const int FEAT_CRESCENT_MOON = 5194; +const int FEAT_QUICK_STAFF = 5190; +const int FEAT_RANGED_DISARM = 5192; +const int FEAT_BEAR_FANG = 5189; +const int FEAT_IMPROVED_RAPID_SHOT = 5188; +const int FEAT_EARTHS_EMBRACE = 5177; +const int FEAT_SHIELD_CHARGE = 3256; +const int FEAT_SHIELD_SLAM = 3257; + +// CityScape Feats +const int FEAT_EFFICIENT_DEFENDER = 5293; +const int FEAT_STRONG_STOMACH = 5329; + +// Heroes of Battle Feats +const int FEAT_IMPROVED_COHORT = 5276; +const int FEAT_SHIELD_WALL = 5273; + +// Heroes of Horror Feats +const int FEAT_CORRUPT_SPELL_FOCUS = 5272; +const int FEAT_GREATER_CORRUPT_SPELL_FOCUS = 5271; +const int FEAT_HAUNTING_MELODY = 5269; +const int FEAT_MASTER_OF_KNOWLEDGE = 5268; +const int FEAT_UNNATURAL_WILL = 5267; + +// Eberron Campaign Setting Feats +const int FEAT_ECCLESIARCH = 5275; +const int FEAT_WAND_MASTERY = 5314; + +// Secrets of Sarlona Feats +const int FEAT_MOUNTAIN_STANCE = 5193; + +// Oriental Adventures Feats +const int FEAT_GREAT_DIPLOMAT = 5274; + +// DungeonScape Feats +const int FEAT_TRAP_ENGINEER = 5292; +const int FEAT_WEAPON_AND_TORCH = 5291; +const int FEAT_FONT_INSPIRATION_1 = 5339; +const int FEAT_FONT_INSPIRATION_2 = 5340; +const int FEAT_FONT_INSPIRATION_3 = 5341; +const int FEAT_FONT_INSPIRATION_4 = 5342; +const int FEAT_FONT_INSPIRATION_5 = 5343; +const int FEAT_FONT_INSPIRATION_6 = 5344; +const int FEAT_FONT_INSPIRATION_7 = 5345; +const int FEAT_FONT_INSPIRATION_8 = 5346; +const int FEAT_FONT_INSPIRATION_9 = 5347; +const int FEAT_FONT_INSPIRATION_10 = 5348; +const int FEAT_OPPORTUNISTIC_PIETY_HEAL = 5358; +const int FEAT_OPPORTUNISTIC_PIETY_TURN = 5359; + +// Combat Maneuver Feats +const int FEAT_CURLING_WAVE_STRIKE = 2809; +const int FEAT_SIDESTEP_CHARGE = 3505; +const int FEAT_POWERFUL_CHARGE = 3506; +const int FEAT_GREATER_POWERFUL_CHARGE = 3507; +const int FEAT_RHINO_TRIBE_CHARGE = 3508; +const int FEAT_FURIOUS_CHARGE = 3509; +const int FEAT_RECKLESS_CHARGE = 3510; +const int FEAT_LION_TRIBE_WARRIOR = 3571; +const int FEAT_TWO_WEAPON_POUNCE = 3573; +const int FEAT_LEGENDARY_WRESTLER = 5173; +const int FEAT_COMBAT_MOVE_1 = 2898; +const int FEAT_COMBAT_MOVE_2 = 5195; +const int FEAT_COMBAT_MOVE_3 = 3415; + +// Unapproachable East Feats +const int FEAT_OWLBEAR_BERSERKER = 5175; +const int FEAT_SNOW_TIGER_BERSERKER = 3572; +const int FEAT_GREAT_STAG_BERSERKER = 3511; +const int FEAT_ETTERCAP_BERSERKER = 5320; +const int FEAT_ICE_TROLL_BERSERKER = 5321; +const int FEAT_WOLF_BERSERKER = 5322; +const int FEAT_BONUS_MOUNTAIN = 5172; +const int FEAT_KEEN_INTELLECT = 5366; +const int FEAT_VREMYONNI_TRAINING = 5367; + +// Player's Handbook 3.5 Feats +const int FEAT_PERSUASIVE = 5319; +const int FEAT_IMPROVED_GRAPPLE = 2804; +const int FEAT_IMPROVED_OVERRUN = 2805; +const int FEAT_IMPROVED_BULLRUSH = 2806; +const int FEAT_IMPROVED_TRIP = 2807; +const int FEAT_PRC_IMP_DISARM = 5196; +const int FEAT_AWESOME_BLOW = 5370; +const int FEAT_IMPROVED_SHIELD_BASH = 3250; +const int FEAT_INVESTIGATOR = 25998; + +// Player's Handbook II Feats +const int FEAT_STEADFAST_DETERMINATION = 3267; +const int FEAT_CROSSBOW_SNIPER = 5311; +const int FEAT_SHIELD_SPECIALIZATION_LIGHT = 3251; +const int FEAT_SHIELD_SPECIALIZATION_HEAVY = 3252; +const int FEAT_SHIELD_WARD = 3253; +const int FEAT_AGILE_SHIELD_FIGHTER = 3254; + +// Weapons of Legacy Feats +const int FEAT_LEAST_LEGACY = 5300; +const int FEAT_LESSER_LEGACY = 5301; +const int FEAT_GREATER_LEGACY = 5302; + +// Complete Arcane Feats +const int FEAT_MASTER_WAND = 5312; +const int FEAT_RECKLESS_WAND_WIELDER = 5313; +const int FEAT_DOUBLE_WAND_WIELDER = 5315; +const int FEAT_WILD_MAGE_RANDOM_DEFLECTOR = 5199; + +// Warforged Juggernaut +const int FEAT_JUGG_RESERVED = 5246; + +// Shadowbane +const int FEAT_SHADOWBANE_SMITE = 5232; + +// Killoren +const int FEAT_KILLOREN_HUNTER = 5206; +const int FEAT_KILLOREN_DESTROYER = 5207; +const int FEAT_KILLOREN_ANCIENT = 5208; +const int FEAT_KILLOREN_ASPECT_H = 5209; +const int FEAT_KILLOREN_ASPECT_D = 5210; +const int FEAT_KILLOREN_ASPECT_A = 5211; + +//Abjurant Champion Feats +const int FEAT_ABJURANT_ARMOR = 24415; +const int FEAT_EXTENDED_ABJURATION = 24416; +const int FEAT_ARCANE_BOOST_AB = 24417; +const int FEAT_ARCANE_BOOST_AC = 24418; +const int FEAT_ARCANE_BOOST_DMG = 24419; +const int FEAT_ARCANE_BOOST_ER = 24420; +const int FEAT_ARCANE_BOOST_SV = 24421; +const int FEAT_MARTIAL_ARCANIST = 24422; + +// Cultist of the Shattered Peak +const int FEAT_CULTIST_SMITE_MAGE = 5368; + +/*////////////////////////////////////////////////// +//////////////// SHADOWCASTING////////////////////// +//////////////////////////////////////////////////*/ + +const int FEAT_PATH_FOCUS_CLOAK_SHADOWS = 23673; +const int FEAT_PATH_FOCUS_DARK_TERRAIN = 23674; +const int FEAT_PATH_FOCUS_EBON_WHISPERS = 23675; +const int FEAT_PATH_FOCUS_EYES_DARKNESS = 23676; +const int FEAT_PATH_FOCUS_SHUTTERS_CLOUDS = 23677; +const int FEAT_PATH_FOCUS_TOUCH_TWILIGHT = 23678; +const int FEAT_PATH_FOCUS_UMBRAL_MIND = 23679; +const int FEAT_PATH_FOCUS_BLACK_MAGIC = 23680; +const int FEAT_PATH_FOCUS_BODY_SOUL = 23681; +const int FEAT_PATH_FOCUS_DARK_REFLECTIONS = 23682; +const int FEAT_PATH_FOCUS_EBON_ROADS = 23683; +const int FEAT_PATH_FOCUS_ELEMENTAL_SHADOWS = 23684; +const int FEAT_PATH_FOCUS_UNBINDING_SHADE = 23685; +const int FEAT_PATH_FOCUS_VEIL_SHADOWS = 23686; +const int FEAT_PATH_FOCUS_BREATH_TWILIGHT = 23687; +const int FEAT_PATH_FOCUS_DARK_METAMORPHOSIS = 23688; +const int FEAT_PATH_FOCUS_EBON_WALLS = 23689; +const int FEAT_PATH_FOCUS_EYES_NIGHT_SKY = 23690; +const int FEAT_PATH_FOCUS_HEART_SOUL = 23691; +const int FEAT_PATH_FOCUS_SHADOW_CALLING = 23692; +const int FEAT_GREATER_PATH_FOCUS_CLOAK_SHADOWS = 23693; +const int FEAT_GREATER_PATH_FOCUS_DARK_TERRAIN = 23694; +const int FEAT_GREATER_PATH_FOCUS_EBON_WHISPERS = 23695; +const int FEAT_GREATER_PATH_FOCUS_EYES_DARKNESS = 23696; +const int FEAT_GREATER_PATH_FOCUS_SHUTTERS_CLOUDS = 23697; +const int FEAT_GREATER_PATH_FOCUS_TOUCH_TWILIGHT = 23698; +const int FEAT_GREATER_PATH_FOCUS_UMBRAL_MIND = 23699; +const int FEAT_GREATER_PATH_FOCUS_BLACK_MAGIC = 23700; +const int FEAT_GREATER_PATH_FOCUS_BODY_SOUL = 23701; +const int FEAT_GREATER_PATH_FOCUS_DARK_REFLECTIONS = 23702; +const int FEAT_GREATER_PATH_FOCUS_EBON_ROADS = 23703; +const int FEAT_GREATER_PATH_FOCUS_ELEMENTAL_SHADOWS = 23704; +const int FEAT_GREATER_PATH_FOCUS_UNBINDING_SHADE = 23705; +const int FEAT_GREATER_PATH_FOCUS_VEIL_SHADOWS = 23706; +const int FEAT_GREATER_PATH_FOCUS_BREATH_TWILIGHT = 23707; +const int FEAT_GREATER_PATH_FOCUS_DARK_METAMORPHOSIS = 23708; +const int FEAT_GREATER_PATH_FOCUS_EBON_WALLS = 23709; +const int FEAT_GREATER_PATH_FOCUS_EYES_NIGHT_SKY = 23710; +const int FEAT_GREATER_PATH_FOCUS_HEART_SOUL = 23711; +const int FEAT_GREATER_PATH_FOCUS_SHADOW_CALLING = 23712; +const int FEAT_NOCTURNAL_CASTER_CLOAK_SHADOWS = 23713; +const int FEAT_NOCTURNAL_CASTER_DARK_TERRAIN = 23714; +const int FEAT_NOCTURNAL_CASTER_EBON_WHISPERS = 23715; +const int FEAT_NOCTURNAL_CASTER_EYES_DARKNESS = 23716; +const int FEAT_NOCTURNAL_CASTER_SHUTTERS_CLOUDS = 23717; +const int FEAT_NOCTURNAL_CASTER_TOUCH_TWILIGHT = 23718; +const int FEAT_NOCTURNAL_CASTER_UMBRAL_MIND = 23719; +const int FEAT_NOCTURNAL_CASTER_BLACK_MAGIC = 23720; +const int FEAT_NOCTURNAL_CASTER_BODY_SOUL = 23721; +const int FEAT_NOCTURNAL_CASTER_DARK_REFLECTIONS = 23722; +const int FEAT_NOCTURNAL_CASTER_EBON_ROADS = 23723; +const int FEAT_NOCTURNAL_CASTER_ELEMENTAL_SHADOWS = 23724; +const int FEAT_NOCTURNAL_CASTER_UNBINDING_SHADE = 23725; +const int FEAT_NOCTURNAL_CASTER_VEIL_SHADOWS = 23726; +const int FEAT_NOCTURNAL_CASTER_BREATH_TWILIGHT = 23727; +const int FEAT_NOCTURNAL_CASTER_DARK_METAMORPHOSIS = 23728; +const int FEAT_NOCTURNAL_CASTER_EBON_WALLS = 23729; +const int FEAT_NOCTURNAL_CASTER_EYES_NIGHT_SKY = 23730; +const int FEAT_NOCTURNAL_CASTER_HEART_SOUL = 23731; +const int FEAT_NOCTURNAL_CASTER_SHADOW_CALLING = 23732; +const int FEAT_FAV_MYST_BENDPERSPECTIVE = 23733; +const int FEAT_FAV_MYST_CARPETSHADOW = 23734; +const int FEAT_FAV_MYST_DUSKANDDAWN = 23735; +const int FEAT_FAV_MYST_LIFEFADES = 23736; +const int FEAT_FAV_MYST_MESMERIZINGSHADE = 23737; +const int FEAT_FAV_MYST_STEELSHADOWS = 23738; +const int FEAT_FAV_MYST_VOICEOFSHADOW = 23739; +const int FEAT_FAV_MYST_BLACKFIRE = 23740; +const int FEAT_FAV_MYST_CONGRESSSHADOWS = 23741; +const int FEAT_FAV_MYST_FLESHFAILS = 23742; +const int FEAT_FAV_MYST_PIERCINGSIGHT = 23743; +const int FEAT_FAV_MYST_SHADOWSKIN = 23744; +const int FEAT_FAV_MYST_SIGHTECLIPSED = 23745; +const int FEAT_FAV_MYST_THOUGHTSSHADOW = 23746; +const int FEAT_FAV_MYST_AFRAIDOFTHEDARK = 23747; +const int FEAT_FAV_MYST_CLINGINGDARKNESS = 23748; +const int FEAT_FAV_MYST_DANCINGSHADOWS = 23749; +const int FEAT_FAV_MYST_FLICKER = 23750; +const int FEAT_FAV_MYST_KILLINGSHADOWS = 23751; +const int FEAT_FAV_MYST_SHARPSHADOWS = 23752; +const int FEAT_FAV_MYST_UMBRALTOUCH = 23753; +const int FEAT_FAV_MYST_AURAOFSHADE = 23754; +const int FEAT_FAV_MYST_BOLSTER = 23755; +const int FEAT_FAV_MYST_SHADOWEVOCATION = 23756; +const int FEAT_FAV_MYST_SHADOWVISION = 23757; +const int FEAT_FAV_MYST_SHADOWSFADE = 23758; +const int FEAT_FAV_MYST_STEPINTOSHADOW = 23759; +const int FEAT_FAV_MYST_WARPSPELL = 23760; +const int FEAT_FAV_MYST_CURTAINSHADOWS = 23761; +const int FEAT_FAV_MYST_DARKAIR = 23762; +const int FEAT_FAV_MYST_ECHOSPELL = 23763; +const int FEAT_FAV_MYST_FEIGNLIFE = 23764; +const int FEAT_FAV_MYST_LANGUOR = 23765; +const int FEAT_FAV_MYST_PASSINTOSHADOW = 23766; +const int FEAT_FAV_MYST_UNRAVELDWEOMER = 23767; +const int FEAT_FAV_MYST_FLOODSHADOWS = 23768; +const int FEAT_FAV_MYST_GREATERSHADOWEVOCATION = 23769; +const int FEAT_FAV_MYST_SHADOWINVESTITURE = 23770; +const int FEAT_FAV_MYST_SHADOWSTORM = 23771; +const int FEAT_FAV_MYST_SHADOWSFADE_GREATER = 23772; +const int FEAT_FAV_MYST_UNVEIL = 23773; +const int FEAT_FAV_MYST_VOYAGESHADOW = 23774; +const int FEAT_FAV_MYST_DARKSOUL = 23775; +const int FEAT_FAV_MYST_EPHEMERALIMAGE = 23776; +const int FEAT_FAV_MYST_LIFEFADESGREATER = 23777; +const int FEAT_FAV_MYST_PRISONNIGHT = 23778; +const int FEAT_FAV_MYST_UMBRALSERVANT = 23779; +const int FEAT_FAV_MYST_TRUTHREVEALED = 23780; +const int FEAT_FAV_MYST_FARSIGHT = 23781; +const int FEAT_FAV_MYST_GRFLESHFAILS = 23782; +const int FEAT_FAV_MYST_SHADOWPLAGUE = 23783; +const int FEAT_FAV_MYST_SOULPUPPET = 23784; +const int FEAT_FAV_MYST_TOMBNIGHT = 23785; +const int FEAT_FAV_MYST_UMBRALBODY = 23786; +const int FEAT_FAV_MYST_ARMYSHADOW = 23787; +const int FEAT_FAV_MYST_CONSUMEESSENCE = 23788; +const int FEAT_FAV_MYST_EPHEMERALSTORM = 23789; +const int FEAT_FAV_MYST_REFLECTIONS = 23790; +const int FEAT_FAV_MYST_SHADOWSURGE = 23791; +const int FEAT_FAV_MYST_SHADOWTIME = 23792; +const int FEAT_SHADOW_CAST = 23793; +const int FEAT_EMPOWER_MYSTERY = 23794; +const int FEAT_EXTEND_MYSTERY = 23795; +const int FEAT_MAXIMIZE_MYSTERY = 23796; +const int FEAT_QUICKEN_MYSTERY = 23797; +const int FEAT_STILL_MYSTERY = 23798; +const int FEAT_SHADOW_FAMILIAR = 23800; +// Web Enhancement +const int FEAT_PATH_FOCUS_NIGHTS_LONG_FINGERS = 23851; +const int FEAT_PATH_FOCUS_DARKENED_ALLEYS = 23852; +const int FEAT_PATH_FOCUS_SHADOWSCAPE = 23853; +const int FEAT_GREATER_PATH_FOCUS_NIGHTS_LONG_FINGERS = 23854; +const int FEAT_GREATER_PATH_FOCUS_DARKENED_ALLEYS = 23855; +const int FEAT_GREATER_PATH_FOCUS_SHADOWSCAPE = 23856; +const int FEAT_NOCTURNAL_CASTER_NIGHTS_LONG_FINGERS = 23857; +const int FEAT_NOCTURNAL_CASTER_DARKENED_ALLEYS = 23858; +const int FEAT_NOCTURNAL_CASTER_SHADOWSCAPE = 23859; +const int FEAT_FAV_MYST_QUICKERTHANTHEEYE = 23860; +const int FEAT_FAV_MYST_TRAILHAZE = 23861; +const int FEAT_FAV_MYST_UMBRALFIST = 23862; +const int FEAT_FAV_MYST_FEARFULGLOOM = 23863; +const int FEAT_FAV_MYST_SICKENINGSHADOW = 23864; +const int FEAT_FAV_MYST_DEADLYSHADE = 23865; +const int FEAT_FAV_MYST_GRASPINGSHADOWS = 23866; +const int FEAT_FAV_MYST_MENAGERIEDARKNESS = 23867; +const int FEAT_FAV_MYST_BLACKLABYRINTH = 23868; + +// Child of Night +const int FEAT_CLOAK_SHADOWS = 23801; +const int FEAT_DANCING_SHADOWS = 23802; + +// Noctumancer +const int FEAT_INNATE_COUNTERSPELL = 23814; + +//Shadowblade +const int FEAT_UNERRING_STRIKE = 3650; +const int FEAT_UNEXPECTED_STRIKE = 3653; +const int FEAT_EPHEMERAL_WEAPON = 3654; +const int FEAT_SHADOWY_STRIKE = 3655; +const int FEAT_FAR_SHADOW = 3656; +const int FEAT_SHADOW_AND_STEALTH = 3651; +const int FEAT_ULTRAVISION = 3658; + +//Shadowsmith +const int FEAT_TOUCH_SHADOW = 23831; +const int FEAT_SHROUD_SHADOW = 23832; +const int FEAT_SHADOW_CRAFT = 23833; +const int FEAT_ARMOR_SHADOW = 23834; +const int FEAT_ARMOR_SHADOW_Q = 23835; + +/*////////////////////////////////////////////////// +//////////////// END SHADOWCASTING////////////////// +//////////////////////////////////////////////////*/ + +/*////////////////////////////////////////////////// +//////////////// Magic of Incarnum ///////////////// +//////////////////////////////////////////////////*/ + +// Class Feat Constants +const int FEAT_INCARNUM_RADIANCE = 8861; +const int FEAT_RAPID_MELDSHAPING = 8862; +const int FEAT_SMITE_OPPOSITION = 8864; +const int FEAT_SHARE_INCARNUM_DEFENSE = 8865; +const int FEAT_REBIND_TOTEM_SOULMELD = 8867; +const int FEAT_SMITE_CHAOS = 8858; +const int FEAT_PRC_ATTACK = 2353; +const int FEAT_INCANDESCENT_OVERLOAD = 8925; +const int FEAT_NECROCARNATE_HARVEST = 8930; +const int FEAT_NECROCARNATE_SOULSHIELD = 8931; +const int FEAT_INCARNUM_BLADE_REBIND = 8952; +const int FEAT_WITCHBORN_INTEGUMENT = 8964; + +// Race Feat Constants +const int FEAT_DUSKLING_SPEED = -1; + +// Incarnum Feat Constants +const int FEAT_INVEST_ESSENTIA_CONV = 8800; +const int FEAT_AZURE_ENMITY = 8869; +const int FEAT_CERULEAN_FORTITUDE = 8870; +const int FEAT_AZURE_TALENT = 8871; +const int FEAT_AZURE_TOUCH = 8872; +const int FEAT_AZURE_TOUGHNESS = 8873; +const int FEAT_AZURE_TURNING = 8874; +const int FEAT_AZURE_WILD_SHAPE = 8875; +const int FEAT_CERULEAN_REFLEXES = 8876; +const int FEAT_CERULEAN_WILL = 8877; +const int FEAT_COBALT_CHARGE = 8878; +const int FEAT_COBALT_EXPERTISE = 8879; +const int FEAT_COBALT_POWER = 8880; +const int FEAT_COBALT_RAGE = 8881; +const int FEAT_HEALING_SOUL = 8882; +const int FEAT_INDIGO_STRIKE = 8883; +const int FEAT_MIDNIGHT_AUGMENTATION = 8884; +const int FEAT_MIDNIGHT_DODGE = 8885; +const int FEAT_MIDNIGHT_METAMAGIC = 8886; +const int FEAT_PSYCARNUM_BLADE = 8887; +const int FEAT_SAPPHIRE_SMITE = 8888; +const int FEAT_SOULTOUCHED_SPELLCASTING = 8889; +const int FEAT_BONUS_ESSENTIA = 8890; +const int FEAT_DIVINE_SOULTOUCH = 8891; +const int FEAT_HEART_INCARNUM = 8892; +const int FEAT_IMPROVED_ESSENTIA_CAPACITY = 8893; +const int FEAT_INCARNUM_FORTIFIED_BODY = 8894; +const int FEAT_INCARNUM_RESISTANCE = 8895; +const int FEAT_PSYCARNUM_INFUSION = 8896; +const int FEAT_INCARNUM_SPELLSHAPING = 8897; +const int FEAT_NECROCARNUM_ACOLYTE = 8898; +const int FEAT_OPEN_LEAST_CHAKRA_CROWN = 8899; +const int FEAT_OPEN_LEAST_CHAKRA_FEET = 8900; +const int FEAT_OPEN_LEAST_CHAKRA_HANDS = 8901; +const int FEAT_OPEN_LESSER_CHAKRA_ARMS = 8902; +const int FEAT_OPEN_LESSER_CHAKRA_BROW = 8903; +const int FEAT_OPEN_LESSER_CHAKRA_SHOULDERS = 8904; +const int FEAT_OPEN_GREATER_CHAKRA_THROAT = 8905; +const int FEAT_OPEN_GREATER_CHAKRA_WAIST = 8906; +const int FEAT_SPLIT_CHAKRA_CROWN = 8907; +const int FEAT_SPLIT_CHAKRA_FEET = 8908; +const int FEAT_SPLIT_CHAKRA_HANDS = 8909; +const int FEAT_SPLIT_CHAKRA_ARMS = 8910; +const int FEAT_SPLIT_CHAKRA_BROW = 8911; +const int FEAT_SPLIT_CHAKRA_SHOULDERS = 8912; +const int FEAT_SPLIT_CHAKRA_THROAT = 8913; +const int FEAT_SPLIT_CHAKRA_WAIST = 8914; +const int FEAT_SPLIT_CHAKRA_HEART = 8932; +const int FEAT_SPLIT_CHAKRA_SOUL = 8933; +const int FEAT_UNDEAD_MELDSHAPER = 8934; +const int FEAT_DOUBLE_CHAKRA_CROWN = 8935; +const int FEAT_DOUBLE_CHAKRA_FEET = 8936; +const int FEAT_DOUBLE_CHAKRA_HANDS = 8937; +const int FEAT_DOUBLE_CHAKRA_ARMS = 8938; +const int FEAT_DOUBLE_CHAKRA_BROW = 8939; +const int FEAT_DOUBLE_CHAKRA_SHOULDERS = 8940; +const int FEAT_DOUBLE_CHAKRA_THROAT = 8941; +const int FEAT_DOUBLE_CHAKRA_WAIST = 8942; +const int FEAT_DOUBLE_CHAKRA_HEART = 8943; +const int FEAT_DOUBLE_CHAKRA_SOUL = 8944; +const int FEAT_DOUBLE_CHAKRA_TOTEM = 8945; +const int FEAT_EXPANDED_SOULMELD_CAPACITY_1 = 8946; +const int FEAT_EXPANDED_SOULMELD_CAPACITY_2 = 8947; +const int FEAT_EXPANDED_SOULMELD_CAPACITY_3 = 8948; +const int FEAT_EXPANDED_SOULMELD_CAPACITY_4 = 8949; +const int FEAT_EXPANDED_SOULMELD_CAPACITY_5 = 8950; +const int FEAT_BONUS_SOULMELD_1 = 8965; +const int FEAT_BONUS_SOULMELD_2 = 8966; +const int FEAT_BONUS_SOULMELD_3 = 8967; +const int FEAT_BONUS_SOULMELD_4 = 8968; +const int FEAT_BONUS_SOULMELD_5 = 8969; +const int FEAT_BONUS_SOULMELD_6 = 8970; +const int FEAT_BONUS_SOULMELD_7 = 8971; +const int FEAT_BONUS_SOULMELD_8 = 8972; +const int FEAT_BONUS_SOULMELD_9 = 8973; +const int FEAT_BONUS_SOULMELD_10 = 8974; +const int FEAT_EPIC_ESSENTIA_1 = 8975; +const int FEAT_EPIC_ESSENTIA_2 = 8976; +const int FEAT_EPIC_ESSENTIA_3 = 8977; +const int FEAT_EPIC_ESSENTIA_4 = 8978; +const int FEAT_EPIC_ESSENTIA_5 = 8979; +const int FEAT_EPIC_ESSENTIA_6 = 8980; +const int FEAT_EXTRA_CHAKRA_BIND_1 = 8981; +const int FEAT_EXTRA_CHAKRA_BIND_2 = 8982; +const int FEAT_EXTRA_CHAKRA_BIND_3 = 8983; +const int FEAT_EXTRA_CHAKRA_BIND_4 = 8984; +const int FEAT_EXTRA_CHAKRA_BIND_5 = 8985; +const int FEAT_EXTRA_CHAKRA_BIND_6 = 8986; +const int FEAT_EXTRA_CHAKRA_BIND_7 = 8987; +const int FEAT_EXTRA_CHAKRA_BIND_8 = 8988; +const int FEAT_EXTRA_CHAKRA_BIND_9 = 8989; +const int FEAT_EXTRA_CHAKRA_BIND_10 = 8990; +const int FEAT_OPEN_HEART_CHAKRA = 8991; +const int FEAT_OPEN_SOUL_CHAKRA = 8992; +const int FEAT_RAPID_MELDSHAPING_1 = 8993; +const int FEAT_RAPID_MELDSHAPING_2 = 8994; +const int FEAT_RAPID_MELDSHAPING_3 = 8995; +const int FEAT_RAPID_MELDSHAPING_4 = 8996; +const int FEAT_RAPID_MELDSHAPING_5 = 8997; +const int FEAT_RAPID_MELDSHAPING_6 = 8998; +const int FEAT_RAPID_MELDSHAPING_7 = 8999; +const int FEAT_RAPID_MELDSHAPING_8 = 9000; +const int FEAT_RAPID_MELDSHAPING_9 = 9001; +const int FEAT_RAPID_MELDSHAPING_10 = 9002; +const int FEAT_REBIND_SOULMELD_CROWN = 9003; +const int FEAT_REBIND_SOULMELD_FEET = 9004; +const int FEAT_REBIND_SOULMELD_HANDS = 9005; +const int FEAT_REBIND_SOULMELD_ARMS = 9006; +const int FEAT_REBIND_SOULMELD_BROW = 9007; +const int FEAT_REBIND_SOULMELD_SHOULDERS = 9008; +const int FEAT_REBIND_SOULMELD_THROAT = 9009; +const int FEAT_REBIND_SOULMELD_WAIST = 9010; +const int FEAT_REBIND_SOULMELD_HEART = 9011; +const int FEAT_REBIND_SOULMELD_SOUL = 9012; +const int FEAT_REBIND_SOULMELD_TOTEM = 9013; + +/*////////////////////////////////////////////////// +////////////// End Magic of Incarnum /////////////// +//////////////////////////////////////////////////*/ + +/*////////////////////////////////////////////////// +///////////////////// Binding ///////////////////// +//////////////////////////////////////////////////*/ + +// Binding Feat Constants +const int FEAT_BIND_VESTIGE = 9131; +const int FEAT_BIND_VESTIGE_IMPROVED = 9132; +const int FEAT_PRACTICED_BINDER = 9133; +const int FEAT_SKILLED_PACT_MAKING = 9134; +const int FEAT_IMPROVED_BINDING = 9135; +const int FEAT_EXPEL_VESTIGE = 9136; +const int FEAT_RAPID_PACT_MAKING = 9137; +const int FEAT_FAVORED_VESTIGE_AMON = 9138; +const int FEAT_FAVORED_VESTIGE_AYM = 9139; +const int FEAT_FAVORED_VESTIGE_LERAJE = 9140; +const int FEAT_FAVORED_VESTIGE_NABERIUS = 9141; +const int FEAT_FAVORED_VESTIGE_RONOVE = 9142; +const int FEAT_FAVORED_VESTIGE_DAHLVERNAR = 9143; +const int FEAT_FAVORED_VESTIGE_HAAGENTI = 9144; +const int FEAT_FAVORED_VESTIGE_MALPHAS = 9145; +const int FEAT_FAVORED_VESTIGE_SAVNOK = 9146; +const int FEAT_FAVORED_VESTIGE_ANDROMALIUS = 9147; +const int FEAT_FAVORED_VESTIGE_FOCALOR = 9148; +const int FEAT_FAVORED_VESTIGE_KARSUS = 9149; +const int FEAT_FAVORED_VESTIGE_PAIMON = 9150; +const int FEAT_FAVORED_VESTIGE_AGARES = 9151; +const int FEAT_FAVORED_VESTIGE_ANDRAS = 9152; +const int FEAT_FAVORED_VESTIGE_BUER = 9153; +const int FEAT_FAVORED_VESTIGE_EURYNOME = 9154; +const int FEAT_FAVORED_VESTIGE_TENEBROUS = 9155; +const int FEAT_FAVORED_VESTIGE_ARETE = 9156; +const int FEAT_FAVORED_VESTIGE_ASTAROTH = 9157; +const int FEAT_FAVORED_VESTIGE_ACERERAK = 9158; +const int FEAT_FAVORED_VESTIGE_BALAM = 9159; +const int FEAT_FAVORED_VESTIGE_DANTALION = 9160; +const int FEAT_FAVORED_VESTIGE_GERYON = 9161; +const int FEAT_FAVORED_VESTIGE_OTIAX = 9162; +const int FEAT_FAVORED_VESTIGE_CHUPOCLOPS = 9163; +const int FEAT_FAVORED_VESTIGE_HAURES = 9164; +const int FEAT_FAVORED_VESTIGE_IPOS = 9165; +const int FEAT_FAVORED_VESTIGE_SHAX = 9166; +const int FEAT_FAVORED_VESTIGE_ZAGAN = 9167; +const int FEAT_FAVORED_VESTIGE_VANUS = 9168; +const int FEAT_FAVORED_VESTIGE_THETRIAD = 9169; +const int FEAT_FAVORED_VESTIGE_DESHARIS = 9170; +const int FEAT_FAVORED_VESTIGE_ZCERYLL = 9171; +const int FEAT_FAVORED_VESTIGE_ELIGOR = 9172; +const int FEAT_FAVORED_VESTIGE_MARCHOSIAS = 9173; +const int FEAT_FAVORED_VESTIGE_ASHARDALON = 9174; +const int FEAT_FAVORED_VESTIGE_HALPHAX = 9175; +const int FEAT_FAVORED_VESTIGE_ORTHOS = 9176; +const int FEAT_FAVORED_VESTIGE_ABYSM = 9177; +const int FEAT_FAVORED_VESTIGE_FOCUS_AMON = 9178; +const int FEAT_FAVORED_VESTIGE_FOCUS_AYM = 9179; +const int FEAT_FAVORED_VESTIGE_FOCUS_LERAJE = 9180; +const int FEAT_FAVORED_VESTIGE_FOCUS_NABERIUS = 9181; +const int FEAT_FAVORED_VESTIGE_FOCUS_RONOVE = 9182; +const int FEAT_FAVORED_VESTIGE_FOCUS_DAHLVERNAR = 9183; +const int FEAT_FAVORED_VESTIGE_FOCUS_HAAGENTI = 9184; +const int FEAT_FAVORED_VESTIGE_FOCUS_MALPHAS = 9185; +const int FEAT_FAVORED_VESTIGE_FOCUS_SAVNOK = 9186; +const int FEAT_FAVORED_VESTIGE_FOCUS_ANDROMALIUS = 9187; +const int FEAT_FAVORED_VESTIGE_FOCUS_FOCALOR = 9188; +const int FEAT_FAVORED_VESTIGE_FOCUS_KARSUS = 9189; +const int FEAT_FAVORED_VESTIGE_FOCUS_PAIMON = 9190; +const int FEAT_FAVORED_VESTIGE_FOCUS_AGARES = 9191; +const int FEAT_FAVORED_VESTIGE_FOCUS_ANDRAS = 9192; +const int FEAT_FAVORED_VESTIGE_FOCUS_BUER = 9193; +const int FEAT_FAVORED_VESTIGE_FOCUS_EURYNOME = 9194; +const int FEAT_FAVORED_VESTIGE_FOCUS_TENEBROUS = 9195; +const int FEAT_FAVORED_VESTIGE_FOCUS_ARETE = 9196; +const int FEAT_FAVORED_VESTIGE_FOCUS_ASTAROTH = 9197; +const int FEAT_FAVORED_VESTIGE_FOCUS_ACERERAK = 9198; +const int FEAT_FAVORED_VESTIGE_FOCUS_BALAM = 9199; +const int FEAT_FAVORED_VESTIGE_FOCUS_DANTALION = 9200; +const int FEAT_FAVORED_VESTIGE_FOCUS_GERYON = 9201; +const int FEAT_FAVORED_VESTIGE_FOCUS_OTIAX = 9202; +const int FEAT_FAVORED_VESTIGE_FOCUS_CHUPOCLOPS = 9203; +const int FEAT_FAVORED_VESTIGE_FOCUS_HAURES = 9204; +const int FEAT_FAVORED_VESTIGE_FOCUS_IPOS = 9205; +const int FEAT_FAVORED_VESTIGE_FOCUS_SHAX = 9206; +const int FEAT_FAVORED_VESTIGE_FOCUS_ZAGAN = 9207; +const int FEAT_FAVORED_VESTIGE_FOCUS_VANUS = 9208; +const int FEAT_FAVORED_VESTIGE_FOCUS_THETRIAD = 9209; +const int FEAT_FAVORED_VESTIGE_FOCUS_DESHARIS = 9210; +const int FEAT_FAVORED_VESTIGE_FOCUS_ZCERYLL = 9211; +const int FEAT_FAVORED_VESTIGE_FOCUS_ELIGOR = 9212; +const int FEAT_FAVORED_VESTIGE_FOCUS_MARCHOSIAS = 9213; +const int FEAT_FAVORED_VESTIGE_FOCUS_ASHARDALON = 9214; +const int FEAT_FAVORED_VESTIGE_FOCUS_HALPHAX = 9215; +const int FEAT_FAVORED_VESTIGE_FOCUS_ORTHOS = 9216; +const int FEAT_FAVORED_VESTIGE_FOCUS_ABYSM = 9217; +const int FEAT_RAPID_RECOVERY_AMON = 9218; +const int FEAT_RAPID_RECOVERY_AYM = 9219; +const int FEAT_RAPID_RECOVERY_LERAJE = 9220; +const int FEAT_RAPID_RECOVERY_NABERIUS = 9221; +const int FEAT_RAPID_RECOVERY_RONOVE = 9222; +const int FEAT_RAPID_RECOVERY_DAHLVERNAR = 9223; +const int FEAT_RAPID_RECOVERY_HAAGENTI = 9224; +const int FEAT_RAPID_RECOVERY_MALPHAS = 9225; +const int FEAT_RAPID_RECOVERY_SAVNOK = 9226; +const int FEAT_RAPID_RECOVERY_ANDROMALIUS = 9227; +const int FEAT_RAPID_RECOVERY_FOCALOR = 9228; +const int FEAT_RAPID_RECOVERY_KARSUS = 9229; +const int FEAT_RAPID_RECOVERY_PAIMON = 9230; +const int FEAT_RAPID_RECOVERY_AGARES = 9231; +const int FEAT_RAPID_RECOVERY_ANDRAS = 9232; +const int FEAT_RAPID_RECOVERY_BUER = 9233; +const int FEAT_RAPID_RECOVERY_EURYNOME = 9234; +const int FEAT_RAPID_RECOVERY_TENEBROUS = 9235; +const int FEAT_RAPID_RECOVERY_ARETE = 9236; +const int FEAT_RAPID_RECOVERY_ASTAROTH = 9237; +const int FEAT_RAPID_RECOVERY_ACERERAK = 9238; +const int FEAT_RAPID_RECOVERY_BALAM = 9239; +const int FEAT_RAPID_RECOVERY_DANTALION = 9240; +const int FEAT_RAPID_RECOVERY_GERYON = 9241; +const int FEAT_RAPID_RECOVERY_OTIAX = 9242; +const int FEAT_RAPID_RECOVERY_CHUPOCLOPS = 9243; +const int FEAT_RAPID_RECOVERY_HAURES = 9244; +const int FEAT_RAPID_RECOVERY_IPOS = 9245; +const int FEAT_RAPID_RECOVERY_SHAX = 9246; +const int FEAT_RAPID_RECOVERY_ZAGAN = 9247; +const int FEAT_RAPID_RECOVERY_VANUS = 9248; +const int FEAT_RAPID_RECOVERY_THETRIAD = 9249; +const int FEAT_RAPID_RECOVERY_DESHARIS = 9250; +const int FEAT_RAPID_RECOVERY_ZCERYLL = 9251; +const int FEAT_RAPID_RECOVERY_ELIGOR = 9252; +const int FEAT_RAPID_RECOVERY_MARCHOSIAS = 9253; +const int FEAT_RAPID_RECOVERY_ASHARDALON = 9254; +const int FEAT_RAPID_RECOVERY_HALPHAX = 9255; +const int FEAT_RAPID_RECOVERY_ORTHOS = 9256; +const int FEAT_RAPID_RECOVERY_ABYSM = 9257; +const int FEAT_IGNORE_SPECIAL_REQUIREMENTS = 9258; +const int FEAT_ANIMA_VESTIGE_METAMAGIC = 9260; +const int FEAT_TENEBROUS_BLAST_VOID = 9265; +const int FEAT_PATRON_VESTIGE_AMON = 9266; +const int FEAT_PATRON_VESTIGE_AYM = 9267; +const int FEAT_PATRON_VESTIGE_LERAJE = 9268; +const int FEAT_PATRON_VESTIGE_NABERIUS = 9269; +const int FEAT_PATRON_VESTIGE_RONOVE = 9270; +const int FEAT_PATRON_VESTIGE_DAHLVERNAR = 9271; +const int FEAT_PATRON_VESTIGE_HAAGENTI = 9272; +const int FEAT_PATRON_VESTIGE_MALPHAS = 9273; +const int FEAT_PATRON_VESTIGE_SAVNOK = 9274; +const int FEAT_PATRON_VESTIGE_ANDROMALIUS = 9275; +const int FEAT_PATRON_VESTIGE_FOCALOR = 9276; +const int FEAT_PATRON_VESTIGE_KARSUS = 9277; +const int FEAT_PATRON_VESTIGE_PAIMON = 9278; +const int FEAT_PATRON_VESTIGE_AGARES = 9279; +const int FEAT_PATRON_VESTIGE_ANDRAS = 9280; +const int FEAT_PATRON_VESTIGE_BUER = 9281; +const int FEAT_PATRON_VESTIGE_EURYNOME = 9282; +const int FEAT_PATRON_VESTIGE_TENEBROUS = 9283; +const int FEAT_PATRON_VESTIGE_ARETE = 9284; +const int FEAT_PATRON_VESTIGE_ASTAROTH = 9285; +const int FEAT_PATRON_VESTIGE_ACERERAK = 9286; +const int FEAT_PATRON_VESTIGE_BALAM = 9287; +const int FEAT_PATRON_VESTIGE_DANTALION = 9288; +const int FEAT_PATRON_VESTIGE_GERYON = 9289; +const int FEAT_PATRON_VESTIGE_OTIAX = 9290; +const int FEAT_PATRON_VESTIGE_CHUPOCLOPS = 9291; +const int FEAT_PATRON_VESTIGE_HAURES = 9292; +const int FEAT_PATRON_VESTIGE_IPOS = 9293; +const int FEAT_PATRON_VESTIGE_SHAX = 9294; +const int FEAT_PATRON_VESTIGE_ZAGAN = 9295; +const int FEAT_PATRON_VESTIGE_VANUS = 9296; +const int FEAT_PATRON_VESTIGE_THETRIAD = 9297; +const int FEAT_PATRON_VESTIGE_DESHARIS = 9298; +const int FEAT_PATRON_VESTIGE_ZCERYLL = 9299; +const int FEAT_PATRON_VESTIGE_ELIGOR = 9300; +const int FEAT_PATRON_VESTIGE_MARCHOSIAS = 9301; +const int FEAT_PATRON_VESTIGE_ASHARDALON = 9302; +const int FEAT_PATRON_VESTIGE_HALPHAX = 9303; +const int FEAT_PATRON_VESTIGE_ORTHOS = 9304; +const int FEAT_PATRON_VESTIGE_ABYSM = 9305; +const int FEAT_SCION_DANTALION_SCHOLARSHIP = 9309; + +/*////////////////////////////////////////////////// +/////////////////// End Binding //////////////////// +//////////////////////////////////////////////////*/ + +//:: Reserve Feats +const int FEAT_HOLY_WARRIOR = 24736; +const int FEAT_MYSTIC_BACKLASH = 24737; +const int FEAT_ACIDIC_SPLATTER = 24738; +const int FEAT_FIERY_BURST = 24739; +const int FEAT_STORM_BOLT = 24740; +const int FEAT_WINTERS_BLAST = 24741; +const int FEAT_CLAP_OF_THUNDER = 24742; +const int FEAT_SICKENING_GRASP = 24743; +const int FEAT_TOUCH_OF_HEALING = 24744; +const int FEAT_DIMENSIONAL_JAUNT = 24745; +const int FEAT_CLUTCH_OF_EARTH = 24746; +const int FEAT_BORNE_ALOFT = 24747; +const int FEAT_PROTECTIVE_WARD = 24748; +const int FEAT_SHADOW_VEIL = 24749; +const int FEAT_SUNLIGHT_EYES = 24750; +const int FEAT_TOUCH_OF_DISTRACTION = 24751; +const int FEAT_UMBRAL_SHROUD = 24752; +const int FEAT_CHARNEL_MIASMA = 24753; +const int FEAT_DROWNING_GLANCE = 24754; +const int FEAT_INVISIBLE_NEEDLE = 24755; +const int FEAT_SUMMON_ELEMENTAL = 24756; +const int FEAT_DIMENSIONAL_REACH = 24757; +const int FEAT_HURRICANE_BREATH = 24758; +const int FEAT_MINOR_SHAPESHIFT = 24759; +const int FEAT_FACECHANGER = 24760; + +//:: Shadow Conjuration +const int FEAT_SHADOW_CONJURATION_CONVERSATION = 24732; +const int PNP_SHADOW_CONJURATION = 24733; +const int PNP_GREATER_SHADOW_CONJURATION = 24734; +const int PNP_SHADES = 24735; + +//:: Epic Level Handbook +const int FEAT_EPIC_SWARM_OF_ARROWS = 25995; + +//:: Warlock invoking marker feats +const int FEAT_ABCHAMP_INVOKING_WARLOCK = 18000; +const int FEAT_AOTS_INVOKING_WARLOCK = 18001; +const int FEAT_ANIMA_INVOKING_WARLOCK = 18002; +const int FEAT_ARCTRICK_INVOKING_WARLOCK = 18003; +const int FEAT_BLDMAGUS_INVOKING_WARLOCK = 18004; +const int FEAT_ASMODEUS_INVOKING_WARLOCK = 18005; +const int FEAT_ELDISCIPLE_INVOKING_WARLOCK = 18006; +const int FEAT_ETHEURGE_INVOKING_WARLOCK = 18007; +const int FEAT_ENLIGHTENEDFIST_INVOKING_WARLOCK = 18008; +const int FEAT_HELLFIRE_INVOKING_WARLOCK = 18009; +const int FEAT_MAESTER_INVOKING_WARLOCK = 18010; +const int FEAT_TIAMAT_INVOKING_WARLOCK = 18011; +const int FEAT_UNSEEN_INVOKING_WARLOCK = 18012; +const int FEAT_VIRTUOSO_INVOKING_WARLOCK = 18013; +const int FEAT_WILDMAGE_INVOKING_WARLOCK = 18014; + +//:: Dragonfire Adept invoking marker feats +const int FEAT_ABCHAMP_INVOKING_DFA = 18015; +const int FEAT_AOTS_INVOKING_DFA = 18016; +const int FEAT_ANIMA_INVOKING_DFA = 18017; +const int FEAT_ARCTRICK_INVOKING_DFA = 18018; +const int FEAT_BLDMAGUS_INVOKING_DFA = 18019; +const int FEAT_ASMODEUS_INVOKING_DFA = 18020; +const int FEAT_ELDISCIPLE_INVOKING_DFA = 18021; +const int FEAT_ENLIGHTENEDFIST_INVOKING_DFA = 18022; +const int FEAT_MAESTER_INVOKING_DFA = 18023; +const int FEAT_TIAMAT_INVOKING_DFA = 18024; +const int FEAT_UNSEEN_INVOKING_DFA = 18025; +const int FEAT_VIRTUOSO_INVOKING_DFA = 18026; +const int FEAT_WILDMAGE_INVOKING_DFA = 18027; + +//:: Dragon Shaman invoking marker feats +const int FEAT_ABCHAMP_INVOKING_DRAGON_SHAMAN = 18028; +const int FEAT_ASMODEUS_INVOKING_DRAGON_SHAMAN = 18029; +const int FEAT_ELDISCIPLE_INVOKING_DRAGON_SHAMAN = 18030; +const int FEAT_ENLIGHTENEDFIST_INVOKING_DRAGON_SHAMAN = 18031; +const int FEAT_TIAMAT_INVOKING_DRAGON_SHAMAN = 18032; +const int FEAT_UNSEEN_INVOKING_DRAGON_SHAMAN = 18033; +const int FEAT_VIRTUOSO_INVOKING_DRAGON_SHAMAN = 18034; +const int FEAT_WILDMAGE_INVOKING_DRAGON_SHAMAN = 18035; + +//:: Shadowcaster marker feats +const int FEAT_AOTS_MYSTERY_SHADOWCASTER = 18070; +const int FEAT_ALIENIST_MYSTERY_SHADOWCASTER = 18071; +const int FEAT_CHILDNIGHT_MYSTERY_SHADOWCASTER = 18072; +const int FEAT_ASMODEUS_MYSTERY_SHADOWCASTER = 18073; +const int FEAT_DSONG_MYSTERY_SHADOWCASTER = 18074; +const int FEAT_ELESAVANT_MYSTERY_SHADOWCASTER = 18075; +const int FEAT_MASTERSHADOW_MYSTERY_SHADOWCASTER = 18076; +const int FEAT_MYSTICTHEURGE_MYSTERY_SHADOWCASTER = 18077; +const int FEAT_NOCTUMANCER_MYSTERY_SHADOWCASTER = 18078; +const int FEAT_OLLAM_MYSTERY_SHADOWCASTER = 18079; +const int FEAT_TIAMAT_MYSTERY_SHADOWCASTER = 18080; +const int FEAT_ORCUS_MYSTERY_SHADOWCASTER = 18081; + +//:: Shadowsmith marker feats +const int FEAT_AOTS_MYSTERY_SHADOWSMITH = 18082; +const int FEAT_ALIENIST_MYSTERY_SHADOWSMITH = 18083; +const int FEAT_CHILDNIGHT_MYSTERY_SHADOWSMITH = 18084; +const int FEAT_ASMODEUS_MYSTERY_SHADOWSMITH = 18085; +const int FEAT_DSONG_MYSTERY_SHADOWSMITH = 18086; +const int FEAT_ELESAVANT_MYSTERY_SHADOWSMITH = 18087; +const int FEAT_MASTERSHADOW_MYSTERY_SHADOWSMITH = 18088; +const int FEAT_MYSTICTHEURGE_MYSTERY_SHADOWSMITH = 18089; +const int FEAT_NOCTUMANCER_MYSTERY_SHADOWSMITH = 18090; +const int FEAT_OLLAM_MYSTERY_SHADOWSMITH = 18091; +const int FEAT_TIAMAT_MYSTERY_SHADOWSMITH = 18092; +const int FEAT_ORCUS_MYSTERY_SHADOWSMITH = 18093; + +//:: None marker feats +const int FEAT_ABCHAMP_NONE = 18036; +const int FEAT_AOTS_NONE = 18037; +const int FEAT_ANIMA_NONE = 18038; +const int FEAT_ARCTRICK_NONE = 18039; +const int FEAT_BLDMAGUS_NONE = 18040; +const int FEAT_ASMODEUS_NONE = 18041; +const int FEAT_ENLIGHTENEDFIST_NONE = 18042; +const int FEAT_MAESTER_NONE = 18043; +const int FEAT_TIAMAT_NONE = 18044; +const int FEAT_UNSEEN_NONE = 18045; +const int FEAT_VIRTUOSO_NONE = 18046; +const int FEAT_WILDMAGE_NONE = 18047; + +const int FEAT_ALIENIST_NONE = 18094; +const int FEAT_DRAGONSONG_NONE = 18095; +const int FEAT_ESAVANT_NONE = 18096; +const int FEAT_MYSTICTHEURGE_NONE = 18097; +const int FEAT_OLLAM_NONE = 18098; +const int FEAT_ORCUS_NONE = 18099; + +const int FEAT_SANCTIFIED_MIND_MANIFEST_NONE = 18173; +const int FEAT_SOULCASTER_MANIFEST_NONE = 18174; + + +//:: Fist of Zuoken marker feats +const int FEAT_CEREBREMANCER_MANIFEST_FOZ = 18130; +const int FEAT_DIAMOND_DRAGON_MANIFEST_FOZ = 18131; +const int FEAT_IRONMIND_MANIFEST_FOZ = 18132; +const int FEAT_PSYCHIC_THEURGE_MANIFEST_FOZ = 18133; +const int FEAT_SANCTIFIED_MIND_MANIFEST_FOZ = 18134; +const int FEAT_SHADOWMIND_MANIFEST_FOZ = 18135; +const int FEAT_SOULCASTER_MANIFEST_FOZ = 18136; + +//:: Psion marker feats +const int FEAT_CEREBREMANCER_MANIFEST_PSION = 18137; +const int FEAT_DIAMOND_DRAGON_MANIFEST_PSION = 18138; +const int FEAT_IRONMIND_MANIFEST_PSION = 18139; +const int FEAT_PSYCHIC_THEURGE_MANIFEST_PSION = 18140; +const int FEAT_SANCTIFIED_MIND_MANIFEST_PSION = 18141; +const int FEAT_SHADOWMIND_MANIFEST_PSION = 18142; +const int FEAT_SOULCASTER_MANIFEST_PSION = 18143; +const int FEAT_THRALLHERD_MANIFEST_PSION = 18144; + +//:: Psychic Rogue marker feats +const int FEAT_CEREBREMANCER_MANIFEST_PSYROUGE = 18145; +const int FEAT_DIAMOND_DRAGON_MANIFEST_PSYROUGE = 18146; +const int FEAT_IRONMIND_MANIFEST_PSYROUGE = 18147; +const int FEAT_PSYCHIC_THEURGE_MANIFEST_PSYROUGE = 18148; +const int FEAT_SANCTIFIED_MIND_MANIFEST_PSYROUGE = 18149; +const int FEAT_SHADOWMIND_MANIFEST_PSYROUGE = 18150; +const int FEAT_SOULCASTER_MANIFEST_PSYROUGE = 18151; + +//:: Psychic Warrior marker feats +const int FEAT_CEREBREMANCER_MANIFEST_PSYWAR = 18152; +const int FEAT_DIAMOND_DRAGON_MANIFEST_PSYWAR = 18153; +const int FEAT_IRONMIND_MANIFEST_PSYWAR = 18154; +const int FEAT_PSYCHIC_THEURGE_MANIFEST_PSYWAR = 18155; +const int FEAT_SANCTIFIED_MIND_MANIFEST_PSYWAR = 18156; +const int FEAT_SHADOWMIND_MANIFEST_PSYWAR = 18157; +const int FEAT_SOULCASTER_MANIFEST_PSYWAR = 18158; + +//:: Warmind marker feats +const int FEAT_CEREBREMANCER_MANIFEST_WARMIND = 18159; +const int FEAT_DIAMOND_DRAGON_MANIFEST_WARMIND = 18160; +const int FEAT_IRONMIND_MANIFEST_WARMIND = 18161; +const int FEAT_PSYCHIC_THEURGE_MANIFEST_WARMIND = 18162; +const int FEAT_SANCTIFIED_MIND_MANIFEST_WARMIND = 18163; +const int FEAT_SHADOWMIND_MANIFEST_WARMIND = 18164; +const int FEAT_SOULCASTER_MANIFEST_WARMIND = 18165; + +//:: Wilder marker feats +const int FEAT_CEREBREMANCER_MANIFEST_WILDER = 18166; +const int FEAT_DIAMOND_DRAGON_MANIFEST_WILDER = 18167; +const int FEAT_IRONMIND_MANIFEST_WILDER = 18168; +const int FEAT_PSYCHIC_THEURGE_MANIFEST_WILDER = 18169; +const int FEAT_SANCTIFIED_MIND_MANIFEST_WILDER = 18170; +const int FEAT_SHADOWMIND_MANIFEST_WILDER = 18171; +const int FEAT_SOULCASTER_MANIFEST_WILDER = 18172; + +//:: Racial caster marker feats +const int FEAT_MONSTROUS_SPELLCASTING_WARSOUL = 18199; +const int FEAT_FEY_SPELLCASTING_GLOURA = 18200; +const int FEAT_ABERRATION_SPELLCASTING_DRIDER = 18201; +const int FEAT_MONSTROUS_SPELLCASTING_ARKAMOI = 18202; +const int FEAT_MONSTROUS_SPELLCASTING_MARRUTACT = 18203; +const int FEAT_MONSTROUS_SPELLCASTING_REDSPAWN_ARCANISS = 18204; +const int FEAT_OUTSIDER_SPELLCASTING_RAKSHASA = 18205; +const int FEAT_SHAPECHANGER_SPELLCASTING_ARANEA = 18206; + +//:: Fey RHD spellcasting marker feats +const int FEAT_ABCHAMP_SPELLCASTING_FEY = 18207; +const int FEAT_AOTS_SPELLCASTING_FEY = 18208; +const int FEAT_ALCHEM_SPELLCASTING_FEY = 18209; +const int FEAT_ALIENIST_SPELLCASTING_FEY = 18210; +const int FEAT_ANIMA_SPELLCASTING_FEY = 18211; +const int FEAT_ARCHMAGE_SPELLCASTING_FEY = 18212; +const int FEAT_ARCTRICK_SPELLCASTING_FEY = 18213; +const int FEAT_ASMODEUS_SPELLCASTING_FEY = 18214; +const int FEAT_BSINGER_SPELLCASTING_FEY = 18215; +const int FEAT_BLDMAGUS_SPELLCASTING_FEY = 18216; +const int FEAT_CMANCER_SPELLCASTING_FEY = 18217; +const int FEAT_DIABOLIST_SPELLCASTING_FEY = 18218; +const int FEAT_DHEART_SPELLCASTING_FEY = 18219; +const int FEAT_DSONG_SPELLCASTING_FEY = 18220; +const int FEAT_EKNIGHT_SPELLCASTING_FEY = 18221; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_FEY = 18222; +const int FEAT_ELESAVANT_SPELLCASTING_FEY = 18223; +const int FEAT_ETHEURGE_SPELLCASTING_FEY = 18224; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_FEY = 18225; +const int FEAT_FROSTMAGE_SPELLCASTING_FEY = 18226; +const int FEAT_GRAZZT_SPELLCASTING_FEY = 18227; +const int FEAT_HARPERM_SPELLCASTING_FEY = 18228; +const int FEAT_HATHRAN_SPELLCASTING_FEY = 18229; +const int FEAT_HAVOC_SPELLCASTING_FEY = 18230; +const int FEAT_JPM_SPELLCASTING_FEY = 18231; +const int FEAT_JUDICATOR_SPELLCASTING_FEY = 18232; +const int FEAT_MAESTER_SPELLCASTING_FEY = 18233; +const int FEAT_MAGEKILLER_SPELLCASTING_FEY = 18234; +const int FEAT_MHARPER_SPELLCASTING_FEY = 18235; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_FEY = 18236; +const int FEAT_NOCTUMANCER_SPELLCASTING_FEY = 18237; +const int FEAT_OOZEMASTER_SPELLCASTING_FEY = 18238; +const int FEAT_PALEMASTER_SPELLCASTING_FEY = 18239; +const int FEAT_RAGEMAGE_SPELLCASTING_FEY = 18240; +const int FEAT_SHADOWADEPT_SPELLCASTING_FEY = 18241; +const int FEAT_SOULCASTER_SPELLCASTING_FEY = 18242; +const int FEAT_SPELLDANCER_SPELLCASTING_FEY = 18243; +const int FEAT_SSWORD_SPELLCASTING_FEY = 18244; +const int FEAT_TIAMAT_SPELLCASTING_FEY = 18245; +const int FEAT_TNECRO_SPELLCASTING_FEY = 18246; +const int FEAT_ULTMAGUS_SPELLCASTING_FEY = 18247; +const int FEAT_UNSEEN_SPELLCASTING_FEY = 18248; +const int FEAT_VIRTUOSO_SPELLCASTING_FEY = 18249; +const int FEAT_WILDMAGE_SPELLCASTING_FEY = 18250; +const int FEAT_WWOC_SPELLCASTING_FEY = 18251; + +//:: Aberration RHD spellcasting marker feats +const int FEAT_ABCHAMP_SPELLCASTING_ABERRATION = 18252; +const int FEAT_AOTS_SPELLCASTING_ABERRATION = 18253; +const int FEAT_ALCHEM_SPELLCASTING_ABERRATION = 18254; +const int FEAT_ALIENIST_SPELLCASTING_ABERRATION = 18255; +const int FEAT_ANIMA_SPELLCASTING_ABERRATION = 18256; +const int FEAT_ARCHMAGE_SPELLCASTING_ABERRATION = 18257; +const int FEAT_ARCTRICK_SPELLCASTING_ABERRATION = 18258; +const int FEAT_ASMODEUS_SPELLCASTING_ABERRATION = 18259; +const int FEAT_BSINGER_SPELLCASTING_ABERRATION = 18260; +const int FEAT_BLDMAGUS_SPELLCASTING_ABERRATION = 18261; +const int FEAT_CMANCER_SPELLCASTING_ABERRATION = 18262; +const int FEAT_DIABOLIST_SPELLCASTING_ABERRATION = 18263; +const int FEAT_DHEART_SPELLCASTING_ABERRATION = 18264; +const int FEAT_DSONG_SPELLCASTING_ABERRATION = 18265; +const int FEAT_EKNIGHT_SPELLCASTING_ABERRATION = 18266; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_ABERRATION = 18267; +const int FEAT_ELESAVANT_SPELLCASTING_ABERRATION = 18268; +const int FEAT_ETHEURGE_SPELLCASTING_ABERRATION = 18269; +const int FEAT_FMM_SPELLCASTING_ABERRATION = 18270; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_ABERRATION = 18271; +const int FEAT_FROSTMAGE_SPELLCASTING_ABERRATION = 18272; +const int FEAT_GRAZZT_SPELLCASTING_ABERRATION = 18273; +const int FEAT_HARPERM_SPELLCASTING_ABERRATION = 18274; +const int FEAT_HATHRAN_SPELLCASTING_ABERRATION = 18275; +const int FEAT_HAVOC_SPELLCASTING_ABERRATION = 18276; +const int FEAT_JPM_SPELLCASTING_ABERRATION = 18277; +const int FEAT_JUDICATOR_SPELLCASTING_ABERRATION = 18278; +const int FEAT_MAESTER_SPELLCASTING_ABERRATION = 18279; +const int FEAT_MAGEKILLER_SPELLCASTING_ABERRATION = 18280; +const int FEAT_MHARPER_SPELLCASTING_ABERRATION = 18281; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_ABERRATION = 18282; +const int FEAT_NOCTUMANCER_SPELLCASTING_ABERRATION = 18283; +const int FEAT_OOZEMASTER_SPELLCASTING_ABERRATION = 18284; +const int FEAT_PALEMASTER_SPELLCASTING_ABERRATION = 18285; +const int FEAT_RAGEMAGE_SPELLCASTING_ABERRATION = 18286; +const int FEAT_SHADOWADEPT_SPELLCASTING_ABERRATION = 18287; +const int FEAT_SOULCASTER_SPELLCASTING_ABERRATION = 18288; +const int FEAT_SPELLDANCER_SPELLCASTING_ABERRATION = 18289; +const int FEAT_SSWORD_SPELLCASTING_ABERRATION = 18290; +const int FEAT_TIAMAT_SPELLCASTING_ABERRATION = 18291; +const int FEAT_TNECRO_SPELLCASTING_ABERRATION = 18292; +const int FEAT_ULTMAGUS_SPELLCASTING_ABERRATION = 18293; +const int FEAT_UNSEEN_SPELLCASTING_ABERRATION = 18294; +const int FEAT_VIRTUOSO_SPELLCASTING_ABERRATION = 18295; +const int FEAT_WAYFARER_SPELLCASTING_ABERRATION = 18296; +const int FEAT_WILDMAGE_SPELLCASTING_ABERRATION = 18297; +const int FEAT_WWOC_SPELLCASTING_ABERRATION = 18298; + +//:: Monstrous RHD spellcasting marker feats +const int FEAT_ABCHAMP_SPELLCASTING_MONSTROUS = 18299; +const int FEAT_AOTS_SPELLCASTING_MONSTROUS = 18300; +const int FEAT_ALCHEM_SPELLCASTING_MONSTROUS = 18301; +const int FEAT_ALIENIST_SPELLCASTING_MONSTROUS = 18302; +const int FEAT_ANIMA_SPELLCASTING_MONSTROUS = 18303; +const int FEAT_ARCHMAGE_SPELLCASTING_MONSTROUS = 18304; +const int FEAT_ARCTRICK_SPELLCASTING_MONSTROUS = 18305; +const int FEAT_ASMODEUS_SPELLCASTING_MONSTROUS = 18306; +const int FEAT_BSINGER_SPELLCASTING_MONSTROUS = 18307; +const int FEAT_BLDMAGUS_SPELLCASTING_MONSTROUS = 18308; +const int FEAT_CMANCER_SPELLCASTING_MONSTROUS = 18309; +const int FEAT_DHEART_SPELLCASTING_MONSTROUS = 18310; +const int FEAT_DIABOLIST_SPELLCASTING_MONSTROUS = 18311; +const int FEAT_DSONG_SPELLCASTING_MONSTROUS = 18312; +const int FEAT_EKNIGHT_SPELLCASTING_MONSTROUS = 18313; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_MONSTROUS = 18314; +const int FEAT_ELESAVANT_SPELLCASTING_MONSTROUS = 18315; +const int FEAT_ETHEURGE_SPELLCASTING_MONSTROUS = 18316; +const int FEAT_FMM_SPELLCASTING_MONSTROUS = 18317; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_MONSTROUS = 18318; +const int FEAT_FROSTMAGE_SPELLCASTING_MONSTROUS = 18319; +const int FEAT_GRAZZT_SPELLCASTING_MONSTROUS = 18320; +const int FEAT_HARPERM_SPELLCASTING_MONSTROUS = 18321; +const int FEAT_HATHRAN_SPELLCASTING_MONSTROUS = 18322; +const int FEAT_HAVOC_SPELLCASTING_MONSTROUS = 18323; +const int FEAT_JPM_SPELLCASTING_MONSTROUS = 18324; +const int FEAT_JUDICATOR_SPELLCASTING_MONSTROUS = 18325; +const int FEAT_MAESTER_SPELLCASTING_MONSTROUS = 18326; +const int FEAT_MAGEKILLER_SPELLCASTING_MONSTROUS = 18327; +const int FEAT_MHARPER_SPELLCASTING_MONSTROUS = 18328; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_MONSTROUS = 18329; +const int FEAT_NOCTUMANCER_SPELLCASTING_MONSTROUS = 18330; +const int FEAT_OOZEMASTER_SPELLCASTING_MONSTROUS = 18331; +const int FEAT_PALEMASTER_SPELLCASTING_MONSTROUS = 18332; +const int FEAT_RAGEMAGE_SPELLCASTING_MONSTROUS = 18333; +const int FEAT_SHADOWADEPT_SPELLCASTING_MONSTROUS = 18334; +const int FEAT_SOULCASTER_SPELLCASTING_MONSTROUS = 18335; +const int FEAT_SPELLDANCER_SPELLCASTING_MONSTROUS = 18336; +const int FEAT_SSWORD_SPELLCASTING_MONSTROUS = 18337; +const int FEAT_TIAMAT_SPELLCASTING_MONSTROUS = 18338; +const int FEAT_TNECRO_SPELLCASTING_MONSTROUS = 18339; +const int FEAT_ULTMAGUS_SPELLCASTING_MONSTROUS = 18340; +const int FEAT_UNSEEN_SPELLCASTING_MONSTROUS = 18341; +const int FEAT_VIRTUOSO_SPELLCASTING_MONSTROUS = 18342; +const int FEAT_WAYFARER_SPELLCASTING_MONSTROUS = 18343; +const int FEAT_WILDMAGE_SPELLCASTING_MONSTROUS = 18344; +const int FEAT_WWOC_SPELLCASTING_MONSTROUS = 18345; + +//:: Outsider RHD spellcasting marker feats +const int FEAT_ABCHAMP_SPELLCASTING_OUTSIDER = 18346; +const int FEAT_AOTS_SPELLCASTING_OUTSIDER = 18347; +const int FEAT_ALCHEM_SPELLCASTING_OUTSIDER = 18348; +const int FEAT_ALIENIST_SPELLCASTING_OUTSIDER = 18349; +const int FEAT_ANIMA_SPELLCASTING_OUTSIDER = 18350; +const int FEAT_ARCHMAGE_SPELLCASTING_OUTSIDER = 18351; +const int FEAT_ARCTRICK_SPELLCASTING_OUTSIDER = 18352; +const int FEAT_ASMODEUS_SPELLCASTING_OUTSIDER = 18353; +const int FEAT_BSINGER_SPELLCASTING_OUTSIDER = 18354; +const int FEAT_BLDMAGUS_SPELLCASTING_OUTSIDER = 18355; +const int FEAT_CMANCER_SPELLCASTING_OUTSIDER = 18356; +const int FEAT_DHEART_SPELLCASTING_OUTSIDER = 18357; +const int FEAT_DIABOLIST_SPELLCASTING_OUTSIDER = 18358; +const int FEAT_DSONG_SPELLCASTING_OUTSIDER = 18359; +const int FEAT_EKNIGHT_SPELLCASTING_OUTSIDER = 18360; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_OUTSIDER = 18361; +const int FEAT_ELESAVANT_SPELLCASTING_OUTSIDER = 18362; +const int FEAT_ETHEURGE_SPELLCASTING_OUTSIDER = 18363; +const int FEAT_FMM_SPELLCASTING_OUTSIDER = 18364; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_OUTSIDER = 18365; +const int FEAT_FROSTMAGE_SPELLCASTING_OUTSIDER = 18366; +const int FEAT_GRAZZT_SPELLCASTING_OUTSIDER = 18367; +const int FEAT_HARPERM_SPELLCASTING_OUTSIDER = 18368; +const int FEAT_HATHRAN_SPELLCASTING_OUTSIDER = 18369; +const int FEAT_HAVOC_SPELLCASTING_OUTSIDER = 18370; +const int FEAT_JPM_SPELLCASTING_OUTSIDER = 18371; +const int FEAT_JUDICATOR_SPELLCASTING_OUTSIDER = 18372; +const int FEAT_MAESTER_SPELLCASTING_OUTSIDER = 18373; +const int FEAT_MAGEKILLER_SPELLCASTING_OUTSIDER = 18374; +const int FEAT_MHARPER_SPELLCASTING_OUTSIDER = 18375; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_OUTSIDER = 18376; +const int FEAT_NOCTUMANCER_SPELLCASTING_OUTSIDER = 18377; +const int FEAT_OOZEMASTER_SPELLCASTING_OUTSIDER = 18378; +const int FEAT_PALEMASTER_SPELLCASTING_OUTSIDER = 18379; +const int FEAT_RAGEMAGE_SPELLCASTING_OUTSIDER = 18380; +const int FEAT_SHADOWADEPT_SPELLCASTING_OUTSIDER = 18381; +const int FEAT_SOULCASTER_SPELLCASTING_OUTSIDER = 18382; +const int FEAT_SPELLDANCER_SPELLCASTING_OUTSIDER = 18383; +const int FEAT_SSWORD_SPELLCASTING_OUTSIDER = 18384; +const int FEAT_TIAMAT_SPELLCASTING_OUTSIDER = 18385; +const int FEAT_TNECRO_SPELLCASTING_OUTSIDER = 18386; +const int FEAT_ULTMAGUS_SPELLCASTING_OUTSIDER = 18387; +const int FEAT_UNSEEN_SPELLCASTING_OUTSIDER = 18388; +const int FEAT_VIRTUOSO_SPELLCASTING_OUTSIDER = 18389; +const int FEAT_WAYFARER_SPELLCASTING_OUTSIDER = 18390; +const int FEAT_WILDMAGE_SPELLCASTING_OUTSIDER = 18391; +const int FEAT_WWOC_SPELLCASTING_OUTSIDER = 18392; + +//:: Shapechanger RHD spellcasting marker feats +const int FEAT_ABCHAMP_SPELLCASTING_SHAPECHANGER = 18393; +const int FEAT_AOTS_SPELLCASTING_SHAPECHANGER = 18394; +const int FEAT_ALCHEM_SPELLCASTING_SHAPECHANGER = 18395; +const int FEAT_ALIENIST_SPELLCASTING_SHAPECHANGER = 18396; +const int FEAT_ANIMA_SPELLCASTING_SHAPECHANGER = 18397; +const int FEAT_ARCHMAGE_SPELLCASTING_SHAPECHANGER = 18398; +const int FEAT_ARCTRICK_SPELLCASTING_SHAPECHANGER = 18399; +const int FEAT_ASMODEUS_SPELLCASTING_SHAPECHANGER = 18400; +const int FEAT_BSINGER_SPELLCASTING_SHAPECHANGER = 18401; +const int FEAT_BLDMAGUS_SPELLCASTING_SHAPECHANGER = 18402; +const int FEAT_CMANCER_SPELLCASTING_SHAPECHANGER = 18403; +const int FEAT_DHEART_SPELLCASTING_SHAPECHANGER = 18404; +const int FEAT_DIABOLIST_SPELLCASTING_SHAPECHANGER = 18405; +const int FEAT_DSONG_SPELLCASTING_SHAPECHANGER = 18406; +const int FEAT_EKNIGHT_SPELLCASTING_SHAPECHANGER = 18407; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_SHAPECHANGER = 18408; +const int FEAT_ELESAVANT_SPELLCASTING_SHAPECHANGER = 18409; +const int FEAT_ETHEURGE_SPELLCASTING_SHAPECHANGER = 18410; +const int FEAT_FMM_SPELLCASTING_SHAPECHANGER = 18411; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SHAPECHANGER = 18412; +const int FEAT_FROSTMAGE_SPELLCASTING_SHAPECHANGER = 18413; +const int FEAT_GRAZZT_SPELLCASTING_SHAPECHANGER = 18414; +const int FEAT_HARPERM_SPELLCASTING_SHAPECHANGER = 18415; +const int FEAT_HATHRAN_SPELLCASTING_SHAPECHANGER = 18416; +const int FEAT_HAVOC_SPELLCASTING_SHAPECHANGER = 18417; +const int FEAT_JPM_SPELLCASTING_SHAPECHANGER = 18418; +const int FEAT_JUDICATOR_SPELLCASTING_SHAPECHANGER = 18419; +const int FEAT_MAESTER_SPELLCASTING_SHAPECHANGER = 18420; +const int FEAT_MAGEKILLER_SPELLCASTING_SHAPECHANGER = 18421; +const int FEAT_MHARPER_SPELLCASTING_SHAPECHANGER = 18422; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SHAPECHANGER = 18423; +const int FEAT_NOCTUMANCER_SPELLCASTING_SHAPECHANGER = 18424; +const int FEAT_OOZEMASTER_SPELLCASTING_SHAPECHANGER = 18425; +const int FEAT_PALEMASTER_SPELLCASTING_SHAPECHANGER = 18426; +const int FEAT_RAGEMAGE_SPELLCASTING_SHAPECHANGER = 18427; +const int FEAT_SHADOWADEPT_SPELLCASTING_SHAPECHANGER = 18428; +const int FEAT_SOULCASTER_SPELLCASTING_SHAPECHANGER = 18429; +const int FEAT_SPELLDANCER_SPELLCASTING_SHAPECHANGER = 18430; +const int FEAT_SSWORD_SPELLCASTING_SHAPECHANGER = 18431; +const int FEAT_TIAMAT_SPELLCASTING_SHAPECHANGER = 18432; +const int FEAT_TNECRO_SPELLCASTING_SHAPECHANGER = 18433; +const int FEAT_ULTMAGUS_SPELLCASTING_SHAPECHANGER = 18434; +const int FEAT_UNSEEN_SPELLCASTING_SHAPECHANGER = 18435; +const int FEAT_VIRTUOSO_SPELLCASTING_SHAPECHANGER = 18436; +const int FEAT_WAYFARER_SPELLCASTING_SHAPECHANGER = 18437; +const int FEAT_WILDMAGE_SPELLCASTING_SHAPECHANGER = 18438; +const int FEAT_WWOC_SPELLCASTING_SHAPECHANGER = 18439; + +//:: Assassin marker feats +const int FEAT_ABCHAMP_SPELLCASTING_ASSASSIN = 18440; +const int FEAT_AOTS_SPELLCASTING_ASSASSIN = 18441; +const int FEAT_ALCHEM_SPELLCASTING_ASSASSIN = 18442; +const int FEAT_ANIMA_SPELLCASTING_ASSASSIN = 18443; +const int FEAT_ARCTRICK_SPELLCASTING_ASSASSIN = 18444; +const int FEAT_ASMODEUS_SPELLCASTING_ASSASSIN = 18445; +const int FEAT_BSINGER_SPELLCASTING_ASSASSIN = 18446; +const int FEAT_CMANCER_SPELLCASTING_ASSASSIN = 18447; +const int FEAT_DIABOLIST_SPELLCASTING_ASSASSIN = 18448; +const int FEAT_DHEART_SPELLCASTING_ASSASSIN = 18449; +const int FEAT_EKNIGHT_SPELLCASTING_ASSASSIN = 18450; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_ASSASSIN = 18451; +const int FEAT_ELESAVANT_SPELLCASTING_ASSASSIN = 18452; +const int FEAT_ETHEURGE_SPELLCASTING_ASSASSIN = 18453; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_ASSASSIN = 18454; +const int FEAT_FROSTMAGE_SPELLCASTING_ASSASSIN = 18455; +const int FEAT_GRAZZT_SPELLCASTING_ASSASSIN = 18456; +const int FEAT_HAVOC_SPELLCASTING_ASSASSIN = 18457; +const int FEAT_JUDICATOR_SPELLCASTING_ASSASSIN = 18458; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_ASSASSIN = 18459; +const int FEAT_NOCTUMANCER_SPELLCASTING_ASSASSIN = 18460; +const int FEAT_OOZEMASTER_SPELLCASTING_ASSASSIN = 18461; +const int FEAT_PALEMASTER_SPELLCASTING_ASSASSIN = 18462; +const int FEAT_RAGEMAGE_SPELLCASTING_ASSASSIN = 18463; +const int FEAT_SHADOWADEPT_SPELLCASTING_ASSASSIN = 18464; +const int FEAT_SOULCASTER_SPELLCASTING_ASSASSIN = 18465; +const int FEAT_SPELLDANCER_SPELLCASTING_ASSASSIN = 18466; +const int FEAT_SSWORD_SPELLCASTING_ASSASSIN = 18467; +const int FEAT_TIAMAT_SPELLCASTING_ASSASSIN = 18468; +const int FEAT_UNSEEN_SPELLCASTING_ASSASSIN = 18469; +const int FEAT_ULTMAGUS_SPELLCASTING_ASSASSIN = 18470; +const int FEAT_WILDMAGE_SPELLCASTING_ASSASSIN = 18471; + +//:: Bard marker feats +const int FEAT_ABCHAMP_SPELLCASTING_BARD = 18472; +const int FEAT_AOTS_SPELLCASTING_BARD = 18473; +const int FEAT_ALCHEM_SPELLCASTING_BARD = 18474; +const int FEAT_ALIENIST_SPELLCASTING_BARD = 18475; +const int FEAT_ANIMA_SPELLCASTING_BARD = 18476; +const int FEAT_ARCTRICK_SPELLCASTING_BARD = 18477; +const int FEAT_ASMODEUS_SPELLCASTING_BARD = 18478; +const int FEAT_BSINGER_SPELLCASTING_BARD = 18479; +const int FEAT_BLDMAGUS_SPELLCASTING_BARD = 18480; +const int FEAT_CMANCER_SPELLCASTING_BARD = 18481; +const int FEAT_DIABOLIST_SPELLCASTING_BARD = 18482; +const int FEAT_DHEART_SPELLCASTING_BARD = 18483; +const int FEAT_DSONG_SPELLCASTING_BARD = 18484; +const int FEAT_EKNIGHT_SPELLCASTING_BARD = 18485; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_BARD = 18486; +const int FEAT_ELESAVANT_SPELLCASTING_BARD = 18487; +const int FEAT_ETHEURGE_SPELLCASTING_BARD = 18488; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BARD = 18489; +const int FEAT_FROSTMAGE_SPELLCASTING_BARD = 18490; +const int FEAT_GRAZZT_SPELLCASTING_BARD = 18491; +const int FEAT_HARPERM_SPELLCASTING_BARD = 18492; +const int FEAT_HATHRAN_SPELLCASTING_BARD = 18493; +const int FEAT_HAVOC_SPELLCASTING_BARD = 18494; +const int FEAT_JPM_SPELLCASTING_BARD = 18495; +const int FEAT_JUDICATOR_SPELLCASTING_BARD = 18496; +const int FEAT_MAESTER_SPELLCASTING_BARD = 18497; +const int FEAT_MAGEKILLER_SPELLCASTING_BARD = 18498; +const int FEAT_MHARPER_SPELLCASTING_BARD = 18499; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_BARD = 18500; +const int FEAT_NOCTUMANCER_SPELLCASTING_BARD = 18501; +const int FEAT_OOZEMASTER_SPELLCASTING_BARD = 18502; +const int FEAT_PALEMASTER_SPELLCASTING_BARD = 18503; +const int FEAT_RAGEMAGE_SPELLCASTING_BARD = 18504; +const int FEAT_SHADOWADEPT_SPELLCASTING_BARD = 18505; +const int FEAT_SOULCASTER_SPELLCASTING_BARD = 18506; +const int FEAT_SPELLDANCER_SPELLCASTING_BARD = 18507; +const int FEAT_SSWORD_SPELLCASTING_BARD = 18508; +const int FEAT_TIAMAT_SPELLCASTING_BARD = 18509; +const int FEAT_ULTMAGUS_SPELLCASTING_BARD = 18510; +const int FEAT_UNSEEN_SPELLCASTING_BARD = 18511; +const int FEAT_VIRTUOSO_SPELLCASTING_BARD = 18512; +const int FEAT_WILDMAGE_SPELLCASTING_BARD = 18513; +const int FEAT_WWOC_SPELLCASTING_BARD = 18514; + +//:: Beguiler marker feats +const int FEAT_ABCHAMP_SPELLCASTING_BEGUILER = 18515; +const int FEAT_AOTS_SPELLCASTING_BEGUILER = 18516; +const int FEAT_ALCHEM_SPELLCASTING_BEGUILER = 18517; +const int FEAT_ALIENIST_SPELLCASTING_BEGUILER = 18518; +const int FEAT_ANIMA_SPELLCASTING_BEGUILER = 18519; +const int FEAT_ARCHMAGE_SPELLCASTING_BEGUILER = 18520; +const int FEAT_ARCTRICK_SPELLCASTING_BEGUILER = 18521; +const int FEAT_ASMODEUS_SPELLCASTING_BEGUILER = 18522; +const int FEAT_BSINGER_SPELLCASTING_BEGUILER = 18523; +const int FEAT_BLDMAGUS_SPELLCASTING_BEGUILER = 18524; +const int FEAT_CMANCER_SPELLCASTING_BEGUILER = 18525; +const int FEAT_DIABOLIST_SPELLCASTING_BEGUILER = 18526; +const int FEAT_DHEART_SPELLCASTING_BEGUILER = 18527; +const int FEAT_DSONG_SPELLCASTING_BEGUILER = 18528; +const int FEAT_EKNIGHT_SPELLCASTING_BEGUILER = 18529; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_BEGUILER = 18530; +const int FEAT_ELESAVANT_SPELLCASTING_BEGUILER = 18531; +const int FEAT_ETHEURGE_SPELLCASTING_BEGUILER = 18532; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BEGUILER = 18533; +const int FEAT_FROSTMAGE_SPELLCASTING_BEGUILER = 18534; +const int FEAT_GRAZZT_SPELLCASTING_BEGUILER = 18535; +const int FEAT_HARPERM_SPELLCASTING_BEGUILER = 18536; +const int FEAT_HATHRAN_SPELLCASTING_BEGUILER = 18537; +const int FEAT_HAVOC_SPELLCASTING_BEGUILER = 18538; +const int FEAT_JPM_SPELLCASTING_BEGUILER = 18539; +const int FEAT_JUDICATOR_SPELLCASTING_BEGUILER = 18540; +const int FEAT_MAESTER_SPELLCASTING_BEGUILER = 18541; +const int FEAT_MAGEKILLER_SPELLCASTING_BEGUILER = 18542; +const int FEAT_MHARPER_SPELLCASTING_BEGUILER = 18543; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_BEGUILER = 18544; +const int FEAT_NOCTUMANCER_SPELLCASTING_BEGUILER = 18545; +const int FEAT_OOZEMASTER_SPELLCASTING_BEGUILER = 18546; +const int FEAT_PALEMASTER_SPELLCASTING_BEGUILER = 18547; +const int FEAT_RAGEMAGE_SPELLCASTING_BEGUILER = 18548; +const int FEAT_SHADOWADEPT_SPELLCASTING_BEGUILER = 18549; +const int FEAT_SOULCASTER_SPELLCASTING_BEGUILER = 18550; +const int FEAT_SPELLDANCER_SPELLCASTING_BEGUILER = 18551; +const int FEAT_SSWORD_SPELLCASTING_BEGUILER = 18552; +const int FEAT_TIAMAT_SPELLCASTING_BEGUILER = 18553; +const int FEAT_ULTMAGUS_SPELLCASTING_BEGUILER = 18554; +const int FEAT_UNSEEN_SPELLCASTING_BEGUILER = 18555; +const int FEAT_VIRTUOSO_SPELLCASTING_BEGUILER = 18556; +const int FEAT_WILDMAGE_SPELLCASTING_BEGUILER = 18557; +const int FEAT_WWOC_SPELLCASTING_BEGUILER = 18558; + +//:: Celebrant of Sharess marker feats +const int FEAT_ABCHAMP_SPELLCASTING_CELEBRANT_SHARESS = 18559; +const int FEAT_ALCHEM_SPELLCASTING_CELEBRANT_SHARESS = 18560; +const int FEAT_ALIENIST_SPELLCASTING_CELEBRANT_SHARESS = 18561; +const int FEAT_ARCTRICK_SPELLCASTING_CELEBRANT_SHARESS = 18562; +const int FEAT_BONDED_SPELLCASTING_CELEBRANT_SHARESS = 18563; +const int FEAT_BSINGER_SPELLCASTING_CELEBRANT_SHARESS = 18564; +const int FEAT_CMANCER_SPELLCASTING_CELEBRANT_SHARESS = 18565; +const int FEAT_DHEART_SPELLCASTING_CELEBRANT_SHARESS = 18566; +const int FEAT_DSONG_SPELLCASTING_CELEBRANT_SHARESS = 18567; +const int FEAT_EKNIGHT_SPELLCASTING_CELEBRANT_SHARESS = 18568; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_CELEBRANT_SHARESS = 18569; +const int FEAT_ELESAVANT_SPELLCASTING_CELEBRANT_SHARESS = 18570; +const int FEAT_ETHEURGE_SPELLCASTING_CELEBRANT_SHARESS = 18571; +const int FEAT_FROSTMAGE_SPELLCASTING_CELEBRANT_SHARESS = 18572; +const int FEAT_HARPERM_SPELLCASTING_CELEBRANT_SHARESS = 18573; +const int FEAT_HAVOC_SPELLCASTING_CELEBRANT_SHARESS = 18574; +const int FEAT_JPM_SPELLCASTING_CELEBRANT_SHARESS = 18575; +const int FEAT_MHARPER_SPELLCASTING_CELEBRANT_SHARESS = 18576; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_CELEBRANT_SHARESS = 18577; +const int FEAT_NOCTUMANCER_SPELLCASTING_CELEBRANT_SHARESS = 18578; +const int FEAT_OOZEMASTER_SPELLCASTING_CELEBRANT_SHARESS = 18579; +const int FEAT_RAGEMAGE_SPELLCASTING_CELEBRANT_SHARESS = 18580; +const int FEAT_SHADOWADEPT_SPELLCASTING_CELEBRANT_SHARESS = 18581; +const int FEAT_SOULCASTER_SPELLCASTING_CELEBRANT_SHARESS = 18582; +const int FEAT_SPELLDANCER_SPELLCASTING_CELEBRANT_SHARESS = 18583; +const int FEAT_SSWORD_SPELLCASTING_CELEBRANT_SHARESS = 18584; +const int FEAT_ULTMAGUS_SPELLCASTING_CELEBRANT_SHARESS = 18585; +const int FEAT_UNSEEN_SPELLCASTING_CELEBRANT_SHARESS = 18586; +const int FEAT_WILDMAGE_SPELLCASTING_CELEBRANT_SHARESS = 18587; +const int FEAT_WWOC_SPELLCASTING_CELEBRANT_SHARESS = 18588; + +//:: CotSP marker feats +const int FEAT_ABCHAMP_SPELLCASTING_CULTIST_PEAK = 18589; +const int FEAT_AOTS_SPELLCASTING_CULTIST_PEAK = 18590; +const int FEAT_ANIMA_SPELLCASTING_CULTIST_PEAK = 18591; +const int FEAT_ARCTRICK_SPELLCASTING_CULTIST_PEAK = 18592; +const int FEAT_ASMODEUS_SPELLCASTING_CULTIST_PEAK = 18593; +const int FEAT_BSINGER_SPELLCASTING_CULTIST_PEAK = 18594; +const int FEAT_CMANCER_SPELLCASTING_CULTIST_PEAK = 18595; +const int FEAT_DIABOLIST_SPELLCASTING_CULTIST_PEAK = 18596; +const int FEAT_DSONG_SPELLCASTING_CULTIST_PEAK = 18597; +const int FEAT_EKNIGHT_SPELLCASTING_CULTIST_PEAK = 18598; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_CULTIST_PEAK = 18599; +const int FEAT_ELESAVANT_SPELLCASTING_CULTIST_PEAK = 18600; +const int FEAT_ETHEURGE_SPELLCASTING_CULTIST_PEAK = 18601; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_CULTIST_PEAK = 18602; +const int FEAT_FROSTMAGE_SPELLCASTING_CULTIST_PEAK = 18603; +const int FEAT_GRAZZT_SPELLCASTING_CULTIST_PEAK = 18604; +const int FEAT_HARPERM_SPELLCASTING_CULTIST_PEAK = 18605; +const int FEAT_HATHRAN_SPELLCASTING_CULTIST_PEAK = 18606; +const int FEAT_HAVOC_SPELLCASTING_CULTIST_PEAK = 18607; +const int FEAT_JPM_SPELLCASTING_CULTIST_PEAK = 18608; +const int FEAT_MHARPER_SPELLCASTING_CULTIST_PEAK = 18609; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_CULTIST_PEAK = 18610; +const int FEAT_NOCTUMANCER_SPELLCASTING_CULTIST_PEAK = 18611; +const int FEAT_OOZEMASTER_SPELLCASTING_CULTIST_PEAK = 18612; +const int FEAT_PALEMASTER_SPELLCASTING_CULTIST_PEAK = 18613; +const int FEAT_RAGEMAGE_SPELLCASTING_CULTIST_PEAK = 18614; +const int FEAT_SHADOWADEPT_SPELLCASTING_CULTIST_PEAK = 18615; +const int FEAT_SOULCASTER_SPELLCASTING_CULTIST_PEAK = 18616; +const int FEAT_SPELLDANCER_SPELLCASTING_CULTIST_PEAK = 18617; +const int FEAT_SSWORD_SPELLCASTING_CULTIST_PEAK = 18618; +const int FEAT_TIAMAT_SPELLCASTING_CULTIST_PEAK = 18619; +const int FEAT_ULTMAGUS_SPELLCASTING_CULTIST_PEAK = 18620; +const int FEAT_UNSEEN_SPELLCASTING_CULTIST_PEAK = 18621; +const int FEAT_WILDMAGE_SPELLCASTING_CULTIST_PEAK = 18622; +const int FEAT_WWOC_SPELLCASTING_CULTIST_PEAK = 18623; + +//:: Dread Necromancer marker feats +const int FEAT_AOTS_SPELLCASTING_DNECRO = 18624; +const int FEAT_ALCHEM_SPELLCASTING_DNECRO = 18625; +const int FEAT_ALIENIST_SPELLCASTING_DNECRO = 18626; +const int FEAT_ANIMA_SPELLCASTING_DNECRO = 18627; +const int FEAT_ARCHMAGE_SPELLCASTING_DNECRO = 18628; +const int FEAT_ARCTRICK_SPELLCASTING_DNECRO = 18629; +const int FEAT_ASMODEUS_SPELLCASTING_DNECRO = 18630; +const int FEAT_BSINGER_SPELLCASTING_DNECRO = 18631; +const int FEAT_BLDMAGUS_SPELLCASTING_DNECRO = 18632; +const int FEAT_CMANCER_SPELLCASTING_DNECRO = 18633; +const int FEAT_DIABOLIST_SPELLCASTING_DNECRO = 18634; +const int FEAT_DHEART_SPELLCASTING_DNECRO = 18635; +const int FEAT_DSONG_SPELLCASTING_DNECRO = 18636; +const int FEAT_EKNIGHT_SPELLCASTING_DNECRO = 18637; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_DNECRO = 18638; +const int FEAT_ELESAVANT_SPELLCASTING_DNECRO = 18639; +const int FEAT_ETHEURGE_SPELLCASTING_DNECRO = 18640; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DNECRO = 18641; +const int FEAT_FROSTMAGE_SPELLCASTING_DNECRO = 18642; +const int FEAT_GRAZZT_SPELLCASTING_DNECRO = 18643; +const int FEAT_HARPERM_SPELLCASTING_DNECRO = 18644; +const int FEAT_HATHRAN_SPELLCASTING_DNECRO = 18645; +const int FEAT_HAVOC_SPELLCASTING_DNECRO = 18646; +const int FEAT_JPM_SPELLCASTING_DNECRO = 18647; +const int FEAT_JUDICATOR_SPELLCASTING_DNECRO = 18648; +const int FEAT_MAESTER_SPELLCASTING_DNECRO = 18649; +const int FEAT_MAGEKILLER_SPELLCASTING_DNECRO = 18650; +const int FEAT_MHARPER_SPELLCASTING_DNECRO = 18651; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_DNECRO = 18652; +const int FEAT_NOCTUMANCER_SPELLCASTING_DNECRO = 18653; +const int FEAT_OOZEMASTER_SPELLCASTING_DNECRO = 18654; +const int FEAT_PALEMASTER_SPELLCASTING_DNECRO = 18655; +const int FEAT_RAGEMAGE_SPELLCASTING_DNECRO = 18656; +const int FEAT_SHADOWADEPT_SPELLCASTING_DNECRO = 18657; +const int FEAT_SOULCASTER_SPELLCASTING_DNECRO = 18658; +const int FEAT_SPELLDANCER_SPELLCASTING_DNECRO = 18659; +const int FEAT_SSWORD_SPELLCASTING_DNECRO = 18660; +const int FEAT_TIAMAT_SPELLCASTING_DNECRO = 18661; +const int FEAT_TNECRO_SPELLCASTING_DNECRO = 18662; +const int FEAT_ULTMAGUS_SPELLCASTING_DNECRO = 18663; +const int FEAT_UNSEEN_SPELLCASTING_DNECRO = 18664; +const int FEAT_WILDMAGE_SPELLCASTING_DNECRO = 18665; +const int FEAT_WWOC_SPELLCASTING_DNECRO = 18666; + +//:: Duskblade marker feats +const int FEAT_ABCHAMP_SPELLCASTING_DUSKBLADE = 18667; +const int FEAT_AOTS_SPELLCASTING_DUSKBLADE = 18668; +const int FEAT_ALCHEM_SPELLCASTING_DUSKBLADE = 18669; +const int FEAT_ANIMA_SPELLCASTING_DUSKBLADE = 18670; +const int FEAT_ARCTRICK_SPELLCASTING_DUSKBLADE = 18671; +const int FEAT_ASMODEUS_SPELLCASTING_DUSKBLADE = 18672; +const int FEAT_BSINGER_SPELLCASTING_DUSKBLADE = 18673; +const int FEAT_BLDMAGUS_SPELLCASTING_DUSKBLADE = 18674; +const int FEAT_CMANCER_SPELLCASTING_DUSKBLADE = 18675; +const int FEAT_DHEART_SPELLCASTING_DUSKBLADE = 18676; +const int FEAT_DIABOLIST_SPELLCASTING_DUSKBLADE = 18677; +const int FEAT_DSONG_SPELLCASTING_DUSKBLADE = 18678; +const int FEAT_EKNIGHT_SPELLCASTING_DUSKBLADE = 18679; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_DUSKBLADE = 18680; +const int FEAT_ELESAVANT_SPELLCASTING_DUSKBLADE = 18681; +const int FEAT_ETHEURGE_SPELLCASTING_DUSKBLADE = 18682; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DUSKBLADE = 18683; +const int FEAT_FROSTMAGE_SPELLCASTING_DUSKBLADE = 18684; +const int FEAT_GRAZZT_SPELLCASTING_DUSKBLADE = 18685; +const int FEAT_HARPERM_SPELLCASTING_DUSKBLADE = 18686; +const int FEAT_HATHRAN_SPELLCASTING_DUSKBLADE = 18687; +const int FEAT_HAVOC_SPELLCASTING_DUSKBLADE = 18688; +const int FEAT_JPM_SPELLCASTING_DUSKBLADE = 18689; +const int FEAT_JUDICATOR_SPELLCASTING_DUSKBLADE = 18690; +const int FEAT_MAESTER_SPELLCASTING_DUSKBLADE = 18691; +const int FEAT_MAGEKILLER_SPELLCASTING_DUSKBLADE = 18692; +const int FEAT_MHARPER_SPELLCASTING_DUSKBLADE = 18693; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_DUSKBLADE = 18694; +const int FEAT_NOCTUMANCER_SPELLCASTING_DUSKBLADE = 18695; +const int FEAT_OOZEMASTER_SPELLCASTING_DUSKBLADE = 18696; +const int FEAT_PALEMASTER_SPELLCASTING_DUSKBLADE = 18697; +const int FEAT_RAGEMAGE_SPELLCASTING_DUSKBLADE = 18698; +const int FEAT_SHADOWADEPT_SPELLCASTING_DUSKBLADE = 18699; +const int FEAT_SOULCASTER_SPELLCASTING_DUSKBLADE = 18700; +const int FEAT_SPELLDANCER_SPELLCASTING_DUSKBLADE = 18701; +const int FEAT_SSWORD_SPELLCASTING_DUSKBLADE = 18702; +const int FEAT_TIAMAT_SPELLCASTING_DUSKBLADE = 18703; +const int FEAT_TNECRO_SPELLCASTING_DUSKBLADE = 18704; +const int FEAT_ULTMAGUS_SPELLCASTING_DUSKBLADE = 18705; +const int FEAT_UNSEEN_SPELLCASTING_DUSKBLADE = 18706; +const int FEAT_VIRTUOSO_SPELLCASTING_DUSKBLADE = 18707; +const int FEAT_WILDMAGE_SPELLCASTING_DUSKBLADE = 18708; +const int FEAT_WWOC_SPELLCASTING_DUSKBLADE = 18709; + +//:: Harper Scout marker feats +const int FEAT_ABCHAMP_SPELLCASTING_HARPER = 18710; +const int FEAT_ANIMA_SPELLCASTING_HARPER = 18711; +const int FEAT_ARCTRICK_SPELLCASTING_HARPER = 18712; +const int FEAT_BSINGER_SPELLCASTING_HARPER = 18713; +const int FEAT_CMANCER_SPELLCASTING_HARPER = 18714; +const int FEAT_DHEART_SPELLCASTING_HARPER = 18715; +const int FEAT_DSONG_SPELLCASTING_HARPER = 18716; +const int FEAT_EKNIGHT_SPELLCASTING_HARPER = 18717; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_HARPER = 18718; +const int FEAT_ELESAVANT_SPELLCASTING_HARPER = 18719; +const int FEAT_ETHEURGE_SPELLCASTING_HARPER = 18720; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_HARPER = 18721; +const int FEAT_FROSTMAGE_SPELLCASTING_HARPER = 18722; +const int FEAT_HARPERM_SPELLCASTING_HARPER = 18723; +const int FEAT_HATHRAN_SPELLCASTING_HARPER = 18724; +const int FEAT_HAVOC_SPELLCASTING_HARPER = 18725; +const int FEAT_JPM_SPELLCASTING_HARPER = 18726; +const int FEAT_MHARPER_SPELLCASTING_HARPER = 18727; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_HARPER = 18728; +const int FEAT_NOCTUMANCER_SPELLCASTING_HARPER = 18729; +const int FEAT_OOZEMASTER_SPELLCASTING_HARPER = 18730; +const int FEAT_PALEMASTER_SPELLCASTING_HARPER = 18731; +const int FEAT_RAGEMAGE_SPELLCASTING_HARPER = 18732; +const int FEAT_SHADOWADEPT_SPELLCASTING_HARPER = 18733; +const int FEAT_SOULCASTER_SPELLCASTING_HARPER = 18734; +const int FEAT_SPELLDANCER_SPELLCASTING_HARPER = 18735; +const int FEAT_SSWORD_SPELLCASTING_HARPER = 18736; +const int FEAT_ULTMAGUS_SPELLCASTING_HARPER = 18737; +const int FEAT_UNSEEN_SPELLCASTING_HARPER = 18738; +const int FEAT_WILDMAGE_SPELLCASTING_HARPER = 18739; + +//:: Hexblade marker feats +const int FEAT_ABCHAMP_SPELLCASTING_HEXBLADE = 18740; +const int FEAT_AOTS_SPELLCASTING_HEXBLADE = 18741; +const int FEAT_ALCHEM_SPELLCASTING_HEXBLADE = 18742; +const int FEAT_ANIMA_SPELLCASTING_HEXBLADE = 18743; +const int FEAT_ARCTRICK_SPELLCASTING_HEXBLADE = 18744; +const int FEAT_ASMODEUS_SPELLCASTING_HEXBLADE = 18745; +const int FEAT_BONDED_SPELLCASTING_HEXBLADE = 18746; +const int FEAT_BSINGER_SPELLCASTING_HEXBLADE = 18747; +const int FEAT_CMANCER_SPELLCASTING_HEXBLADE = 18748; +const int FEAT_DIABOLIST_SPELLCASTING_HEXBLADE = 18749; +const int FEAT_DHEART_SPELLCASTING_HEXBLADE = 18750; +const int FEAT_DSONG_SPELLCASTING_HEXBLADE = 18751; +const int FEAT_EKNIGHT_SPELLCASTING_HEXBLADE = 18752; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_HEXBLADE = 18753; +const int FEAT_ELESAVANT_SPELLCASTING_HEXBLADE = 18754; +const int FEAT_ETHEURGE_SPELLCASTING_HEXBLADE = 18755; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_HEXBLADE = 18756; +const int FEAT_FROSTMAGE_SPELLCASTING_HEXBLADE = 18757; +const int FEAT_GRAZZT_SPELLCASTING_HEXBLADE = 18758; +const int FEAT_HARPERM_SPELLCASTING_HEXBLADE = 18759; +const int FEAT_HATHRAN_SPELLCASTING_HEXBLADE = 18760; +const int FEAT_HAVOC_SPELLCASTING_HEXBLADE = 18761; +const int FEAT_JPM_SPELLCASTING_HEXBLADE = 18762; +const int FEAT_JUDICATOR_SPELLCASTING_HEXBLADE = 18763; +const int FEAT_MHARPER_SPELLCASTING_HEXBLADE = 18764; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_HEXBLADE = 18765; +const int FEAT_NOCTUMANCER_SPELLCASTING_HEXBLADE = 18766; +const int FEAT_OOZEMASTER_SPELLCASTING_HEXBLADE = 18767; +const int FEAT_PALEMASTER_SPELLCASTING_HEXBLADE = 18768; +const int FEAT_RAGEMAGE_SPELLCASTING_HEXBLADE = 18769; +const int FEAT_SHADOWADEPT_SPELLCASTING_HEXBLADE = 18770; +const int FEAT_SOULCASTER_SPELLCASTING_HEXBLADE = 18771; +const int FEAT_SPELLDANCER_SPELLCASTING_HEXBLADE = 18772; +const int FEAT_SSWORD_SPELLCASTING_HEXBLADE = 18773; +const int FEAT_TIAMAT_SPELLCASTING_HEXBLADE = 18774; +const int FEAT_TNECRO_SPELLCASTING_HEXBLADE = 18775; +const int FEAT_ULTMAGUS_SPELLCASTING_HEXBLADE = 18776; +const int FEAT_UNSEEN_SPELLCASTING_HEXBLADE = 18777; +const int FEAT_WILDMAGE_SPELLCASTING_HEXBLADE = 18778; +const int FEAT_WWOC_SPELLCASTING_HEXBLADE = 18779; + +//:: Knight of the Weave marker feats +const int FEAT_ABCHAMP_SPELLCASTING_KNIGHT_WEAVE = 18780; +const int FEAT_ALCHEM_SPELLCASTING_KNIGHT_WEAVE = 18781; +const int FEAT_ANIMA_SPELLCASTING_KNIGHT_WEAVE = 18782; +const int FEAT_ARCTRICK_SPELLCASTING_KNIGHT_WEAVE = 18783; +const int FEAT_BSINGER_SPELLCASTING_KNIGHT_WEAVE = 18784; +const int FEAT_BLDMAGUS_SPELLCASTING_KNIGHT_WEAVE = 18785; +const int FEAT_CMANCER_SPELLCASTING_KNIGHT_WEAVE = 18786; +const int FEAT_DHEART_SPELLCASTING_KNIGHT_WEAVE = 18787; +const int FEAT_DSONG_SPELLCASTING_KNIGHT_WEAVE = 18788; +const int FEAT_EKNIGHT_SPELLCASTING_KNIGHT_WEAVE = 18789; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_KNIGHT_WEAVE = 18790; +const int FEAT_ELESAVANT_SPELLCASTING_KNIGHT_WEAVE = 18791; +const int FEAT_ETHEURGE_SPELLCASTING_KNIGHT_WEAVE = 18792; +const int FEAT_FMM_SPELLCASTING_KNIGHT_WEAVE = 18793; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_KNIGHT_WEAVE = 18794; +const int FEAT_FROSTMAGE_SPELLCASTING_KNIGHT_WEAVE = 18795; +const int FEAT_HARPERM_SPELLCASTING_KNIGHT_WEAVE = 18796; +const int FEAT_HATHRAN_SPELLCASTING_KNIGHT_WEAVE = 18797; +const int FEAT_HAVOC_SPELLCASTING_KNIGHT_WEAVE = 18798; +const int FEAT_JPM_SPELLCASTING_KNIGHT_WEAVE = 18799; +const int FEAT_MAESTER_SPELLCASTING_KNIGHT_WEAVE = 18800; +const int FEAT_MHARPER_SPELLCASTING_KNIGHT_WEAVE = 18801; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_KNIGHT_WEAVE = 18802; +const int FEAT_NOCTUMANCER_SPELLCASTING_KNIGHT_WEAVE = 18803; +const int FEAT_OOZEMASTER_SPELLCASTING_KNIGHT_WEAVE = 18804; +const int FEAT_PALEMASTER_SPELLCASTING_KNIGHT_WEAVE = 18805; +const int FEAT_RAGEMAGE_SPELLCASTING_KNIGHT_WEAVE = 18806; +const int FEAT_SHADOWADEPT_SPELLCASTING_KNIGHT_WEAVE = 18807; +const int FEAT_SOULCASTER_SPELLCASTING_KNIGHT_WEAVE = 18808; +const int FEAT_SPELLDANCER_SPELLCASTING_KNIGHT_WEAVE = 18809; +const int FEAT_SSWORD_SPELLCASTING_KNIGHT_WEAVE = 18810; +const int FEAT_ULTMAGUS_SPELLCASTING_KNIGHT_WEAVE = 18811; +const int FEAT_UNSEEN_SPELLCASTING_KNIGHT_WEAVE = 18812; +const int FEAT_WAYFARER_SPELLCASTING_KNIGHT_WEAVE = 18813; +const int FEAT_WILDMAGE_SPELLCASTING_KNIGHT_WEAVE = 18814; +const int FEAT_WWOC_SPELLCASTING_KNIGHT_WEAVE = 18815; + +//:: Telflammar Shadowlord marker feats +const int FEAT_ABCHAMP_SPELLCASTING_SHADOWLORD = 18816; +const int FEAT_AOTS_SPELLCASTING_SHADOWLORD = 18817; +const int FEAT_ALCHEM_SPELLCASTING_SHADOWLORD = 18818; +const int FEAT_ANIMA_SPELLCASTING_SHADOWLORD = 18819; +const int FEAT_ARCTRICK_SPELLCASTING_SHADOWLORD = 18820; +const int FEAT_ASMODEUS_SPELLCASTING_SHADOWLORD = 18821; +const int FEAT_BSINGER_SPELLCASTING_SHADOWLORD = 18822; +const int FEAT_CMANCER_SPELLCASTING_SHADOWLORD = 18823; +const int FEAT_DIABOLIST_SPELLCASTING_SHADOWLORD = 18824; +const int FEAT_DHEART_SPELLCASTING_SHADOWLORD = 18825; +const int FEAT_DSONG_SPELLCASTING_SHADOWLORD = 18826; +const int FEAT_EKNIGHT_SPELLCASTING_SHADOWLORD = 18827; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_SHADOWLORD = 18828; +const int FEAT_ELESAVANT_SPELLCASTING_SHADOWLORD = 18829; +const int FEAT_ETHEURGE_SPELLCASTING_SHADOWLORD = 18830; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SHADOWLORD = 18831; +const int FEAT_FROSTMAGE_SPELLCASTING_SHADOWLORD = 18832; +const int FEAT_GRAZZT_SPELLCASTING_SHADOWLORD = 18833; +const int FEAT_HARPERM_SPELLCASTING_SHADOWLORD = 18834; +const int FEAT_HATHRAN_SPELLCASTING_SHADOWLORD = 18835; +const int FEAT_HAVOC_SPELLCASTING_SHADOWLORD = 18836; +const int FEAT_JPM_SPELLCASTING_SHADOWLORD = 18837; +const int FEAT_JUDICATOR_SPELLCASTING_SHADOWLORD = 18838; +const int FEAT_MHARPER_SPELLCASTING_SHADOWLORD = 18839; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SHADOWLORD = 18840; +const int FEAT_NOCTUMANCER_SPELLCASTING_SHADOWLORD = 18841; +const int FEAT_OOZEMASTER_SPELLCASTING_SHADOWLORD = 18842; +const int FEAT_PALEMASTER_SPELLCASTING_SHADOWLORD = 18843; +const int FEAT_RAGEMAGE_SPELLCASTING_SHADOWLORD = 18844; +const int FEAT_SHADOWADEPT_SPELLCASTING_SHADOWLORD = 18845; +const int FEAT_SOULCASTER_SPELLCASTING_SHADOWLORD = 18846; +const int FEAT_SPELLDANCER_SPELLCASTING_SHADOWLORD = 18847; +const int FEAT_SSWORD_SPELLCASTING_SHADOWLORD = 18848; +const int FEAT_TIAMAT_SPELLCASTING_SHADOWLORD = 18849; +const int FEAT_TNECRO_SPELLCASTING_SHADOWLORD = 18850; +const int FEAT_ULTMAGUS_SPELLCASTING_SHADOWLORD = 18851; +const int FEAT_UNSEEN_SPELLCASTING_SHADOWLORD = 18852; +const int FEAT_WILDMAGE_SPELLCASTING_SHADOWLORD = 18853; +const int FEAT_WWOC_SPELLCASTING_SHADOWLORD = 18854; + +//:: Sorcerer marker feats +const int FEAT_ABCHAMP_SPELLCASTING_SORCERER = 18855; +const int FEAT_AOTS_SPELLCASTING_SORCERER = 18856; +const int FEAT_ALCHEM_SPELLCASTING_SORCERER = 18857; +const int FEAT_ALIENIST_SPELLCASTING_SORCERER = 18858; +const int FEAT_ANIMA_SPELLCASTING_SORCERER = 18859; +const int FEAT_ARCHMAGE_SPELLCASTING_SORCERER = 18860; +const int FEAT_ARCTRICK_SPELLCASTING_SORCERER = 18861; +const int FEAT_ASMODEUS_SPELLCASTING_SORCERER = 18862; +const int FEAT_BSINGER_SPELLCASTING_SORCERER = 18863; +const int FEAT_BLDMAGUS_SPELLCASTING_SORCERER = 18864; +const int FEAT_BONDED_SPELLCASTING_SORCERER = 18865; +const int FEAT_CMANCER_SPELLCASTING_SORCERER = 18866; +const int FEAT_DIABOLIST_SPELLCASTING_SORCERER = 18867; +const int FEAT_DHEART_SPELLCASTING_SORCERER = 18868; +const int FEAT_DSONG_SPELLCASTING_SORCERER = 18869; +const int FEAT_EKNIGHT_SPELLCASTING_SORCERER = 18870; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_SORCERER = 18871; +const int FEAT_ELESAVANT_SPELLCASTING_SORCERER = 18872; +const int FEAT_ETHEURGE_SPELLCASTING_SORCERER = 18873; +const int FEAT_FMM_SPELLCASTING_SORCERER = 18874; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SORCERER = 18875; +const int FEAT_FROSTMAGE_SPELLCASTING_SORCERER = 18876; +const int FEAT_GRAZZT_SPELLCASTING_SORCERER = 18877; +const int FEAT_HARPERM_SPELLCASTING_SORCERER = 18878; +const int FEAT_HATHRAN_SPELLCASTING_SORCERER = 18879; +const int FEAT_HAVOC_SPELLCASTING_SORCERER = 18880; +const int FEAT_JPM_SPELLCASTING_SORCERER = 18881; +const int FEAT_JUDICATOR_SPELLCASTING_SORCERER = 18882; +const int FEAT_MAESTER_SPELLCASTING_SORCERER = 18883; +const int FEAT_MAGEKILLER_SPELLCASTING_SORCERER = 18884; +const int FEAT_MHARPER_SPELLCASTING_SORCERER = 18885; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SORCERER = 18886; +const int FEAT_NOCTUMANCER_SPELLCASTING_SORCERER = 18887; +const int FEAT_OOZEMASTER_SPELLCASTING_SORCERER = 18888; +const int FEAT_PALEMASTER_SPELLCASTING_SORCERER = 18889; +const int FEAT_RAGEMAGE_SPELLCASTING_SORCERER = 18890; +const int FEAT_SHADOWADEPT_SPELLCASTING_SORCERER = 18891; +const int FEAT_SOULCASTER_SPELLCASTING_SORCERER = 18892; +const int FEAT_SPELLDANCER_SPELLCASTING_SORCERER = 18893; +const int FEAT_SSWORD_SPELLCASTING_SORCERER = 18894; +const int FEAT_TIAMAT_SPELLCASTING_SORCERER = 18895; +const int FEAT_TNECRO_SPELLCASTING_SORCERER = 18896; +const int FEAT_ULTMAGUS_SPELLCASTING_SORCERER = 18897; +const int FEAT_UNSEEN_SPELLCASTING_SORCERER = 18898; +const int FEAT_VIRTUOSO_SPELLCASTING_SORCERER = 18899; +const int FEAT_WAYFARER_SPELLCASTING_SORCERER = 18900; +const int FEAT_WILDMAGE_SPELLCASTING_SORCERER = 18901; +const int FEAT_WWOC_SPELLCASTING_SORCERER = 18902; + +//:: Sublime Chord marker feats +const int FEAT_ABCHAMP_SPELLCASTING_SUBLIME_CHORD = 18903; +const int FEAT_AOTS_SPELLCASTING_SUBLIME_CHORD = 18904; +const int FEAT_ALCHEM_SPELLCASTING_SUBLIME_CHORD = 18905; +const int FEAT_ALIENIST_SPELLCASTING_SUBLIME_CHORD = 18906; +const int FEAT_ANIMA_SPELLCASTING_SUBLIME_CHORD = 18907; +const int FEAT_ARCHMAGE_SPELLCASTING_SUBLIME_CHORD = 18908; +const int FEAT_ARCTRICK_SPELLCASTING_SUBLIME_CHORD = 18909; +const int FEAT_ASMODEUS_SPELLCASTING_SUBLIME_CHORD = 18910; +const int FEAT_BSINGER_SPELLCASTING_SUBLIME_CHORD = 18911; +const int FEAT_BLDMAGUS_SPELLCASTING_SUBLIME_CHORD = 18912; +const int FEAT_CMANCER_SPELLCASTING_SUBLIME_CHORD = 18913; +const int FEAT_DIABOLIST_SPELLCASTING_SUBLIME_CHORD = 18914; +const int FEAT_DHEART_SPELLCASTING_SUBLIME_CHORD = 18915; +const int FEAT_DSONG_SPELLCASTING_SUBLIME_CHORD = 18916; +const int FEAT_EKNIGHT_SPELLCASTING_SUBLIME_CHORD = 18917; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_SUBLIME_CHORD = 18918; +const int FEAT_ELESAVANT_SPELLCASTING_SUBLIME_CHORD = 18919; +const int FEAT_ETHEURGE_SPELLCASTING_SUBLIME_CHORD = 18920; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SUBLIME_CHORD = 18921; +const int FEAT_FROSTMAGE_SPELLCASTING_SUBLIME_CHORD = 18922; +const int FEAT_GRAZZT_SPELLCASTING_SUBLIME_CHORD = 18923; +const int FEAT_HARPERM_SPELLCASTING_SUBLIME_CHORD = 18924; +const int FEAT_HATHRAN_SPELLCASTING_SUBLIME_CHORD = 18925; +const int FEAT_HAVOC_SPELLCASTING_SUBLIME_CHORD = 18926; +const int FEAT_JPM_SPELLCASTING_SUBLIME_CHORD = 18927; +const int FEAT_JUDICATOR_SPELLCASTING_SUBLIME_CHORD = 18928; +const int FEAT_MAESTER_SPELLCASTING_SUBLIME_CHORD = 18929; +const int FEAT_MAGEKILLER_SPELLCASTING_SUBLIME_CHORD = 18930; +const int FEAT_MHARPER_SPELLCASTING_SUBLIME_CHORD = 18931; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SUBLIME_CHORD = 18932; +const int FEAT_NOCTUMANCER_SPELLCASTING_SUBLIME_CHORD = 18933; +const int FEAT_OOZEMASTER_SPELLCASTING_SUBLIME_CHORD = 18934; +const int FEAT_PALEMASTER_SPELLCASTING_SUBLIME_CHORD = 18935; +const int FEAT_RAGEMAGE_SPELLCASTING_SUBLIME_CHORD = 18936; +const int FEAT_SHADOWADEPT_SPELLCASTING_SUBLIME_CHORD = 18937; +const int FEAT_SOULCASTER_SPELLCASTING_SUBLIME_CHORD = 18938; +const int FEAT_SPELLDANCER_SPELLCASTING_SUBLIME_CHORD = 18939; +const int FEAT_SSWORD_SPELLCASTING_SUBLIME_CHORD = 18940; +const int FEAT_TIAMAT_SPELLCASTING_SUBLIME_CHORD = 18941; +const int FEAT_TNECRO_SPELLCASTING_SUBLIME_CHORD = 18942; +const int FEAT_ULTMAGUS_SPELLCASTING_SUBLIME_CHORD = 18943; +const int FEAT_UNSEEN_SPELLCASTING_SUBLIME_CHORD = 18944; +const int FEAT_WAYFARER_SPELLCASTING_SUBLIME_CHORD = 18945; +const int FEAT_WILDMAGE_SPELLCASTING_SUBLIME_CHORD = 18946; +const int FEAT_WWOC_SPELLCASTING_SUBLIME_CHORD = 18947; + +const int FEAT_SUBLIME_CHORD_SPELLCASTING_ABERRATION = 19605; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_FEY = 19606; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_MONSTROUS = 19607; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_OUTSIDER = 19608; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_SHAPECHANGER = 19609; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_BARD = 19610; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_BEGUILER = 19611; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_DUSKBLADE = 19612; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_SORCERER = 19613; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_WARMAGE = 19614; +const int FEAT_SUBLIME_CHORD_SPELLCASTING_WIZARD = 19615; + +//:: Suel Archanamach marker feats +const int FEAT_ABCHAMP_SPELLCASTING_SUEL_ARCHANAMACH = 18948; +const int FEAT_AOTS_SPELLCASTING_SUEL_ARCHANAMACH = 18949; +const int FEAT_ALCHEM_SPELLCASTING_SUEL_ARCHANAMACH = 18950; +const int FEAT_ANIMA_SPELLCASTING_SUEL_ARCHANAMACH = 18951; +const int FEAT_ARCTRICK_SPELLCASTING_SUEL_ARCHANAMACH = 18952; +const int FEAT_ASMODEUS_SPELLCASTING_SUEL_ARCHANAMACH = 18953; +const int FEAT_BSINGER_SPELLCASTING_SUEL_ARCHANAMACH = 18954; +const int FEAT_BLDMAGUS_SPELLCASTING_ARCHANAMACH = 18955; +const int FEAT_CMANCER_SPELLCASTING_SUEL_ARCHANAMACH = 18956; +const int FEAT_DIABOLIST_SPELLCASTING_SUEL_ARCHANAMACH = 18957; +const int FEAT_DHEART_SPELLCASTING_SUEL_ARCHANAMACH = 18958; +const int FEAT_DSONG_SPELLCASTING_SUEL_ARCHANAMACH = 18959; +const int FEAT_EKNIGHT_SPELLCASTING_SUEL_ARCHANAMACH = 18960; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_SUEL_ARCHANAMACH = 18961; +const int FEAT_ELESAVANT_SPELLCASTING_SUEL_ARCHANAMACH = 18962; +const int FEAT_ETHEURGE_SPELLCASTING_SUEL_ARCHANAMACH = 18963; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SUEL_ARCHANAMACH = 18964; +const int FEAT_FROSTMAGE_SPELLCASTING_SUEL_ARCHANAMACH = 18965; +const int FEAT_GRAZZT_SPELLCASTING_SUEL_ARCHANAMACH = 18966; +const int FEAT_HARPERM_SPELLCASTING_SUEL_ARCHANAMACH = 18967; +const int FEAT_HATHRAN_SPELLCASTING_SUEL_ARCHANAMACH = 18968; +const int FEAT_HAVOC_SPELLCASTING_SUEL_ARCHANAMACH = 18969; +const int FEAT_JPM_SPELLCASTING_SUEL_ARCHANAMACH = 18970; +const int FEAT_JUDICATOR_SPELLCASTING_SUEL_ARCHANAMACH = 18971; +const int FEAT_MAESTER_SPELLCASTING_SUEL_ARCHANAMACH = 18972; +const int FEAT_MAGEKILLER_SPELLCASTING_SUEL_ARCHANAMACH = 18973; +const int FEAT_MHARPER_SPELLCASTING_SUEL_ARCHANAMACH = 18974; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SUEL_ARCHANAMACH = 18975; +const int FEAT_NOCTUMANCER_SPELLCASTING_SUEL_ARCHANAMACH = 18976; +const int FEAT_OOZEMASTER_SPELLCASTING_SUEL_ARCHANAMACH = 18977; +const int FEAT_PALEMASTER_SPELLCASTING_SUEL_ARCHANAMACH = 18978; +const int FEAT_RAGEMAGE_SPELLCASTING_SUEL_ARCHANAMACH = 18979; +const int FEAT_SHADOWADEPT_SPELLCASTING_SUEL_ARCHANAMACH = 18980; +const int FEAT_SOULCASTER_SPELLCASTING_SUEL_ARCHANAMACH = 18981; +const int FEAT_SPELLDANCER_SPELLCASTING_SUEL_ARCHANAMACH = 18982; +const int FEAT_SSWORD_SPELLCASTING_SUEL_ARCHANAMACH = 18983; +const int FEAT_TIAMAT_SPELLCASTING_SUEL_ARCHANAMACH = 18984; +const int FEAT_ULTMAGUS_SPELLCASTING_SUEL_ARCHANAMACH = 18985; +const int FEAT_UNSEEN_SPELLCASTING_SUEL_ARCHANAMACH = 18986; +const int FEAT_WILDMAGE_SPELLCASTING_SUEL_ARCHANAMACH = 18987; +const int FEAT_WWOC_SPELLCASTING_SUEL_ARCHANAMACH = 18988; + +//:: Warmage marker feats +const int FEAT_AOTS_SPELLCASTING_WARMAGE = 18989; +const int FEAT_ALCHEM_SPELLCASTING_WARMAGE = 18990; +const int FEAT_ANIMA_SPELLCASTING_WARMAGE = 18991; +const int FEAT_ARCHMAGE_SPELLCASTING_WARMAGE = 18992; +const int FEAT_ARCTRICK_SPELLCASTING_WARMAGE = 18993; +const int FEAT_ASMODEUS_SPELLCASTING_WARMAGE = 18994; +const int FEAT_BSINGER_SPELLCASTING_WARMAGE = 18995; +const int FEAT_BLDMAGUS_SPELLCASTING_WARMAGE = 18996; +const int FEAT_BONDED_SPELLCASTING_WARMAGE = 18997; +const int FEAT_CMANCER_SPELLCASTING_WARMAGE = 18998; +const int FEAT_DIABOLIST_SPELLCASTING_WARMAGE = 18999; +const int FEAT_DHEART_SPELLCASTING_WARMAGE = 19000; +const int FEAT_DSONG_SPELLCASTING_WARMAGE = 19001; +const int FEAT_EKNIGHT_SPELLCASTING_WARMAGE = 19002; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_WARMAGE = 19003; +const int FEAT_ELESAVANT_SPELLCASTING_WARMAGE = 19004; +const int FEAT_ETHEURGE_SPELLCASTING_WARMAGE = 19005; +const int FEAT_FMM_SPELLCASTING_WARMAGE = 19006; +const int FEAT_FROSTMAGE_SPELLCASTING_WARMAGE = 19007; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_WARMAGE = 19008; +const int FEAT_GRAZZT_SPELLCASTING_WARMAGE = 19009; +const int FEAT_HARPERM_SPELLCASTING_WARMAGE = 19010; +const int FEAT_HATHRAN_SPELLCASTING_WARMAGE = 19011; +const int FEAT_HAVOC_SPELLCASTING_WARMAGE = 19012; +const int FEAT_JPM_SPELLCASTING_WARMAGE = 19013; +const int FEAT_JUDICATOR_SPELLCASTING_WARMAGE = 19014; +const int FEAT_MAESTER_SPELLCASTING_WARMAGE = 19015; +const int FEAT_MAGEKILLER_SPELLCASTING_WARMAGE = 19016; +const int FEAT_MHARPER_SPELLCASTING_WARMAGE = 19017; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_WARMAGE = 19018; +const int FEAT_NOCTUMANCER_SPELLCASTING_WARMAGE = 19019; +const int FEAT_OOZEMASTER_SPELLCASTING_WARMAGE = 19020; +const int FEAT_PALEMASTER_SPELLCASTING_WARMAGE = 19021; +const int FEAT_RAGEMAGE_SPELLCASTING_WARMAGE = 19022; +const int FEAT_SHADOWADEPT_SPELLCASTING_WARMAGE = 19023; +const int FEAT_SOULCASTER_SPELLCASTING_WARMAGE = 19024; +const int FEAT_SPELLDANCER_SPELLCASTING_WARMAGE = 19025; +const int FEAT_SSWORD_SPELLCASTING_WARMAGE = 19026; +const int FEAT_TIAMAT_SPELLCASTING_WARMAGE = 19027; +const int FEAT_ULTMAGUS_SPELLCASTING_WARMAGE = 19028; +const int FEAT_UNSEEN_SPELLCASTING_WARMAGE = 19029; +const int FEAT_VIRTUOSO_SPELLCASTING_WARMAGE = 19030; +const int FEAT_WILDMAGE_SPELLCASTING_WARMAGE = 19031; +const int FEAT_WWOC_SPELLCASTING_WARMAGE = 19032; + +//:: Wizard marker feats +const int FEAT_ABCHAMP_SPELLCASTING_WIZARD = 19033; +const int FEAT_AOTS_SPELLCASTING_WIZARD = 19034; +const int FEAT_ALCHEM_SPELLCASTING_WIZARD = 19035; +const int FEAT_ALIENIST_SPELLCASTING_WIZARD = 19036; +const int FEAT_ANIMA_SPELLCASTING_WIZARD = 19037; +const int FEAT_ARCHMAGE_SPELLCASTING_WIZARD = 19038; +const int FEAT_ARCTRICK_SPELLCASTING_WIZARD = 19039; +const int FEAT_ASMODEUS_SPELLCASTING_WIZARD = 19040; +const int FEAT_BSINGER_SPELLCASTING_WIZARD = 19041; +const int FEAT_BLDMAGUS_SPELLCASTING_WIZARD = 19042; +const int FEAT_BONDED_SPELLCASTING_WIZARD = 19043; +const int FEAT_CMANCER_SPELLCASTING_WIZARD = 19044; +const int FEAT_DIABOLIST_SPELLCASTING_WIZARD = 19045; +const int FEAT_DSONG_SPELLCASTING_WIZARD = 19046; +const int FEAT_EKNIGHT_SPELLCASTING_WIZARD = 19047; +const int FEAT_ENLIGHTENEDFIST_SPELLCASTING_WIZARD = 19048; +const int FEAT_ELESAVANT_SPELLCASTING_WIZARD = 19049; +const int FEAT_ETHEURGE_SPELLCASTING_WIZARD = 19050; +const int FEAT_FMM_SPELLCASTING_WIZARD = 19051; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_WIZARD = 19052; +const int FEAT_FROSTMAGE_SPELLCASTING_WIZARD = 19053; +const int FEAT_GRAZZT_SPELLCASTING_WIZARD = 19054; +const int FEAT_HARPERM_SPELLCASTING_WIZARD = 19055; +const int FEAT_HATHRAN_SPELLCASTING_WIZARD = 19056; +const int FEAT_HAVOC_SPELLCASTING_WIZARD = 19057; +const int FEAT_JPM_SPELLCASTING_WIZARD = 19058; +const int FEAT_JUDICATOR_SPELLCASTING_WIZARD = 19059; +const int FEAT_MAESTER_SPELLCASTING_WIZARD = 19060; +const int FEAT_MAGEKILLER_SPELLCASTING_WIZARD = 19061; +const int FEAT_MHARPER_SPELLCASTING_WIZARD = 19062; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_WIZARD = 19063; +const int FEAT_NOCTUMANCER_SPELLCASTING_WIZARD = 19064; +const int FEAT_OOZEMASTER_SPELLCASTING_WIZARD = 19065; +const int FEAT_PALEMASTER_SPELLCASTING_WIZARD = 19066; +const int FEAT_RAGEMAGE_SPELLCASTING_WIZARD = 19067; +const int FEAT_REDWIZ_SPELLCASTING_WIZARD = 19068; +const int FEAT_SHADOWADEPT_SPELLCASTING_WIZARD = 19069; +const int FEAT_SOULCASTER_SPELLCASTING_WIZARD = 19070; +const int FEAT_SPELLDANCER_SPELLCASTING_WIZARD = 19071; +const int FEAT_SSWORD_SPELLCASTING_WIZARD = 19072; +const int FEAT_TIAMAT_SPELLCASTING_WIZARD = 19073; +const int FEAT_TNECRO_SPELLCASTING_WIZARD = 19074; +const int FEAT_ULTMAGUS_SPELLCASTING_WIZARD = 19075; +const int FEAT_UNSEEN_SPELLCASTING_WIZARD = 19076; +const int FEAT_VIRTUOSO_SPELLCASTING_WIZARD = 19077; +const int FEAT_WAYFARER_SPELLCASTING_WIZARD = 19078; +const int FEAT_WILDMAGE_SPELLCASTING_WIZARD = 19079; +const int FEAT_WWOC_SPELLCASTING_WIZARD = 19080; + +//:: Archivist marker feats +const int FEAT_BFZ_SPELLCASTING_ARCHIVIST = 19081; +const int FEAT_BLIGHTLORD_SPELLCASTING_ARCHIVIST = 19082; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_ARCHIVIST = 19083; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_ARCHIVIST = 19084; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_ARCHIVIST = 19085; +const int FEAT_ELDISCIPLE_SPELLCASTING_ARCHIVIST = 19086; +const int FEAT_FISTRAZIEL_SPELLCASTING_ARCHIVIST = 19087; +const int FEAT_FORESTMASTER_SPELLCASTING_ARCHIVIST = 19088; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_ARCHIVIST = 19089; +const int FEAT_HATHRAN_SPELLCASTING_ARCHIVIST = 19090; +const int FEAT_HEARTWARDER_SPELLCASTING_ARCHIVIST = 19091; +const int FEAT_HIEROPHANT_SPELLCASTING_ARCHIVIST = 19092; +const int FEAT_HOSPITALER_SPELLCASTING_ARCHIVIST = 19093; +const int FEAT_JUDICATOR_SPELLCASTING_ARCHIVIST = 19094; +const int FEAT_KORD_SPELLCASTING_ARCHIVIST = 19095; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_ARCHIVIST = 19096; +const int FEAT_OLLAM_SPELLCASTING_ARCHIVIST = 19097; +const int FEAT_OOZEMASTER_SPELLCASTING_ARCHIVIST = 19098; +const int FEAT_ORCUS_SPELLCASTING_ARCHIVIST = 19099; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_ARCHIVIST = 19100; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_ARCHIVIST = 19101; +const int FEAT_RUNECASTER_SPELLCASTING_ARCHIVIST = 19102; +const int FEAT_SACREDPURIFIER_SPELLCASTING_ARCHIVIST = 19103; +const int FEAT_SACREDFIST_SPELLCASTING_ARCHIVIST = 19104; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_ARCHIVIST = 19105; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_ARCHIVIST = 19106; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_ARCHIVIST = 19107; +const int FEAT_SHINING_BLADE_SPELLCASTING_ARCHIVIST = 19108; +const int FEAT_STORMLORD_SPELLCASTING_ARCHIVIST = 19109; +const int FEAT_SWIFT_WING_SPELLCASTING_ARCHIVIST = 19110; +const int FEAT_TEMPUS_SPELLCASTING_ARCHIVIST = 19111; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_ARCHIVIST = 19112; +const int FEAT_TNECRO_SPELLCASTING_ARCHIVIST = 19113; +const int FEAT_WARPRIEST_SPELLCASTING_ARCHIVIST = 19114; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_ARCHIVIST = 19596; + +//:: Blackguard marker feats +const int FEAT_BFZ_SPELLCASTING_BLACKGUARD = 19115; +const int FEAT_BLIGHTLORD_SPELLCASTING_BLACKGUARD = 19116; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_BLACKGUARD = 19117; +const int FEAT_ELDISCIPLE_SPELLCASTING_BLACKGUARD = 19118; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BLACKGUARD = 19119; +const int FEAT_HOSPITALER_SPELLCASTING_BLACKGUARD = 19120; +const int FEAT_JUDICATOR_SPELLCASTING_BLACKGUARD = 19121; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_BLACKGUARD = 19122; +const int FEAT_OOZEMASTER_SPELLCASTING_BLACKGUARD = 19123; +const int FEAT_ORCUS_SPELLCASTING_BLACKGUARD = 19124; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_BLACKGUARD = 19125; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_BLACKGUARD = 19126; +const int FEAT_RUNECASTER_SPELLCASTING_BLACKGUARD = 19127; +const int FEAT_SACREDFIST_SPELLCASTING_BLACKGUARD = 19128; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_BLACKGUARD = 19129; +const int FEAT_STORMLORD_SPELLCASTING_BLACKGUARD = 19130; +const int FEAT_SWIFT_WING_SPELLCASTING_BLACKGUARD = 19131; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_BLACKGUARD = 19132; +const int FEAT_TEMPUS_SPELLCASTING_BLACKGUARD = 19133; +const int FEAT_WARPRIEST_SPELLCASTING_BLACKGUARD = 19134; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_BLACKGUARD = 19597; + + +//:: Blighter marker feats +const int FEAT_BFZ_SPELLCASTING_BLIGHTER = 19135; +const int FEAT_BLIGHTLORD_SPELLCASTING_BLIGHTER = 19136; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_BLIGHTER = 19137; +const int FEAT_ELDISCIPLE_SPELLCASTING_BLIGHTER = 19138; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BLIGHTER = 19139; +const int FEAT_HIEROPHANT_SPELLCASTING_BLIGHTER = 19140; +const int FEAT_HOSPITALER_SPELLCASTING_BLIGHTER = 19141; +const int FEAT_JUDICATOR_SPELLCASTING_BLIGHTER = 19142; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_BLIGHTER = 19143; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_BLIGHTER = 19144; +const int FEAT_OOZEMASTER_SPELLCASTING_BLIGHTER = 19145; +const int FEAT_ORCUS_SPELLCASTING_BLIGHTER = 19146; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_BLIGHTER = 19147; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_BLIGHTER = 19148; +const int FEAT_RUNECASTER_SPELLCASTING_BLIGHTER = 19149; +const int FEAT_SACREDFIST_SPELLCASTING_BLIGHTER = 19150; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_BLIGHTER = 19151; +const int FEAT_STORMLORD_SPELLCASTING_BLIGHTER = 19152; +const int FEAT_SWIFT_WING_SPELLCASTING_BLIGHTER = 19153; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_BLIGHTER = 19154; +const int FEAT_TNECRO_SPELLCASTING_BLIGHTER = 19155; +const int FEAT_WARPRIEST_SPELLCASTING_BLIGHTER = 19156; + +//:: Cleric marker feats +const int FEAT_BFZ_SPELLCASTING_CLERIC = 19157; +const int FEAT_BLIGHTLORD_SPELLCASTING_CLERIC = 19158; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_CLERIC = 19159; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_CLERIC = 19160; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_CLERIC = 19161; +const int FEAT_ELDISCIPLE_SPELLCASTING_CLERIC = 19162; +const int FEAT_FISTRAZIEL_SPELLCASTING_CLERIC = 19163; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_CLERIC = 19164; +const int FEAT_FORESTMASTER_SPELLCASTING_CLERIC = 19165; +const int FEAT_HATHRAN_SPELLCASTING_CLERIC = 19166; +const int FEAT_HEARTWARDER_SPELLCASTING_CLERIC = 19167; +const int FEAT_HIEROPHANT_SPELLCASTING_CLERIC = 19168; +const int FEAT_HOSPITALER_SPELLCASTING_CLERIC = 19169; +const int FEAT_JUDICATOR_SPELLCASTING_CLERIC = 19170; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_CLERIC = 19171; +const int FEAT_KORD_SPELLCASTING_CLERIC = 19172; +const int FEAT_MORNINGLORD_SPELLCASTING_CLERIC = 19173; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_CLERIC = 19174; +const int FEAT_OLLAM_SPELLCASTING_CLERIC = 19175; +const int FEAT_OOZEMASTER_SPELLCASTING_CLERIC = 19176; +const int FEAT_ORCUS_SPELLCASTING_CLERIC = 19177; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_CLERIC = 19178; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_CLERIC = 19179; +const int FEAT_RUNECASTER_SPELLCASTING_CLERIC = 19180; +const int FEAT_SACREDFIST_SPELLCASTING_CLERIC = 19181; +const int FEAT_SACREDPURIFIER_SPELLCASTING_CLERIC = 19182; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_CLERIC = 19183; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_CLERIC = 19184; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_CLERIC = 19185; +const int FEAT_SHINING_BLADE_SPELLCASTING_CLERIC = 19186; +const int FEAT_STORMLORD_SPELLCASTING_CLERIC = 19187; +const int FEAT_SWIFT_WING_SPELLCASTING_CLERIC = 19188; +const int FEAT_TEMPUS_SPELLCASTING_CLERIC = 19189; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_CLERIC = 19190; +const int FEAT_TNECRO_SPELLCASTING_CLERIC = 19191; +const int FEAT_WARPRIEST_SPELLCASTING_CLERIC = 19192; + +//:: Slayer of Domiel marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_DOMIEL = 19193; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_DOMIEL = 19194; +const int FEAT_ELDISCIPLE_SPELLCASTING_DOMIEL = 19195; +const int FEAT_FISTRAZIEL_SPELLCASTING_DOMIEL = 19196; +const int FEAT_HATHRAN_SPELLCASTING_DOMIEL = 19197; +const int FEAT_HOSPITALER_SPELLCASTING_DOMIEL = 19198; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_DOMIEL = 19199; +const int FEAT_OLLAM_SPELLCASTING_DOMIEL = 19200; +const int FEAT_OOZEMASTER_SPELLCASTING_DOMIEL = 19201; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_DOMIEL = 19202; +const int FEAT_RUNECASTER_SPELLCASTING_DOMIEL = 19203; +const int FEAT_SACREDFIST_SPELLCASTING_DOMIEL = 19204; +const int FEAT_SACREDPURIFIER_SPELLCASTING_DOMIEL = 19205; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_DOMIEL = 19206; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_DOMIEL = 19207; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_DOMIEL = 19208; +const int FEAT_SHINING_BLADE_SPELLCASTING_DOMIEL = 19209; +const int FEAT_SWIFT_WING_SPELLCASTING_DOMIEL = 19210; +const int FEAT_WARPRIEST_SPELLCASTING_DOMIEL = 19211; + +//: Druid maker feats +const int FEAT_BLIGHTLORD_SPELLCASTING_DRUID = 19212; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_DRUID = 19213; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_DRUID = 19214; +const int FEAT_ELDISCIPLE_SPELLCASTING_DRUID = 19215; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DRUID = 19216; +const int FEAT_FORESTMASTER_SPELLCASTING_DRUID = 19217; +const int FEAT_HATHRAN_SPELLCASTING_DRUID = 19218; +const int FEAT_HIEROPHANT_SPELLCASTING_DRUID = 19219; +const int FEAT_HOSPITALER_SPELLCASTING_DRUID = 19220; +const int FEAT_JUDICATOR_SPELLCASTING_DRUID = 19221; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_DRUID = 19222; +const int FEAT_OOZEMASTER_SPELLCASTING_DRUID = 19223; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_DRUID = 19224; +const int FEAT_RUNECASTER_SPELLCASTING_DRUID = 19225; +const int FEAT_SACREDFIST_SPELLCASTING_DRUID = 19226; +const int FEAT_SACREDPURIFIER_SPELLCASTING_DRUID = 19227; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_DRUID = 19228; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_DRUID = 19229; +const int FEAT_STORMLORD_SPELLCASTING_DRUID = 19230; +const int FEAT_SWIFT_WING_SPELLCASTING_DRUID = 19231; +const int FEAT_WARPRIEST_SPELLCASTING_DRUID = 19232; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_DRUID = 19598; + +//:: Favoured Soul marker feats +const int FEAT_BFZ_SPELLCASTING_FAVOURED_SOUL = 19233; +const int FEAT_BLIGHTLORD_SPELLCASTING_FAVOURED_SOUL = 19234; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_FAVOURED_SOUL = 19235; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_FAVOURED_SOUL = 19236; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_FAVOURED_SOUL = 19237; +const int FEAT_ELDISCIPLE_SPELLCASTING_FAVOURED_SOUL = 19238; +const int FEAT_FISTRAZIEL_SPELLCASTING_FAVOURED_SOUL = 19239; +const int FEAT_FORESTMASTER_SPELLCASTING_FAVOURED_SOUL = 19240; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_FAVOURED_SOUL = 19241; +const int FEAT_HATHRAN_SPELLCASTING_FAVOURED_SOUL = 19242; +const int FEAT_HEARTWARDER_SPELLCASTING_FAVOURED_SOUL = 19243; +const int FEAT_HIEROPHANT_SPELLCASTING_FAVOURED_SOUL = 19244; +const int FEAT_HOSPITALER_SPELLCASTING_FAVOURED_SOUL = 19245; +const int FEAT_JUDICATOR_SPELLCASTING_FAVOURED_SOUL = 19246; +const int FEAT_KORD_SPELLCASTING_FAVOURED_SOUL = 19247; +const int FEAT_MORNINGLORD_SPELLCASTING_FAVOURED_SOUL = 19248; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_FAVOURED_SOUL = 19249; +const int FEAT_OLLAM_SPELLCASTING_FAVOURED_SOUL = 19250; +const int FEAT_OOZEMASTER_SPELLCASTING_FAVOURED_SOUL = 19251; +const int FEAT_ORCUS_SPELLCASTING_FAVOURED_SOUL = 19252; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_FAVOURED_SOUL = 19253; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_FAVOURED_SOUL = 19254; +const int FEAT_RUNECASTER_SPELLCASTING_FAVOURED_SOUL = 19255; +const int FEAT_SACREDFIST_SPELLCASTING_FAVOURED_SOUL = 19256; +const int FEAT_SACREDPURIFIER_SPELLCASTING_FAVOURED_SOUL = 19257; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_FAVOURED_SOUL = 19258; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_FAVOURED_SOUL = 19259; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_FAVOURED_SOUL = 19260; +const int FEAT_SHINING_BLADE_SPELLCASTING_FAVOURED_SOUL = 19261; +const int FEAT_STORMLORD_SPELLCASTING_FAVOURED_SOUL = 19262; +const int FEAT_SWIFT_WING_SPELLCASTING_FAVOURED_SOUL = 19263; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_FAVOURED_SOUL = 19264; +const int FEAT_TEMPUS_SPELLCASTING_FAVOURED_SOUL = 19265; +const int FEAT_TNECRO_SPELLCASTING_FAVOURED_SOUL = 19266; +const int FEAT_WARPRIEST_SPELLCASTING_FAVOURED_SOUL = 19267; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_FAVOURED_SOUL = 19599; + +//:: Healer marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_HEALER = 19268; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_HEALER = 19269; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_HEALER = 19270; +const int FEAT_ELDISCIPLE_SPELLCASTING_HEALER = 19271; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_HEALER = 19272; +const int FEAT_HATHRAN_SPELLCASTING_HEALER = 19273; +const int FEAT_HEARTWARDER_SPELLCASTING_HEALER = 19274; +const int FEAT_HIEROPHANT_SPELLCASTING_HEALER = 19275; +const int FEAT_HOSPITALER_SPELLCASTING_HEALER = 19276; +const int FEAT_KORD_SPELLCASTING_HEALER = 19277; +const int FEAT_MORNINGLORD_SPELLCASTING_HEALER = 19278; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_HEALER = 19279; +const int FEAT_OLLAM_SPELLCASTING_HEALER = 19280; +const int FEAT_OOZEMASTER_SPELLCASTING_HEALER = 19281; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_HEALER = 19282; +const int FEAT_RUNECASTER_SPELLCASTING_HEALER = 19283; +const int FEAT_SACREDFIST_SPELLCASTING_HEALER = 19284; +const int FEAT_SACREDPURIFIER_SPELLCASTING_HEALER = 19285; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_HEALER = 19286; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_HEALER = 19287; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_HEALER = 19288; +const int FEAT_SHINING_BLADE_SPELLCASTING_HEALER = 19289; +const int FEAT_SWIFT_WING_SPELLCASTING_HEALER = 19290; +const int FEAT_WARPRIEST_SPELLCASTING_HEALER = 19291; + +//:: Justice of Weald & Woe maker feats +const int FEAT_BFZ_SPELLCASTING_JUSTICEWW = 19292; +const int FEAT_BLIGHTLORD_SPELLCASTING_JUSTICEWW = 19293; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_JUSTICEWW = 19294; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_JUSTICEWW = 19295; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_JUSTICEWW = 19296; +const int FEAT_ELDISCIPLE_SPELLCASTING_JUSTICEWW = 19297; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_JUSTICEWW = 19298; +const int FEAT_HATHRAN_SPELLCASTING_JUSTICEWW = 19299; +const int FEAT_HEARTWARDER_SPELLCASTING_JUSTICEWW = 19300; +const int FEAT_HOSPITALER_SPELLCASTING_JUSTICEWW = 19301; +const int FEAT_JUDICATOR_SPELLCASTING_JUSTICEWW = 19302; +const int FEAT_KORD_SPELLCASTING_JUSTICEWW = 19303; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_JUSTICEWW = 19304; +const int FEAT_OLLAM_SPELLCASTING_JUSTICEWW = 19305; +const int FEAT_OOZEMASTER_SPELLCASTING_JUSTICEWW = 19306; +const int FEAT_ORCUS_SPELLCASTING_JUSTICEWW = 19307; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_JUSTICEWW = 19308; +const int FEAT_RUNECASTER_SPELLCASTING_JUSTICEWW = 19309; +const int FEAT_SACREDFIST_SPELLCASTING_JUSTICEWW = 19310; +const int FEAT_SACREDPURIFIER_SPELLCASTING_JUSTICEWW = 19311; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_JUSTICEWW = 19312; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_JUSTICEWW = 19313; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_JUSTICEWW = 19314; +const int FEAT_SHINING_BLADE_SPELLCASTING_JUSTICEWW = 19315; +const int FEAT_STORMLORD_SPELLCASTING_JUSTICEWW = 19316; +const int FEAT_SWIFT_WING_SPELLCASTING_JUSTICEWW = 19317; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_JUSTICEWW = 19318; +const int FEAT_TEMPUS_SPELLCASTING_JUSTICEWW = 19319; +const int FEAT_WARPRIEST_SPELLCASTING_JUSTICEWW = 19320; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_JUSTICEWW = 19600; + +//:: Knight of the Chalice marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_KNIGHT_CHALICE = 19321; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_KNIGHT_CHALICE = 19322; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_KNIGHT_CHALICE = 19323; +const int FEAT_ELDISCIPLE_SPELLCASTING_KNIGHT_CHALICE = 19324; +const int FEAT_FISTRAZIEL_SPELLCASTING_KNIGHT_CHALICE = 19325; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_KNIGHT_CHALICE = 19326; +const int FEAT_HATHRAN_SPELLCASTING_KNIGHT_CHALICE = 19327; +const int FEAT_HOSPITALER_SPELLCASTING_KNIGHT_CHALICE = 19328; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_KNIGHT_CHALICE = 19329; +const int FEAT_OLLAM_SPELLCASTING_KNIGHT_CHALICE = 19330; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_KNIGHT_CHALICE = 19331; +const int FEAT_RUNECASTER_SPELLCASTING_KNIGHT_CHALICE = 19332; +const int FEAT_SACREDFIST_SPELLCASTING_KNIGHT_CHALICE = 19333; +const int FEAT_SACREDPURIFIER_SPELLCASTING_KNIGHT_CHALICE = 19334; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_KNIGHT_CHALICE = 19335; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_KNIGHT_CHALICE = 19336; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_KNIGHT_CHALICE = 19337; +const int FEAT_SHINING_BLADE_SPELLCASTING_KNIGHT_CHALICE = 19338; +const int FEAT_SWIFT_WING_SPELLCASTING_KNIGHT_CHALICE = 19339; +const int FEAT_WARPRIEST_SPELLCASTING_KNIGHT_CHALICE = 19340; + +//:: Knight of the Middle Circle marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19341; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19342; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19343; +const int FEAT_ELDISCIPLE_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19344; +const int FEAT_FISTRAZIEL_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19345; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19346; +const int FEAT_HATHRAN_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19347; +const int FEAT_HEARTWARDER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19348; +const int FEAT_HOSPITALER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19349; +const int FEAT_KORD_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19350; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19351; +const int FEAT_OLLAM_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19352; +const int FEAT_OOZEMASTER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19353; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19354; +const int FEAT_RUNECASTER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19355; +const int FEAT_SACREDFIST_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19356; +const int FEAT_SACREDPURIFIER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19357; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19358; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19359; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19360; +const int FEAT_SHINING_BLADE_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19361; +const int FEAT_SWIFT_WING_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19362; +const int FEAT_WARPRIEST_SPELLCASTING_KNIGHT_MIDDLECIRCLE = 19363; + +//:: Nentyar Hunter marer feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_NENTYAR_HUNTER = 19364; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_NENTYAR_HUNTER = 19365; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_NENTYAR_HUNTER = 19366; +const int FEAT_ELDISCIPLE_SPELLCASTING_NENTYAR_HUNTER = 19367; +const int FEAT_FORESTMASTER_SPELLCASTING_NENTYAR_HUNTER = 19368; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_NENTYAR_HUNTER = 19369; +const int FEAT_HATHRAN_SPELLCASTING_NENTYAR_HUNTER = 19370; +const int FEAT_HOSPITALER_SPELLCASTING_NENTYAR_HUNTER = 19371; +const int FEAT_KORD_SPELLCASTING_NENTYAR_HUNTER = 19372; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_NENTYAR_HUNTER = 19373; +const int FEAT_OLLAM_SPELLCASTING_NENTYAR_HUNTER = 19374; +const int FEAT_OOZEMASTER_SPELLCASTING_NENTYAR_HUNTER = 19375; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_NENTYAR_HUNTER = 19376; +const int FEAT_RUNECASTER_SPELLCASTING_NENTYAR_HUNTER = 19377; +const int FEAT_SACREDFIST_SPELLCASTING_NENTYAR_HUNTER = 19378; +const int FEAT_SACREDPURIFIER_SPELLCASTING_NENTYAR_HUNTER = 19379; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_NENTYAR_HUNTER = 19380; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_NENTYAR_HUNTER = 19381; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_NENTYAR_HUNTER = 19382; +const int FEAT_SHINING_BLADE_SPELLCASTING_NENTYAR_HUNTER = 19383; +const int FEAT_SWIFT_WING_SPELLCASTING_NENTYAR_HUNTER = 19384; +const int FEAT_WARPRIEST_SPELLCASTING_NENTYAR_HUNTER = 19385; + +//:: Ocular Adept marker feats +const int FEAT_BFZ_SPELLCASTING_OCULAR = 19386; +const int FEAT_ELDISCIPLE_SPELLCASTING_OCULAR = 19387; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_OCULAR = 19388; +const int FEAT_HIEROPHANT_SPELLCASTING_OCULAR = 19389; +const int FEAT_HOSPITALER_SPELLCASTING_OCULAR = 19390; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_OCULAR = 19391; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_OCULAR = 19392; +const int FEAT_OOZEMASTER_SPELLCASTING_OCULAR = 19393; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_OCULAR = 19394; +const int FEAT_RUNECASTER_SPELLCASTING_OCULAR = 19395; +const int FEAT_SACREDFIST_SPELLCASTING_OCULAR = 19396; +const int FEAT_TNECRO_SPELLCASTING_OCULAR = 19397; +const int FEAT_WARPRIEST_SPELLCASTING_OCULAR = 19398; + +//:: Paladin marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_PALADIN = 19399; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_PALADIN = 19400; +const int FEAT_FISTRAZIEL_SPELLCASTING_PALADIN = 19401; +const int FEAT_HATHRAN_SPELLCASTING_PALADIN = 19402; +const int FEAT_HOSPITALER_SPELLCASTING_PALADIN = 19403; +const int FEAT_MORNINGLORD_SPELLCASTING_PALADIN = 19404; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_PALADIN = 19405; +const int FEAT_OLLAM_SPELLCASTING_PALADIN = 19406; +const int FEAT_OOZEMASTER_SPELLCASTING_PALADIN = 19407; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_PALADIN = 19408; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_PALADIN = 19409; +const int FEAT_RUNECASTER_SPELLCASTING_PALADIN = 19410; +const int FEAT_SACREDFIST_SPELLCASTING_PALADIN = 19411; +const int FEAT_SACREDPURIFIER_SPELLCASTING_PALADIN = 19412; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_PALADIN = 19413; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_PALADIN = 19414; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_PALADIN = 19415; +const int FEAT_SHINING_BLADE_SPELLCASTING_PALADIN = 19416; +const int FEAT_SWIFT_WING_SPELLCASTING_PALADIN = 19417; +const int FEAT_WARPRIEST_SPELLCASTING_PALADIN = 19418; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_PALADIN = 19594; + +//:: Ranger marker feats +const int FEAT_BFZ_SPELLCASTING_RANGER = 19419; +const int FEAT_BLIGHTLORD_SPELLCASTING_RANGER = 19420; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_RANGER = 19421; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_RANGER = 19422; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_RANGER = 19423; +const int FEAT_ELDISCIPLE_SPELLCASTING_RANGER = 19424; +const int FEAT_FORESTMASTER_SPELLCASTING_RANGER = 19425; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_RANGER = 19426; +const int FEAT_HATHRAN_SPELLCASTING_RANGER = 19427; +const int FEAT_HEARTWARDER_SPELLCASTING_RANGER = 19428; +const int FEAT_HOSPITALER_SPELLCASTING_RANGER = 19429; +const int FEAT_JUDICATOR_SPELLCASTING_RANGER = 19430; +const int FEAT_KORD_SPELLCASTING_RANGER = 19431; +const int FEAT_MORNINGLORD_SPELLCASTING_RANGER = 19432; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_RANGER = 19433; +const int FEAT_OLLAM_SPELLCASTING_RANGER = 19434; +const int FEAT_OOZEMASTER_SPELLCASTING_RANGER = 19435; +const int FEAT_ORCUS_SPELLCASTING_RANGER = 19436; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_RANGER = 19437; +const int FEAT_RUNECASTER_SPELLCASTING_RANGER = 19438; +const int FEAT_SACREDFIST_SPELLCASTING_RANGER = 19439; +const int FEAT_SACREDPURIFIER_SPELLCASTING_RANGER = 19440; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_RANGER = 19441; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_RANGER = 19442; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_RANGER = 19443; +const int FEAT_SHINING_BLADE_SPELLCASTING_RANGER = 19444; +const int FEAT_STORMLORD_SPELLCASTING_RANGER = 19445; +const int FEAT_SWIFT_WING_SPELLCASTING_RANGER = 19446; +const int FEAT_TEMPUS_SPELLCASTING_RANGER = 19447; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_RANGER = 19448; +const int FEAT_WARPRIEST_SPELLCASTING_RANGER = 19449; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_RANGER = 19601; + +//:: Shaman marker feats +const int FEAT_BFZ_SPELLCASTING_OASHAMAN = 19450; +const int FEAT_BLIGHTLORD_SPELLCASTING_OASHAMAN = 19451; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_OASHAMAN = 19452; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_OASHAMAN = 19453; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_OASHAMAN = 19454; +const int FEAT_ELDISCIPLE_SPELLCASTING_OASHAMAN = 19455; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_OASHAMAN = 19456; +const int FEAT_FORESTMASTER_SPELLCASTING_OASHAMAN = 19457; +const int FEAT_HATHRAN_SPELLCASTING_OASHAMAN = 19458; +const int FEAT_HEARTWARDER_SPELLCASTING_OASHAMAN = 19459; +const int FEAT_HIEROPHANT_SPELLCASTING_OASHAMAN = 19460; +const int FEAT_HOSPITALER_SPELLCASTING_OASHAMAN = 19461; +const int FEAT_JUDICATOR_SPELLCASTING_OASHAMAN = 19462; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_OASHAMAN = 19463; +const int FEAT_KORD_SPELLCASTING_OASHAMAN = 19464; +const int FEAT_MORNINGLORD_SPELLCASTING_OASHAMAN = 19465; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_OASHAMAN = 19466; +const int FEAT_OLLAM_SPELLCASTING_OASHAMAN = 19467; +const int FEAT_OOZEMASTER_SPELLCASTING_OASHAMAN = 19468; +const int FEAT_ORCUS_SPELLCASTING_OASHAMAN = 19469; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_OASHAMAN = 19470; +const int FEAT_RUBY_VINDICATOR_SPELLCASTING_OASHAMAN = 19471; +const int FEAT_RUNECASTER_SPELLCASTING_OASHAMAN = 19472; +const int FEAT_SACREDFIST_SPELLCASTING_OASHAMAN = 19473; +const int FEAT_SACREDPURIFIER_SPELLCASTING_OASHAMAN = 19474; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_OASHAMAN = 19475; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_OASHAMAN = 19476; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_OASHAMAN = 19477; +const int FEAT_SHINING_BLADE_SPELLCASTING_OASHAMAN = 19478; +const int FEAT_STORMLORD_SPELLCASTING_OASHAMAN = 19479; +const int FEAT_SWIFT_WING_SPELLCASTING_OASHAMAN = 19480; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_OASHAMAN = 19481; +const int FEAT_TEMPUS_SPELLCASTING_OASHAMAN = 19482; +const int FEAT_TNECRO_SPELLCASTING_OASHAMAN = 19483; +const int FEAT_WARPRIEST_SPELLCASTING_OASHAMAN = 19484; + +//:: Sohei marker feats +const int FEAT_BFZ_SPELLCASTING_SOHEI = 19485; +const int FEAT_BLIGHTLORD_SPELLCASTING_SOHEI = 19486; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_SOHEI = 19487; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_SOHEI = 19488; +const int FEAT_ELDISCIPLE_SPELLCASTING_SOHEI = 19489; +const int FEAT_FISTRAZIEL_SPELLCASTING_SOHEI = 19490; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SOHEI = 19491; +const int FEAT_HATHRAN_SPELLCASTING_SOHEI = 19492; +const int FEAT_HOSPITALER_SPELLCASTING_SOHEI = 19493; +const int FEAT_JUDICATOR_SPELLCASTING_SOHEI = 19494; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SOHEI = 19495; +const int FEAT_OLLAM_SPELLCASTING_SOHEI = 19496; +const int FEAT_OOZEMASTER_SPELLCASTING_SOHEI = 19497; +const int FEAT_ORCUS_SPELLCASTING_SOHEI = 19498; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_SOHEI = 19499; +const int FEAT_RUNECASTER_SPELLCASTING_SOHEI = 19500; +const int FEAT_SACREDFIST_SPELLCASTING_SOHEI = 19501; +const int FEAT_SACREDPURIFIER_SPELLCASTING_SOHEI = 19502; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_SOHEI = 19503; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_SOHEI = 19504; +const int FEAT_SHINING_BLADE_SPELLCASTING_SOHEI = 19505; +const int FEAT_SWIFT_WING_SPELLCASTING_SOHEI = 19506; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_SOHEI = 19507; +const int FEAT_WARPRIEST_SPELLCASTING_SOHEI = 19508; +const int FEAT_MORNINGLORD_SPELLCASTING_SOHEI = 19595; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_SOHEI = 19602; + +//:: Soldier of Light marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_SOL = 19509; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_SOL = 19510; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_SOL = 19511; +const int FEAT_ELDISCIPLE_SPELLCASTING_SOL = 19512; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SOL = 19513; +const int FEAT_HATHRAN_SPELLCASTING_SOL = 19514; +const int FEAT_HOSPITALER_SPELLCASTING_SOL = 19515; +const int FEAT_MORNINGLORD_SPELLCASTING_SOL = 19516; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SOL = 19517; +const int FEAT_OOZEMASTER_SPELLCASTING_SOL = 19518; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_SOL = 19519; +const int FEAT_RUNECASTER_SPELLCASTING_SOL = 19520; +const int FEAT_SACREDFIST_SPELLCASTING_SOL = 19521; +const int FEAT_SACREDPURIFIER_SPELLCASTING_SOL = 19522; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_SOL = 19523; +const int FEAT_SWIFT_WING_SPELLCASTING_SOL = 19524; +const int FEAT_WARPRIEST_SPELLCASTING_SOL = 19525; + +//:: Spirit Shaman marker feats +const int FEAT_BFZ_SPELLCASTING_SPSHAMAN = 19526; +const int FEAT_BLIGHTLORD_SPELLCASTING_SPSHAMAN = 19527; +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_SPSHAMAN = 19528; +const int FEAT_COMBAT_MEDIC_SPELLCASTING_SPSHAMAN = 19529; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_SPSHAMAN = 19530; +const int FEAT_ELDISCIPLE_SPELLCASTING_SPSHAMAN = 19531; +const int FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SPSHAMAN = 19532; +const int FEAT_FORESTMASTER_SPELLCASTING_SPSHAMAN = 19533; +const int FEAT_HATHRAN_SPELLCASTING_SPSHAMAN = 19534; +const int FEAT_HEARTWARDER_SPELLCASTING_SPSHAMAN = 19535; +const int FEAT_HIEROPHANT_SPELLCASTING_SPSHAMAN = 19536; +const int FEAT_HOSPITALER_SPELLCASTING_SPSHAMAN = 19537; +const int FEAT_JUDICATOR_SPELLCASTING_SPSHAMAN = 19538; +const int FEAT_KORD_SPELLCASTING_SPSHAMAN = 19539; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_SPSHAMAN = 19540; +const int FEAT_OLLAM_SPELLCASTING_SPSHAMAN = 19541; +const int FEAT_ORCUS_SPELLCASTING_SPSHAMAN = 19542; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_SPSHAMAN = 19543; +const int FEAT_RUNECASTER_SPELLCASTING_SPSHAMAN = 19544; +const int FEAT_SACREDFIST_SPELLCASTING_SPSHAMAN = 19545; +const int FEAT_SACREDPURIFIER_SPELLCASTING_SPSHAMAN = 19546; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_SPSHAMAN = 19547; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_SPSHAMAN = 19548; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_SPSHAMAN = 19549; +const int FEAT_SHINING_BLADE_SPELLCASTING_SPSHAMAN = 19550; +const int FEAT_STORMLORD_SPELLCASTING_SPSHAMAN = 19551; +const int FEAT_SWIFT_WING_SPELLCASTING_SPSHAMAN = 19552; +const int FEAT_TEMPUS_SPELLCASTING_SPSHAMAN = 19553; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_SPSHAMAN = 19554; +const int FEAT_WARPRIEST_SPELLCASTING_SPSHAMAN = 19555; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_SPSHAMAN = 19603; + +//:: Ur-Priest marker feats +const int FEAT_BFZ_SPELLCASTING_UR_PRIEST = 19556; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_UR_PRIEST = 19557; +const int FEAT_ELDISCIPLE_SPELLCASTING_UR_PRIEST = 19558; +const int FEAT_HIEROPHANT_SPELLCASTING_UR_PRIEST = 19559; +const int FEAT_HOSPITALER_SPELLCASTING_UR_PRIEST = 19560; +const int FEAT_JUDICATOR_SPELLCASTING_UR_PRIEST = 19561; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_UR_PRIEST = 19562; +const int FEAT_OOZEMASTER_SPELLCASTING_UR_PRIEST = 19563; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_UR_PRIEST = 19564; +const int FEAT_RUNECASTER_SPELLCASTING_UR_PRIEST = 19565; +const int FEAT_SACREDFIST_SPELLCASTING_UR_PRIEST = 19566; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_UR_PRIEST = 19567; +const int FEAT_SWIFT_WING_SPELLCASTING_UR_PRIEST = 19568; +const int FEAT_TENEBROUS_APOSTATE_SPELLCASTING_UR_PRIEST = 19569; +const int FEAT_TNECRO_SPELLCASTING_UR_PRIEST = 19570; +const int FEAT_WARPRIEST_SPELLCASTING_UR_PRIEST = 19571; +const int FEAT_MASTER_OF_SHROUDS_SPELLCASTING_UR_PRIEST = 19604; + +//:: Vassal of Bahamut marker feats +const int FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_VASSAL = 19572; +const int FEAT_CONTEMPLATIVE_SPELLCASTING_VASSAL = 19573; +const int FEAT_FISTRAZIEL_SPELLCASTING_VASSAL = 19574; +const int FEAT_HATHRAN_SPELLCASTING_VASSAL = 19575; +const int FEAT_HOSPITALER_SPELLCASTING_VASSAL = 19576; +const int FEAT_MYSTIC_THEURGE_SPELLCASTING_VASSAL = 19577; +const int FEAT_OLLAM_SPELLCASTING_VASSAL = 19578; +const int FEAT_OOZEMASTER_SPELLCASTING_VASSAL = 19579; +const int FEAT_PSYCHIC_THEURGE_SPELLCASTING_VASSAL = 19580; +const int FEAT_RUNECASTER_SPELLCASTING_VASSAL = 19581; +const int FEAT_SACREDFIST_SPELLCASTING_VASSAL = 19582; +const int FEAT_SACREDPURIFIER_SPELLCASTING_VASSAL = 19583; +const int FEAT_SANCTIFIED_MIND_SPELLCASTING_VASSAL = 19584; +const int FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_VASSAL = 19585; +const int FEAT_SHADOWBANE_STALKER_SPELLCASTING_VASSAL = 19586; +const int FEAT_SHINING_BLADE_SPELLCASTING_VASSAL = 19587; +const int FEAT_SWIFT_WING_SPELLCASTING_VASSAL = 19588; +const int FEAT_WARPRIEST_SPELLCASTING_VASSAL = 19589; + +//:: No spellcasting or invoking marker feats +const int FEAT_ASMODEUS_SPELLCASTING_NONE = 19590; +const int FEAT_TIAMAT_SPELLCASTING_NONE = 19591; +const int FEAT_DSONG_SPELLCASTING_NONE = 19592; +const int FEAT_OLLAM_SPELLCASTING_NONE = 19593; + +//:: PRC8 Hidden Talent Feats +const int FEAT_HIDDEN_TALENT_BIOFEEDBACK = 25901; +const int FEAT_HIDDEN_TALENT_BITE_WOLF = 25902; +const int FEAT_HIDDEN_TALENT_BOLT = 25903; +const int FEAT_HIDDEN_TALENT_BURST = 25904; +const int FEAT_HIDDEN_TALENT_CALLTOMIND = 25905; +const int FEAT_HIDDEN_TALENT_CALL_WEAPONRY = 25906; +const int FEAT_HIDDEN_TALENT_CHAMELEON = 25907; +const int FEAT_HIDDEN_TALENT_CLAWS_BEAST = 25908; +const int FEAT_HIDDEN_TALENT_COMPRESSION = 25909; +const int FEAT_HIDDEN_TALENT_CONCEALTHOUGHT = 25910; +const int FEAT_HIDDEN_TALENT_CREATESOUND = 25911; +const int FEAT_HIDDEN_TALENT_CRYSTALSHARD = 25912; +const int FEAT_HIDDEN_TALENT_DAZE = 25913; +const int FEAT_HIDDEN_TALENT_DECELERATION = 25914; +const int FEAT_HIDDEN_TALENT_DEFPRECOG = 25915; +const int FEAT_HIDDEN_TALENT_DEMORALIZE = 25916; +const int FEAT_HIDDEN_TALENT_DISABLE = 25917; +const int FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH = 25918; +const int FEAT_HIDDEN_TALENT_DISTRACT = 25919; +const int FEAT_HIDDEN_TALENT_ELFSIGHT = 25920; +const int FEAT_HIDDEN_TALENT_EMPATHY = 25921; +const int FEAT_HIDDEN_TALENT_EMPTYMIND = 25922; +const int FEAT_HIDDEN_TALENT_ENERGYRAY = 25923; +const int FEAT_HIDDEN_TALENT_ENTANGLE = 25924; +const int FEAT_HIDDEN_TALENT_EXPANSION = 25925; +const int FEAT_HIDDEN_TALENT_FARHAND = 25926; +const int FEAT_HIDDEN_TALENT_FORCESCREEN = 25927; +const int FEAT_HIDDEN_TALENT_GREASE = 25928; +const int FEAT_HIDDEN_TALENT_HAMMER = 25929; +const int FEAT_HIDDEN_TALENT_INERTIALARMOUR = 25930; +const int FEAT_HIDDEN_TALENT_MATTERAGITATION = 25931; +const int FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW = 25932; +const int FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON = 25933; +const int FEAT_HIDDEN_TALENT_MINDTHRUST = 25934; +const int FEAT_HIDDEN_TALENT_MYLIGHT = 25935; +const int FEAT_HIDDEN_TALENT_OFFPRECOG = 25936; +const int FEAT_HIDDEN_TALENT_OFFPRESC = 25937; +const int FEAT_HIDDEN_TALENT_PREVENOM = 25938; +const int FEAT_HIDDEN_TALENT_PREVENOM_WEAPON = 25939; +const int FEAT_HIDDEN_TALENT_SKATE = 25940; +const int FEAT_HIDDEN_TALENT_STOMP = 25941; +const int FEAT_HIDDEN_TALENT_SYNESTHETE = 25942; +const int FEAT_HIDDEN_TALENT_TELEMPATHICPRO = 25943; +const int FEAT_HIDDEN_TALENT_THICKSKIN = 25944; +const int FEAT_HIDDEN_TALENT_VIGOR = 25945; +const int FEAT_HIDDEN_TALENT_GRIP_IRON = 25946; + + + + +//:: Test void +// void main (){} \ No newline at end of file diff --git a/src/include/prc_getbest_inc.nss b/src/include/prc_getbest_inc.nss new file mode 100644 index 0000000..a17eb0a --- /dev/null +++ b/src/include/prc_getbest_inc.nss @@ -0,0 +1,404 @@ +// Returns the best available-for-casting n-th Level spell from oTarget. +int GetBestL0Spell(object oTarget, int nSpell); +int GetBestL1Spell(object oTarget, int nSpell); +int GetBestL2Spell(object oTarget, int nSpell); +int GetBestL3Spell(object oTarget, int nSpell); +int GetBestL4Spell(object oTarget, int nSpell); +int GetBestL5Spell(object oTarget, int nSpell); +int GetBestL6Spell(object oTarget, int nSpell); +int GetBestL7Spell(object oTarget, int nSpell); +int GetBestL8Spell(object oTarget, int nSpell); +int GetBestL9Spell(object oTarget, int nSpell); + +// Returns the best available-for-casting spell from oTarget's repertoire. +int GetBestAvailableSpell(object oTarget); + +#include "prc_inc_core" + +int GetBestL0Spell(object oTarget, int nFallbackSpell) +{ + int nRow = 0; + string s2DA = "spells"; + string sInnate, sStrRef; + int nSpellID; + + while (TRUE) + { + sInnate = Get2DACache(s2DA, "Innate", nRow); + if (sInnate == "") break; // End of 2DA + + if (StringToInt(sInnate) == 0) + { + nSpellID = nRow; + + if (PRCGetHasSpell(nSpellID, oTarget)) + { + return nSpellID; + } + } + + nRow++; + } + + return nFallbackSpell; +} + +/* int GetBestL0Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_ACID_SPLASH, oTarget)) return SPELL_ACID_SPLASH; + if(PRCGetHasSpell(SPELL_RAY_OF_FROST, oTarget)) return SPELL_RAY_OF_FROST; + if(PRCGetHasSpell(SPELL_DAZE, oTarget)) return SPELL_DAZE; + if(PRCGetHasSpell(SPELL_ELECTRIC_JOLT, oTarget)) return SPELL_ELECTRIC_JOLT; + if(PRCGetHasSpell(SPELL_FLARE, oTarget)) return SPELL_FLARE; + if(PRCGetHasSpell(SPELL_RESISTANCE, oTarget)) return SPELL_RESISTANCE; + if(PRCGetHasSpell(SPELL_LIGHT, oTarget)) return SPELL_LIGHT; + if(PRCGetHasSpell(SPELL_VIRTUE, oTarget)) return SPELL_VIRTUE; + if(PRCGetHasSpell(SPELL_CURE_MINOR_WOUNDS, oTarget)) return SPELL_CURE_MINOR_WOUNDS; + if(PRCGetHasSpell(SPELL_INFLICT_MINOR_WOUNDS, oTarget)) return SPELL_INFLICT_MINOR_WOUNDS; + return nSpell; +} */ + +/* + +bscureObject + + */ + +int GetBestL1Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_MAGIC_MISSILE, oTarget)) return SPELL_MAGIC_MISSILE; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_I, oTarget)) return SPELL_SUMMON_CREATURE_I; + if(PRCGetHasSpell(SPELL_DOOM, oTarget)) return SPELL_DOOM; + if(PRCGetHasSpell(SPELL_BANE, oTarget)) return SPELL_BANE; + if(PRCGetHasSpell(SPELL_BLESS, oTarget)) return SPELL_BLESS; + if(PRCGetHasSpell(SPELL_MAGIC_FANG, oTarget)) return SPELL_MAGIC_FANG; + if(PRCGetHasSpell(SPELL_MAGE_ARMOR, oTarget)) return SPELL_MAGE_ARMOR; + if(PRCGetHasSpell(SPELL_ENDURE_ELEMENTS, oTarget)) return SPELL_ENDURE_ELEMENTS; + if(PRCGetHasSpell(SPELL_LESSER_DISPEL, oTarget)) return SPELL_LESSER_DISPEL; + if(PRCGetHasSpell(SPELL_SANCTUARY, oTarget)) return SPELL_SANCTUARY; + if(PRCGetHasSpell(SPELL_SHIELD, oTarget)) return SPELL_SHIELD; + if(PRCGetHasSpell(SPELL_CHARM_PERSON, oTarget)) return SPELL_CHARM_PERSON; + if(PRCGetHasSpell(SPELL_DEAFENING_CLANG, oTarget)) return SPELL_DEAFENING_CLANG; + if(PRCGetHasSpell(SPELL_BALAGARNSIRONHORN, oTarget)) return SPELL_BALAGARNSIRONHORN; + if(PRCGetHasSpell(SPELL_BLESS_WEAPON, oTarget)) return SPELL_BLESS_WEAPON; + if(PRCGetHasSpell(SPELL_SHELGARNS_PERSISTENT_BLADE, oTarget)) return SPELL_SHELGARNS_PERSISTENT_BLADE; + if(PRCGetHasSpell(SPELL_NEGATIVE_ENERGY_RAY, oTarget)) return SPELL_NEGATIVE_ENERGY_RAY; + if(PRCGetHasSpell(SPELL_BURNING_HANDS, oTarget)) return SPELL_BURNING_HANDS; + if(PRCGetHasSpell(SPELL_HORIZIKAULS_BOOM, oTarget)) return SPELL_HORIZIKAULS_BOOM; + if(PRCGetHasSpell(SPELL_SHIELD_OF_FAITH, oTarget)) return SPELL_SHIELD_OF_FAITH; + if(PRCGetHasSpell(SPELL_AMPLIFY, oTarget)) return SPELL_AMPLIFY; + if(PRCGetHasSpell(SPELL_TRUE_STRIKE, oTarget)) return SPELL_TRUE_STRIKE; + if(PRCGetHasSpell(SPELL_RAY_OF_ENFEEBLEMENT, oTarget)) return SPELL_RAY_OF_ENFEEBLEMENT; + if(PRCGetHasSpell(SPELL_EXPEDITIOUS_RETREAT, oTarget)) return SPELL_EXPEDITIOUS_RETREAT; + if(PRCGetHasSpell(SPELL_ICE_DAGGER, oTarget)) return SPELL_ICE_DAGGER; + if(PRCGetHasSpell(SPELL_ENTROPIC_SHIELD, oTarget)) return SPELL_ENTROPIC_SHIELD; + if(PRCGetHasSpell(SPELL_ENTANGLE, oTarget)) return SPELL_ENTANGLE; + if(PRCGetHasSpell(SPELL_DIVINE_FAVOR, oTarget)) return SPELL_DIVINE_FAVOR; + if(PRCGetHasSpell(SPELL_FEAR, oTarget)) return SPELL_FEAR; + if(PRCGetHasSpell(SPELL_SLEEP, oTarget)) return SPELL_SLEEP; + if(PRCGetHasSpell(SPELL_SORROW, oTarget)) return SPELL_SORROW; + if(PRCGetHasSpell(SPELL_MAGIC_WEAPON, oTarget)) return SPELL_MAGIC_WEAPON; + if(PRCGetHasSpell(SPELL_SCARE, oTarget)) return SPELL_SCARE; + if(PRCGetHasSpell(SPELL_GREASE, oTarget)) return SPELL_GREASE; + if(PRCGetHasSpell(SPELL_CAMOFLAGE, oTarget)) return SPELL_CAMOFLAGE; + if(PRCGetHasSpell(SPELL_COLOR_SPRAY, oTarget)) return SPELL_COLOR_SPRAY; + if(PRCGetHasSpell(SPELL_RAY_OF_HOPE, oTarget)) return SPELL_RAY_OF_HOPE; + if(PRCGetHasSpell(SPELL_RESIST_ELEMENTS, oTarget)) return SPELL_RESIST_ELEMENTS; + if(PRCGetHasSpell(SPELL_REMOVE_FEAR, oTarget)) return SPELL_REMOVE_FEAR; + if(PRCGetHasSpell(SPELL_IRONGUTS, oTarget)) return SPELL_IRONGUTS; + if(PRCGetHasSpell(SPELL_PROTECTION_FROM_LAW, oTarget)) return SPELL_PROTECTION_FROM_LAW; + if(PRCGetHasSpell(SPELL_PROTECTION_FROM_GOOD, oTarget)) return SPELL_PROTECTION_FROM_GOOD; + if(PRCGetHasSpell(SPELL_PROTECTION__FROM_CHAOS, oTarget)) return SPELL_PROTECTION__FROM_CHAOS; + if(PRCGetHasSpell(SPELL_PROTECTION_FROM_EVIL, oTarget)) return SPELL_PROTECTION_FROM_EVIL; + if(PRCGetHasSpell(SPELL_IDENTIFY, oTarget)) return SPELL_IDENTIFY; + if(PRCGetHasSpell(SPELL_CURE_LIGHT_WOUNDS, oTarget)) return SPELL_CURE_LIGHT_WOUNDS; + if(PRCGetHasSpell(SPELL_INFLICT_LIGHT_WOUNDS, oTarget)) return SPELL_INFLICT_LIGHT_WOUNDS; + if(PRCGetHasSpell(SPELL_EXTRACT_DRUG, oTarget)) return SPELL_EXTRACT_DRUG; + if(PRCGetHasSpell(SPELL_OBSCURE_OBJECT, oTarget)) return SPELL_OBSCURE_OBJECT; + if(PRCGetHasSpell(2839, oTarget)) return 2839; //:: Disguise Self + return nSpell; +} + +int GetBestL2Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_MELFS_ACID_ARROW, oTarget)) return SPELL_MELFS_ACID_ARROW; + if(PRCGetHasSpell(SPELL_BULLS_STRENGTH, oTarget)) return SPELL_BULLS_STRENGTH; + if(PRCGetHasSpell(SPELL_CATS_GRACE, oTarget)) return SPELL_CATS_GRACE; + if(PRCGetHasSpell(SPELL_ENDURANCE, oTarget)) return SPELL_ENDURANCE; + if(PRCGetHasSpell(SPELL_FOXS_CUNNING, oTarget)) return SPELL_FOXS_CUNNING; + if(PRCGetHasSpell(SPELL_EAGLE_SPLEDOR, oTarget)) return SPELL_EAGLE_SPLEDOR; + if(PRCGetHasSpell(SPELL_OWLS_WISDOM, oTarget)) return SPELL_OWLS_WISDOM; + if(PRCGetHasSpell(SPELL_PROTECTION_FROM_ELEMENTS, oTarget)) return SPELL_PROTECTION_FROM_ELEMENTS; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_II, oTarget)) return SPELL_SUMMON_CREATURE_II; + if(PRCGetHasSpell(SPELL_ONE_WITH_THE_LAND, oTarget)) return SPELL_ONE_WITH_THE_LAND; + if(PRCGetHasSpell(SPELL_INVISIBILITY, oTarget)) return SPELL_INVISIBILITY; + if(PRCGetHasSpell(SPELL_CLARITY, oTarget)) return SPELL_CLARITY; + if(PRCGetHasSpell(SPELL_FIND_TRAPS, oTarget)) return SPELL_FIND_TRAPS; + if(PRCGetHasSpell(SPELL_LESSER_RESTORATION, oTarget)) return SPELL_LESSER_RESTORATION; + if(PRCGetHasSpell(SPELL_FLAME_LASH, oTarget)) return SPELL_FLAME_LASH; + if(PRCGetHasSpell(SPELL_FLAME_WEAPON, oTarget)) return SPELL_FLAME_WEAPON; + if(PRCGetHasSpell(SPELL_WEB, oTarget)) return SPELL_WEB; + if(PRCGetHasSpell(SPELL_COMBUST, oTarget)) return SPELL_COMBUST; + if(PRCGetHasSpell(SPELL_GHOUL_TOUCH, oTarget)) return SPELL_GHOUL_TOUCH; + if(PRCGetHasSpell(SPELL_KNOCK, oTarget)) return SPELL_KNOCK; + if(PRCGetHasSpell(SPELL_GHOSTLY_VISAGE, oTarget)) return SPELL_GHOSTLY_VISAGE; + if(PRCGetHasSpell(SPELL_SOUND_BURST, oTarget)) return SPELL_SOUND_BURST; + if(PRCGetHasSpell(SPELL_SILENCE, oTarget)) return SPELL_SILENCE; + if(PRCGetHasSpell(SPELL_SEE_INVISIBILITY, oTarget)) return SPELL_SEE_INVISIBILITY; + if(PRCGetHasSpell(SPELL_HOLD_PERSON, oTarget)) return SPELL_HOLD_PERSON; + if(PRCGetHasSpell(SPELL_GEDLEES_ELECTRIC_LOOP, oTarget)) return SPELL_GEDLEES_ELECTRIC_LOOP; + if(PRCGetHasSpell(SPELL_REMOVE_PARALYSIS, oTarget)) return SPELL_REMOVE_PARALYSIS; + if(PRCGetHasSpell(SPELL_CLOUD_OF_BEWILDERMENT, oTarget)) return SPELL_CLOUD_OF_BEWILDERMENT; + if(PRCGetHasSpell(SPELL_TASHAS_HIDEOUS_LAUGHTER, oTarget)) return SPELL_TASHAS_HIDEOUS_LAUGHTER; + if(PRCGetHasSpell(SPELL_BLOOD_FRENZY, oTarget)) return SPELL_BLOOD_FRENZY; + if(PRCGetHasSpell(SPELL_BLINDNESS_AND_DEAFNESS, oTarget)) return SPELL_BLINDNESS_AND_DEAFNESS; + if(PRCGetHasSpell(SPELL_STONE_BONES, oTarget)) return SPELL_STONE_BONES; + if(PRCGetHasSpell(SPELL_BARKSKIN, oTarget)) return SPELL_BARKSKIN; + if(PRCGetHasSpell(SPELL_DARKVISION, oTarget)) return SPELL_DARKVISION; + if(PRCGetHasSpell(SPELL_DEATH_ARMOR, oTarget)) return SPELL_DEATH_ARMOR; + if(PRCGetHasSpell(SPELL_DARKNESS, oTarget)) return SPELL_DARKNESS; + if(PRCGetHasSpell(SPELL_CHARM_PERSON_OR_ANIMAL, oTarget)) return SPELL_CHARM_PERSON_OR_ANIMAL; + if(PRCGetHasSpell(SPELL_AURAOFGLORY, oTarget)) return SPELL_AURAOFGLORY; + if(PRCGetHasSpell(SPELL_HOLD_ANIMAL, oTarget)) return SPELL_HOLD_ANIMAL; + if(PRCGetHasSpell(SPELL_AID, oTarget)) return SPELL_AID; + if(PRCGetHasSpell(SPELL_CONTINUAL_FLAME, oTarget)) return SPELL_CONTINUAL_FLAME; + if(PRCGetHasSpell(SPELL_CURE_MODERATE_WOUNDS, oTarget)) return SPELL_CURE_MODERATE_WOUNDS; + if(PRCGetHasSpell(SPELL_INFLICT_MODERATE_WOUNDS, oTarget)) return SPELL_INFLICT_MODERATE_WOUNDS; + return nSpell; +} + +int GetBestL3Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_FLAME_ARROW, oTarget)) return SPELL_FLAME_ARROW; + if(PRCGetHasSpell(SPELL_CALL_LIGHTNING, oTarget)) return SPELL_CALL_LIGHTNING; + if(PRCGetHasSpell(SPELL_FIREBALL, oTarget)) return SPELL_FIREBALL; + if(PRCGetHasSpell(SPELL_DISPLACEMENT, oTarget)) return SPELL_DISPLACEMENT; + if(PRCGetHasSpell(SPELL_DISPEL_MAGIC, oTarget)) return SPELL_DISPEL_MAGIC; + if(PRCGetHasSpell(SPELL_HASTE, oTarget)) return SPELL_HASTE; + if(PRCGetHasSpell(SPELL_SLOW, oTarget)) return SPELL_SLOW; + if(PRCGetHasSpell(SPELL_VAMPIRIC_TOUCH, oTarget)) return SPELL_VAMPIRIC_TOUCH; + if(PRCGetHasSpell(SPELL_SEARING_LIGHT, oTarget)) return SPELL_SEARING_LIGHT; + if(PRCGetHasSpell(SPELL_SCINTILLATING_SPHERE, oTarget)) return SPELL_SCINTILLATING_SPHERE; + if(PRCGetHasSpell(SPELL_MESTILS_ACID_BREATH, oTarget)) return SPELL_MESTILS_ACID_BREATH; + if(PRCGetHasSpell(SPELL_MAGIC_CIRCLE_AGAINST_LAW, oTarget)) return SPELL_MAGIC_CIRCLE_AGAINST_LAW; + if(PRCGetHasSpell(SPELL_MAGIC_CIRCLE_AGAINST_GOOD, oTarget)) return SPELL_MAGIC_CIRCLE_AGAINST_GOOD; + if(PRCGetHasSpell(SPELL_MAGIC_CIRCLE_AGAINST_EVIL, oTarget)) return SPELL_MAGIC_CIRCLE_AGAINST_EVIL; + if(PRCGetHasSpell(SPELL_MAGIC_CIRCLE_AGAINST_CHAOS, oTarget)) return SPELL_MAGIC_CIRCLE_AGAINST_CHAOS; + if(PRCGetHasSpell(SPELL_LIGHTNING_BOLT, oTarget)) return SPELL_LIGHTNING_BOLT; + if(PRCGetHasSpell(SPELL_NEGATIVE_ENERGY_BURST, oTarget)) return SPELL_NEGATIVE_ENERGY_BURST; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_III, oTarget)) return SPELL_SUMMON_CREATURE_III; + if(PRCGetHasSpell(SPELL_KEEN_EDGE, oTarget)) return SPELL_KEEN_EDGE; + if(PRCGetHasSpell(SPELL_MAGIC_VESTMENT, oTarget)) return SPELL_MAGIC_VESTMENT; + if(PRCGetHasSpell(SPELL_DOMINATE_ANIMAL, oTarget)) return SPELL_DOMINATE_ANIMAL; + if(PRCGetHasSpell(SPELL_GLYPH_OF_WARDING, oTarget)) return SPELL_GLYPH_OF_WARDING; + if(PRCGetHasSpell(SPELL_INVISIBILITY_SPHERE, oTarget)) return SPELL_INVISIBILITY_SPHERE; + if(PRCGetHasSpell(SPELL_INVISIBILITY_PURGE, oTarget)) return SPELL_INVISIBILITY_PURGE; + if(PRCGetHasSpell(SPELL_FEAR, oTarget)) return SPELL_FEAR; + if(PRCGetHasSpell(SPELL_BLADE_THIRST, oTarget)) return SPELL_BLADE_THIRST; + if(PRCGetHasSpell(SPELL_GREATER_MAGIC_WEAPON, oTarget)) return SPELL_GREATER_MAGIC_WEAPON; + if(PRCGetHasSpell(SPELL_POISON, oTarget)) return SPELL_POISON; + if(PRCGetHasSpell(SPELL_STINKING_CLOUD, oTarget)) return SPELL_STINKING_CLOUD; + if(PRCGetHasSpell(SPELL_SPIKE_GROWTH, oTarget)) return SPELL_SPIKE_GROWTH; + if(PRCGetHasSpell(SPELL_WOUNDING_WHISPERS, oTarget)) return SPELL_WOUNDING_WHISPERS; + if(PRCGetHasSpell(SPELL_QUILLFIRE, oTarget)) return SPELL_QUILLFIRE; + if(PRCGetHasSpell(SPELL_GREATER_MAGIC_FANG, oTarget)) return SPELL_GREATER_MAGIC_FANG; + if(PRCGetHasSpell(SPELL_GUST_OF_WIND, oTarget)) return SPELL_GUST_OF_WIND; + if(PRCGetHasSpell(SPELL_INFESTATION_OF_MAGGOTS, oTarget)) return SPELL_INFESTATION_OF_MAGGOTS; + if(PRCGetHasSpell(SPELL_ANIMATE_DEAD, oTarget)) return SPELL_ANIMATE_DEAD; + if(PRCGetHasSpell(SPELL_NEUTRALIZE_POISON, oTarget)) return SPELL_NEUTRALIZE_POISON; + if(PRCGetHasSpell(SPELL_NEGATIVE_ENERGY_PROTECTION, oTarget)) return SPELL_NEGATIVE_ENERGY_PROTECTION; + if(PRCGetHasSpell(SPELL_CONTAGION, oTarget)) return SPELL_CONTAGION; + if(PRCGetHasSpell(SPELL_HEALING_STING, oTarget)) return SPELL_HEALING_STING; + if(PRCGetHasSpell(SPELL_REMOVE_DISEASE, oTarget)) return SPELL_REMOVE_DISEASE; + if(PRCGetHasSpell(SPELL_REMOVE_CURSE, oTarget)) return SPELL_REMOVE_CURSE; + if(PRCGetHasSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, oTarget)) return SPELL_REMOVE_BLINDNESS_AND_DEAFNESS; + if(PRCGetHasSpell(SPELL_CONFUSION, oTarget)) return SPELL_CONFUSION; + if(PRCGetHasSpell(SPELL_PRAYER, oTarget)) return SPELL_PRAYER; + if(PRCGetHasSpell(SPELL_DARKFIRE, oTarget)) return SPELL_DARKFIRE; + if(PRCGetHasSpell(SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE, oTarget)) return SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE; + if(PRCGetHasSpell(SPELL_CHARM_MONSTER, oTarget)) return SPELL_CHARM_MONSTER; + if(PRCGetHasSpell(SPELL_BESTOW_CURSE, oTarget)) return SPELL_BESTOW_CURSE; + if(PRCGetHasSpell(SPELL_CURE_SERIOUS_WOUNDS, oTarget)) return SPELL_CURE_SERIOUS_WOUNDS; + if(PRCGetHasSpell(SPELL_INFLICT_SERIOUS_WOUNDS, oTarget)) return SPELL_INFLICT_SERIOUS_WOUNDS; + return nSpell; +} + +int GetBestL4Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_ISAACS_LESSER_MISSILE_STORM, oTarget)) return SPELL_ISAACS_LESSER_MISSILE_STORM; + if(PRCGetHasSpell(SPELL_STONESKIN, oTarget)) return SPELL_STONESKIN; + if(PRCGetHasSpell(SPELL_DOMINATE_PERSON, oTarget)) return SPELL_DOMINATE_PERSON; + if(PRCGetHasSpell(SPELL_LESSER_SPELL_BREACH, oTarget)) return SPELL_LESSER_SPELL_BREACH; + if(PRCGetHasSpell(SPELL_ELEMENTAL_SHIELD, oTarget)) return SPELL_ELEMENTAL_SHIELD; + if(PRCGetHasSpell(SPELL_IMPROVED_INVISIBILITY, oTarget)) return SPELL_IMPROVED_INVISIBILITY; + if(PRCGetHasSpell(SPELL_ICE_STORM, oTarget)) return SPELL_ICE_STORM; + if(PRCGetHasSpell(SPELL_HAMMER_OF_THE_GODS, oTarget)) return SPELL_HAMMER_OF_THE_GODS; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_IV, oTarget)) return SPELL_SUMMON_CREATURE_IV; + if(PRCGetHasSpell(SPELL_EVARDS_BLACK_TENTACLES, oTarget)) return SPELL_EVARDS_BLACK_TENTACLES; + if(PRCGetHasSpell(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oTarget)) return SPELL_MINOR_GLOBE_OF_INVULNERABILITY; + if(PRCGetHasSpell(SPELL_LEGEND_LORE, oTarget)) return SPELL_LEGEND_LORE; + if(PRCGetHasSpell(SPELL_POLYMORPH_SELF, oTarget)) return SPELL_POLYMORPH_SELF; + if(PRCGetHasSpell(SPELL_PHANTASMAL_KILLER, oTarget)) return SPELL_PHANTASMAL_KILLER; + if(PRCGetHasSpell(SPELL_DIVINE_POWER, oTarget)) return SPELL_DIVINE_POWER; + if(PRCGetHasSpell(SPELL_DEATH_WARD, oTarget)) return SPELL_DEATH_WARD; + if(PRCGetHasSpell(SPELL_FREEDOM_OF_MOVEMENT, oTarget)) return SPELL_FREEDOM_OF_MOVEMENT; + if(PRCGetHasSpell(SPELL_WAR_CRY, oTarget)) return SPELL_WAR_CRY; + if(PRCGetHasSpell(SPELL_WALL_OF_FIRE, oTarget)) return SPELL_WALL_OF_FIRE; + if(PRCGetHasSpell(SPELL_RESTORATION, oTarget)) return SPELL_RESTORATION; + if(PRCGetHasSpell(SPELL_MASS_CAMOFLAGE, oTarget)) return SPELL_MASS_CAMOFLAGE; + if(PRCGetHasSpell(SPELL_ENERVATION, oTarget)) return SPELL_ENERVATION; + if(PRCGetHasSpell(SPELL_HOLY_SWORD, oTarget)) return SPELL_HOLY_SWORD; + if(PRCGetHasSpell(SPELL_HOLD_MONSTER, oTarget)) return SPELL_HOLD_MONSTER; + if(PRCGetHasSpell(SPELL_DISMISSAL, oTarget)) return SPELL_DISMISSAL; + if(PRCGetHasSpell(SPELL_INFLICT_CRITICAL_WOUNDS, oTarget)) return SPELL_INFLICT_CRITICAL_WOUNDS; + if(PRCGetHasSpell(SPELL_CURE_CRITICAL_WOUNDS, oTarget)) return SPELL_CURE_CRITICAL_WOUNDS; + return nSpell; +} + +int GetBestL5Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_TRUE_SEEING, oTarget)) return SPELL_TRUE_SEEING; + if(PRCGetHasSpell(SPELL_BIGBYS_INTERPOSING_HAND, oTarget)) return SPELL_BIGBYS_INTERPOSING_HAND; + if(PRCGetHasSpell(SPELL_GREATER_DISPELLING, oTarget)) return SPELL_GREATER_DISPELLING; + if(PRCGetHasSpell(SPELL_LESSER_SPELL_MANTLE, oTarget)) return SPELL_LESSER_SPELL_MANTLE; + if(PRCGetHasSpell(SPELL_SPELL_RESISTANCE, oTarget)) return SPELL_SPELL_RESISTANCE; + if(PRCGetHasSpell(SPELL_MONSTROUS_REGENERATION, oTarget)) return SPELL_MONSTROUS_REGENERATION; + if(PRCGetHasSpell(SPELL_RAISE_DEAD, oTarget)) return SPELL_RAISE_DEAD; + if(PRCGetHasSpell(SPELL_MIND_FOG, oTarget)) return SPELL_MIND_FOG; + if(PRCGetHasSpell(SPELL_SLAY_LIVING, oTarget)) return SPELL_SLAY_LIVING; + if(PRCGetHasSpell(SPELL_LESSER_PLANAR_BINDING, oTarget)) return SPELL_LESSER_PLANAR_BINDING; + if(PRCGetHasSpell(SPELL_LESSER_MIND_BLANK, oTarget)) return SPELL_LESSER_MIND_BLANK; + if(PRCGetHasSpell(SPELL_FLAME_STRIKE, oTarget)) return SPELL_FLAME_STRIKE; + if(PRCGetHasSpell(SPELL_FIREBRAND, oTarget)) return SPELL_FIREBRAND; + if(PRCGetHasSpell(SPELL_INFERNO, oTarget)) return SPELL_INFERNO; + if(PRCGetHasSpell(SPELL_CONE_OF_COLD, oTarget)) return SPELL_CONE_OF_COLD; + if(PRCGetHasSpell(SPELL_BALL_LIGHTNING, oTarget)) return SPELL_BALL_LIGHTNING; + if(PRCGetHasSpell(SPELL_ENERGY_BUFFER, oTarget)) return SPELL_ENERGY_BUFFER; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_V, oTarget)) return SPELL_SUMMON_CREATURE_V; + if(PRCGetHasSpell(SPELL_MESTILS_ACID_SHEATH, oTarget)) return SPELL_MESTILS_ACID_SHEATH; + if(PRCGetHasSpell(SPELL_FEEBLEMIND, oTarget)) return SPELL_FEEBLEMIND; + if(PRCGetHasSpell(SPELL_ETHEREAL_VISAGE, oTarget)) return SPELL_ETHEREAL_VISAGE; + if(PRCGetHasSpell(SPELL_VINE_MINE, oTarget)) return SPELL_VINE_MINE; + if(PRCGetHasSpell(SPELL_BATTLETIDE, oTarget)) return SPELL_BATTLETIDE; + if(PRCGetHasSpell(SPELL_CLOUDKILL, oTarget)) return SPELL_CLOUDKILL; + if(PRCGetHasSpell(SPELL_AWAKEN, oTarget)) return SPELL_AWAKEN; + if(PRCGetHasSpell(SPELL_HEALING_CIRCLE, oTarget)) return SPELL_HEALING_CIRCLE; + if(PRCGetHasSpell(SPELL_CIRCLE_OF_DOOM, oTarget)) return SPELL_CIRCLE_OF_DOOM; + return nSpell; +} + +int GetBestL6Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_ISAACS_GREATER_MISSILE_STORM, oTarget)) return SPELL_ISAACS_GREATER_MISSILE_STORM; + if(PRCGetHasSpell(SPELL_BIGBYS_FORCEFUL_HAND, oTarget)) return SPELL_BIGBYS_FORCEFUL_HAND; + if(PRCGetHasSpell(SPELL_CHAIN_LIGHTNING, oTarget)) return SPELL_CHAIN_LIGHTNING; + if(PRCGetHasSpell(SPELL_MASS_HASTE, oTarget)) return SPELL_MASS_HASTE; + if(PRCGetHasSpell(SPELL_DROWN, oTarget)) return SPELL_DROWN; + if(PRCGetHasSpell(SPELL_GREATER_STONESKIN, oTarget)) return SPELL_GREATER_STONESKIN; + if(PRCGetHasSpell(SPELL_GREATER_SPELL_BREACH, oTarget)) return SPELL_GREATER_SPELL_BREACH; + if(PRCGetHasSpell(SPELL_CIRCLE_OF_DEATH, oTarget)) return SPELL_CIRCLE_OF_DEATH; + if(PRCGetHasSpell(SPELL_GLOBE_OF_INVULNERABILITY, oTarget)) return SPELL_GLOBE_OF_INVULNERABILITY; + if(PRCGetHasSpell(SPELL_UNDEATH_TO_DEATH, oTarget)) return SPELL_UNDEATH_TO_DEATH; + if(PRCGetHasSpell(SPELL_CRUMBLE, oTarget)) return SPELL_CRUMBLE; + if(PRCGetHasSpell(SPELL_REGENERATE, oTarget)) return SPELL_REGENERATE; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_VI, oTarget)) return SPELL_SUMMON_CREATURE_VI; + if(PRCGetHasSpell(SPELL_STONEHOLD, oTarget)) return SPELL_STONEHOLD; + if(PRCGetHasSpell(SPELL_FLESH_TO_STONE, oTarget)) return SPELL_FLESH_TO_STONE; + if(PRCGetHasSpell(SPELL_STONE_TO_FLESH, oTarget)) return SPELL_STONE_TO_FLESH; + if(PRCGetHasSpell(SPELL_TENSERS_TRANSFORMATION, oTarget)) return SPELL_TENSERS_TRANSFORMATION; + if(PRCGetHasSpell(SPELL_CREATE_UNDEAD, oTarget)) return SPELL_CREATE_UNDEAD; + if(PRCGetHasSpell(SPELL_CONTROL_UNDEAD, oTarget)) return SPELL_CONTROL_UNDEAD; + if(PRCGetHasSpell(SPELL_PLANAR_BINDING, oTarget)) return SPELL_PLANAR_BINDING; + if(PRCGetHasSpell(SPELL_PLANAR_ALLY, oTarget)) return SPELL_PLANAR_ALLY; + if(PRCGetHasSpell(SPELL_DIRGE, oTarget)) return SPELL_DIRGE; + if(PRCGetHasSpell(SPELL_BLADE_BARRIER, oTarget)) return SPELL_BLADE_BARRIER; + if(PRCGetHasSpell(SPELL_BANISHMENT, oTarget)) return SPELL_BANISHMENT; + if(PRCGetHasSpell(SPELL_ACID_FOG, oTarget)) return SPELL_ACID_FOG; + if(PRCGetHasSpell(SPELL_HEAL, oTarget)) return SPELL_HEAL; + if(PRCGetHasSpell(SPELL_HARM, oTarget)) return SPELL_HARM; + return nSpell; +} + +int GetBestL7Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_SPELL_MANTLE, oTarget)) return SPELL_SPELL_MANTLE; + if(PRCGetHasSpell(SPELL_BIGBYS_GRASPING_HAND, oTarget)) return SPELL_BIGBYS_GRASPING_HAND; + if(PRCGetHasSpell(SPELL_FIRE_STORM, oTarget)) return SPELL_FIRE_STORM; + if(PRCGetHasSpell(SPELL_FINGER_OF_DEATH, oTarget)) return SPELL_FINGER_OF_DEATH; + if(PRCGetHasSpell(SPELL_PROTECTION_FROM_SPELLS, oTarget)) return SPELL_PROTECTION_FROM_SPELLS; + if(PRCGetHasSpell(SPELL_WORD_OF_FAITH, oTarget)) return SPELL_WORD_OF_FAITH; + if(PRCGetHasSpell(SPELL_SHADOW_SHIELD, oTarget)) return SPELL_SHADOW_SHIELD; + if(PRCGetHasSpell(SPELL_CREEPING_DOOM, oTarget)) return SPELL_CREEPING_DOOM; + if(PRCGetHasSpell(SPELL_DESTRUCTION, oTarget)) return SPELL_DESTRUCTION; + if(PRCGetHasSpell(SPELL_PRISMATIC_SPRAY, oTarget)) return SPELL_PRISMATIC_SPRAY; + if(PRCGetHasSpell(SPELL_DELAYED_BLAST_FIREBALL, oTarget)) return SPELL_DELAYED_BLAST_FIREBALL; + if(PRCGetHasSpell(SPELL_GREAT_THUNDERCLAP, oTarget)) return SPELL_GREAT_THUNDERCLAP; + if(PRCGetHasSpell(SPELL_POWER_WORD_STUN, oTarget)) return SPELL_POWER_WORD_STUN; + if(PRCGetHasSpell(SPELL_MORDENKAINENS_SWORD, oTarget)) return SPELL_MORDENKAINENS_SWORD; + if(PRCGetHasSpell(SPELL_RESURRECTION, oTarget)) return SPELL_RESURRECTION; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_VII, oTarget)) return SPELL_SUMMON_CREATURE_VII; + if(PRCGetHasSpell(SPELL_AURA_OF_VITALITY, oTarget)) return SPELL_AURA_OF_VITALITY; + if(PRCGetHasSpell(SPELL_GREATER_RESTORATION, oTarget)) return SPELL_GREATER_RESTORATION; + return nSpell; +} + +int GetBestL8Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_BIGBYS_CLENCHED_FIST, oTarget)) return SPELL_BIGBYS_CLENCHED_FIST; + if(PRCGetHasSpell(SPELL_HORRID_WILTING, oTarget)) return SPELL_HORRID_WILTING; + if(PRCGetHasSpell(SPELL_EARTHQUAKE, oTarget)) return SPELL_EARTHQUAKE; + if(PRCGetHasSpell(SPELL_NATURES_BALANCE, oTarget)) return SPELL_NATURES_BALANCE; + if(PRCGetHasSpell(SPELL_INCENDIARY_CLOUD, oTarget)) return SPELL_INCENDIARY_CLOUD; + if(PRCGetHasSpell(SPELL_MIND_BLANK, oTarget)) return SPELL_MIND_BLANK; + if(PRCGetHasSpell(SPELL_PREMONITION, oTarget)) return SPELL_PREMONITION; + if(PRCGetHasSpell(SPELL_SUNBURST, oTarget)) return SPELL_SUNBURST; + if(PRCGetHasSpell(SPELL_SUNBEAM, oTarget)) return SPELL_SUNBEAM; + if(PRCGetHasSpell(SPELL_MASS_CHARM, oTarget)) return SPELL_MASS_CHARM; + if(PRCGetHasSpell(SPELL_MASS_BLINDNESS_AND_DEAFNESS, oTarget)) return SPELL_MASS_BLINDNESS_AND_DEAFNESS; + if(PRCGetHasSpell(SPELL_BOMBARDMENT, oTarget)) return SPELL_BOMBARDMENT; + if(PRCGetHasSpell(SPELL_GREATER_PLANAR_BINDING, oTarget)) return SPELL_GREATER_PLANAR_BINDING; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_VIII, oTarget)) return SPELL_SUMMON_CREATURE_VIII; + if(PRCGetHasSpell(SPELL_CREATE_GREATER_UNDEAD, oTarget)) return SPELL_CREATE_GREATER_UNDEAD; + if(PRCGetHasSpell(SPELL_BLACKSTAFF, oTarget)) return SPELL_BLACKSTAFF; + if(PRCGetHasSpell(SPELL_MASS_HEAL, oTarget)) return SPELL_MASS_HEAL; + return nSpell; +} + +int GetBestL9Spell(object oTarget, int nSpell) +{ + if(PRCGetHasSpell(SPELL_TIME_STOP, oTarget)) return SPELL_TIME_STOP; + if(PRCGetHasSpell(SPELL_BLACK_BLADE_OF_DISASTER, oTarget)) return SPELL_BLACK_BLADE_OF_DISASTER; + if(PRCGetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION, oTarget)) return SPELL_MORDENKAINENS_DISJUNCTION; + if(PRCGetHasSpell(SPELL_GREATER_SPELL_MANTLE, oTarget)) return SPELL_GREATER_SPELL_MANTLE; + if(PRCGetHasSpell(SPELL_BIGBYS_CRUSHING_HAND, oTarget)) return SPELL_BIGBYS_CRUSHING_HAND; + if(PRCGetHasSpell(SPELL_WAIL_OF_THE_BANSHEE, oTarget)) return SPELL_WAIL_OF_THE_BANSHEE; + if(PRCGetHasSpell(SPELL_WEIRD, oTarget)) return SPELL_WEIRD; + if(PRCGetHasSpell(SPELL_METEOR_SWARM, oTarget)) return SPELL_METEOR_SWARM; + if(PRCGetHasSpell(SPELL_IMPLOSION, oTarget)) return SPELL_IMPLOSION; + if(PRCGetHasSpell(SPELL_POWER_WORD_KILL, oTarget)) return SPELL_POWER_WORD_KILL; + if(PRCGetHasSpell(SPELL_STORM_OF_VENGEANCE, oTarget)) return SPELL_STORM_OF_VENGEANCE; + if(PRCGetHasSpell(SPELL_SHAPECHANGE, oTarget)) return SPELL_SHAPECHANGE; + if(PRCGetHasSpell(SPELL_DOMINATE_MONSTER, oTarget)) return SPELL_DOMINATE_MONSTER; + if(PRCGetHasSpell(SPELL_ELEMENTAL_SWARM, oTarget)) return SPELL_ELEMENTAL_SWARM; + if(PRCGetHasSpell(SPELL_SUMMON_CREATURE_IX, oTarget)) return SPELL_SUMMON_CREATURE_IX; + if(PRCGetHasSpell(SPELL_GATE, oTarget)) return SPELL_GATE; + if(PRCGetHasSpell(SPELL_ENERGY_DRAIN, oTarget)) return SPELL_ENERGY_DRAIN; + if(PRCGetHasSpell(SPELL_UNDEATHS_ETERNAL_FOE, oTarget)) return SPELL_UNDEATHS_ETERNAL_FOE; + return nSpell; +} + +int GetBestAvailableSpell(object oTarget) +{ + int nBestSpell = GetBestL9Spell(oTarget, 99999); + if(nBestSpell == 99999) nBestSpell = GetBestL8Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL7Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL6Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL5Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL4Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL3Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL2Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL1Spell(oTarget, nBestSpell); + if(nBestSpell == 99999) nBestSpell = GetBestL0Spell(oTarget, nBestSpell); + return nBestSpell; +} + diff --git a/src/include/prc_inc_actions.nss b/src/include/prc_inc_actions.nss new file mode 100644 index 0000000..f8117d3 --- /dev/null +++ b/src/include/prc_inc_actions.nss @@ -0,0 +1,142 @@ +//:://///////////////////////////////////////////// +//:: Actions include +//:: prc_inc_actions +//:://///////////////////////////////////////////// +/** @file + Defines functions related to use of actions. + + @author Ornedan + @date Created - 2006.01.26 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/// A marker local the presence of which indicates that a creature's +/// available swift action has been used for a particular round +const string PRC_SWIFT_ACTION_MARKER = "PRC_SwiftActionUsed"; + +/// A marker local the presence of which indicates that a creature's +/// available move action has been used for a particular round +const string PRC_MOVE_ACTION_MARKER = "PRC_MoveActionUsed"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines whether the given creature can use a swift action at this moment + * and if it can, marks that action as used. + * Conditions are that it has not used a swift action already in the last + * 6 seconds, and that that GetCanTakeFreeAction() returns TRUE. + * In addition, if a PC, it must be commandable by the player. This is in order + * to prevent abuse of instant action feats. + * + * @param oCreature Creature whose eligibility to use a swift action to test. + * @return TRUE if the creature can take a swift action now, FALSE otherwise. + */ +int TakeSwiftAction(object oCreature); + +/** + * Determines whether the given creature can use a free action at this moment. + * Condition is that it is not affected by a condition that would prevent + * action queue processing. + * If the bTestControllability is set to true, this will also test for + * conditions that would prevent control by the player. + * + * @param oCreature Creature whose eligibility to use a free action to test. + * @param bTestControllability Whether to test for controllability affecting conditions. + * @return TRUE if the creature can take a free action now, FALSE otherwise. + */ +int GetCanTakeFreeAction(object oCreature, int bTestControllability = FALSE); + +/** + * Determines whether the given creature can use a move action at this moment + * and if it can, marks that action as used. + * Conditions are that it has not used a move action already in the last + * 6 seconds + * In addition, if a PC, it must be commandable by the player. This is in order + * to prevent abuse of instant action feats. + * + * @param oCreature Creature whose eligibility to use a move action to test. + * @return TRUE if the creature can take a move action now, FALSE otherwise. + */ +int TakeMoveAction(object oCreature); + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int TakeSwiftAction(object oCreature) +{ + if(!GetLocalInt(oCreature, PRC_SWIFT_ACTION_MARKER) && + GetCanTakeFreeAction(oCreature, GetIsPC(oCreature)) && + GetCommandable(oCreature) + ) + { + // Mark swift action used up for this round + SetLocalInt(oCreature, PRC_SWIFT_ACTION_MARKER, TRUE); + DelayCommand(6.0f, DeleteLocalInt(oCreature, PRC_SWIFT_ACTION_MARKER)); + + // Can use swift action + return TRUE; + } + + // Can't use swift action + FloatingTextStringOnCreature("You have already used your swift action this round.", oCreature, FALSE); + return FALSE; +} + +int GetCanTakeFreeAction(object oCreature, int bTestControllability = FALSE) +{ + effect eTest = GetFirstEffect(oCreature); + int nEType; + while(GetIsEffectValid(eTest)) + { + nEType = GetEffectType(eTest); + if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE || + nEType == EFFECT_TYPE_DAZED || + nEType == EFFECT_TYPE_PARALYZE || + nEType == EFFECT_TYPE_PETRIFY || + nEType == EFFECT_TYPE_SLEEP || + nEType == EFFECT_TYPE_STUNNED || + (bTestControllability && + (nEType == EFFECT_TYPE_CHARMED || + nEType == EFFECT_TYPE_CONFUSED || + nEType == EFFECT_TYPE_DOMINATED || + nEType == EFFECT_TYPE_FRIGHTENED || + nEType == EFFECT_TYPE_TURNED + ) + ) + ) + return FALSE; + + // Get next effect + eTest = GetNextEffect(oCreature); + } + + return TRUE; +} + +int TakeMoveAction(object oCreature) +{ + if(!GetLocalInt(oCreature, PRC_MOVE_ACTION_MARKER) && GetCommandable(oCreature)) + { + // Mark move action used up for this round + SetLocalInt(oCreature, PRC_MOVE_ACTION_MARKER, TRUE); + DelayCommand(6.0f, DeleteLocalInt(oCreature, PRC_MOVE_ACTION_MARKER)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSlow(), oCreature, 4.0); + + // Can use move action + return TRUE; + } + + // Can't use move action + FloatingTextStringOnCreature("You have already used your move action this round.", oCreature, FALSE); + return FALSE; +} \ No newline at end of file diff --git a/src/include/prc_inc_array.nss b/src/include/prc_inc_array.nss new file mode 100644 index 0000000..d4367a6 --- /dev/null +++ b/src/include/prc_inc_array.nss @@ -0,0 +1,568 @@ +//:://///////////////////////////////////////////// +//:: Array simulation include +//:: prc_inc_array +//::////////////////////////////////////////////// +/** @file + Array simulation include + + This file defines a set of functions for creating + and manipulating arrays, which are implemented as + local variables on some holder object. + + + Notes: + + * Arrays are dynamic and may be increased in size by just _set_'ing a new + element + * There are no restrictions on what is in the array (can have multiple + types in the same array) + * Arrays start at index 0 + + //////////////////////////////////////////////////////////////////////////////// + // (c) Mr. Figglesworth 2002 + // This code is licensed under beerware. You are allowed to freely use it + // and modify it in any way. Your only two obligations are: (1) at your option, + // to buy the author a beer if you ever meet him; and (2) include the + // copyright notice and license in any redistribution of this code or + // alterations of it. + // + // Full credit for how the array gets implemented goes to the guy who wrote + // the article and posted it on the NWNVault (I couldn't find your article + // to give you credit :( ). And, of course, to bioware. Great job! + //////////////////////////////////////////////////////////////////////////////// +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +/** + * Creates a new array on the given storage object. If an + * array with the same name already exists, the function + * errors. + * + * @param store The object to use as holder for the array + * @param name The name of the array + * @return SDL_SUCCESS if the array was successfully created, + * one of SDL_ERROR_* on error. + */ +int array_create(object store, string name); + +/** + * Deletes an array, erasing all it's entries. + * + * @param store The object which holds the array to delete + * @param name The name of the array + * @return SDL_SUCCESS if the array was successfully deleted, + * one of SDL_ERROR_* on error + */ +int array_delete(object store, string name); + +/** + * Stores a string in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the string at + * @param entry The string to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_string(object store, string name, int i, string entry); + +/** + * Stores an integer in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the integer at + * @param entry The integer to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_int(object store, string name, int i, int entry); + +/** + * Stores a float in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the float at + * @param entry The float to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_float(object store, string name, int i, float entry); + +/** + * Stores an object in an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to store the object at + * @param entry The object to store + * @return SDL_SUCCESS if the store was successfull, SDL_ERROR_* on error. + */ +int array_set_object(object store, string name, int i, object entry); + +/** + * Gets a string from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the string from + * @return The value contained at the index on success, + * "" on error + */ +string array_get_string(object store, string name, int i); + +/** + * Gets an integer from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the integer from + * @return The value contained at the index on success, + * 0 on error + */ +int array_get_int(object store, string name, int i); + +/** + * Gets a float from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the float from + * @return The value contained at the index on success, + * 0.0f on error + */ +float array_get_float(object store, string name, int i); + +/** + * Gets an object from an array. + * + * @param store The object holding the array + * @param name The name of the array + * @param i The index to retrieve the object from + * @return The value contained at the index on success, + * OBJECT_INVALID on error + */ +object array_get_object(object store, string name, int i); + +/** + * Removes all entries in the array with indexes greater than or equal to + * the new size and sets the array size to be equal to the new size. + * + * @param store The object holding the array + * @param name The name of the array + * @param size_new The new number of entries in the array + * @return SDL_SUCCESS on successful resize, SDL_ERROR_* on + * error + */ +int array_shrink(object store, string name, int size_new); + +/** + * Gets the current size of the array. This is one greater + * than the index of highest indexed element the array + * has contained since the last array_shrink or the new size + * specified by the last array_shrink, whichever is greater. + * + * @param store The object holding the array + * @param name The name of the array + * @return The size of the array, or -1 if the specified + * array does not exist. + */ +int array_get_size(object store, string name); + +/** + * Checks whether the given array exists. + * + * @param store The object holding the array + * @param name The name of the array + * @return TRUE if the array exists, FALSE otherwise. + */ +int array_exists(object store, string name); + +/* These need to be rewritten and made less bug-prone before being taken into use. + Preferrably not necessarily have it be fucking massive square matrix, but instead + store a separate length for each x row. + +int array_2d_create(object store, string name); +int array_2d_delete(object store, string name); + +int array_2d_set_string(object store, string name, int i, int j, string entry); +int array_2d_set_int(object store, string name, int i, int j, int entry); +int array_2d_set_float(object store, string name, int i, int j, float entry); +int array_2d_set_object(object store, string name, int i, int j, object entry); + +// returns "" or 0 on error +string array_2d_get_string(object store, string name, int i, int j); +int array_2d_get_int(object store, string name, int i, int j); +float array_2d_get_float(object store, string name, int i, int j); +object array_2d_get_object(object store, string name, int i, int j); + +// changes memory usage of array (deletes x[ > size_new]) +int array_2d_shrink(object store, string name, int size_new, int axis); + +// gets current maximum size of array +int array_2d_get_size(object store, string name, int axis); + +int array_2d_exists(object store, string name); +*/ + +///////////////////////////////////// +// Error Returns +///////////////////////////////////// + +const int SDL_SUCCESS = 1; +const int SDL_ERROR = 1000; +const int SDL_ERROR_ALREADY_EXISTS = 1001; +const int SDL_ERROR_DOES_NOT_EXIST = 1002; +const int SDL_ERROR_OUT_OF_BOUNDS = 1003; +const int SDL_ERROR_NO_ZERO_SIZE = 1004; // Not used - Ornedan 2006.09.15 +const int SDL_ERROR_NOT_VALID_OBJECT = 1005; +const int SDL_ERROR_INVALID_PARAMETER = 1006; + + +///////////////////////////////////// +// Implementation +///////////////////////////////////// + +int array_create(object store, string name) +{ + // error checking + if(!GetIsObjectValid(store)) + return SDL_ERROR_NOT_VALID_OBJECT; + else if(array_exists(store, name)) + return SDL_ERROR_ALREADY_EXISTS; + else + { + // Initialize the size (always one greater than the actual size) + SetLocalInt(store,name,1); + return SDL_SUCCESS; + } +} + +void array_delete_loop(object store, string name, int nMin, int nMax) +{ + int i = nMin; + while(i < nMin + 250 && i < nMax) + { + DeleteLocalString(store, name+"_"+IntToString(i)); + + // just in case, delete possible object names + DeleteLocalObject(store, name+"_"+IntToString(i)+"_OBJECT"); + i++; + } + // delay continuation to avoid TMI + if(i < nMax) + DelayCommand(0.0, array_delete_loop(store, name, i, nMax)); +} + +int array_delete(object store, string name) +{ + // error checking + int size=GetLocalInt(store,name); + if (size==0) + return SDL_ERROR_DOES_NOT_EXIST; + + array_delete_loop(store, name, 0, size+5); + + DeleteLocalInt(store,name); + + return SDL_SUCCESS; +} + +int array_set_string(object store, string name, int i, string entry) +{ + int size = GetLocalInt(store,name); + if(size == 0) + return SDL_ERROR_DOES_NOT_EXIST; + if(i < 0) + return SDL_ERROR_OUT_OF_BOUNDS; + + SetLocalString(store,name+"_"+IntToString(i),entry); + + // save size if we've enlarged it + if(i+2>size) + SetLocalInt(store,name,i+2); + + return SDL_SUCCESS; +} + +int array_set_int(object store, string name, int i, int entry) +{ + return array_set_string(store,name,i,IntToString(entry)); +} + +int array_set_float(object store, string name, int i, float entry) +{ + return array_set_string(store,name,i,FloatToString(entry)); +} + +int array_set_object(object store, string name, int i, object entry) +{ + int results = array_set_string(store, name, i, "OBJECT"); + if (results == SDL_SUCCESS) + SetLocalObject(store, name + "_" + IntToString(i) + "_OBJECT", entry); + + return results; +} + +string array_get_string(object store, string name, int i) +{ + // error checking + int size=GetLocalInt(store,name); + if (size==0 || i>size || i < 0) + return ""; + + return GetLocalString(store,name+"_"+IntToString(i)); +} + +int array_get_int(object store, string name, int i) +{ + return StringToInt(array_get_string(store,name,i)); +} + +float array_get_float(object store, string name, int i) +{ + return StringToFloat(array_get_string(store,name,i)); +} + +object array_get_object(object store, string name, int i) +{ + if(array_get_string(store, name, i) == "OBJECT") + return GetLocalObject(store,name+"_"+IntToString(i)+"_OBJECT"); + else + return OBJECT_INVALID; +} + +int array_shrink(object store, string name, int size_new) +{ + // Get the current size value + int size = GetLocalInt(store, name); + // Error check - non-existent array + if(size == 0) + return SDL_ERROR_DOES_NOT_EXIST; + // If the new number of elements is equal to or greater than the current number of elements, autosuccess + if((size - 1) <= size_new) + return SDL_SUCCESS; + + // Delete entries that are outside the new array bounds + int i; + for(i = size_new; i < size; i++) + { + DeleteLocalString(store, name+"_"+IntToString(i)); + + // just in case, delete possible object names + DeleteLocalObject(store, name+"_"+IntToString(i)+"_OBJECT"); + } + + // Store the new size, with the +1 existence marker + SetLocalInt(store, name, size_new + 1); + + return SDL_SUCCESS; +} + +int array_get_size(object store, string name) +{ + return GetLocalInt(store, name) - 1; +} + +int array_exists(object store, string name) +{ + // If the size and presence indicator local is non-zero, the array exists. Normalize it's value to TRUE / FALSE and return + return GetLocalInt(store, name) != 0; +} + +/* +int array_2d_create(object store, string name) +{ + // error checking + if(!GetIsObjectValid(store)) + return SDL_ERROR_NOT_VALID_OBJECT; + else if(GetLocalInt(store,name)) + return SDL_ERROR_ALREADY_EXISTS; + else + { + // Initialize the size (always one greater than the actual size) + SetLocalInt(store,name+"_A",1); + SetLocalInt(store,name+"_B",1); + return SDL_SUCCESS; + } +} + + +int array_2d_delete(object store, string name) +{ + // error checking + int sizeA=GetLocalInt(store,name+"_A"); + if (sizeA==0) + return SDL_ERROR_DOES_NOT_EXIST; + int sizeB=GetLocalInt(store,name+"_B"); + if (sizeB==0) + return SDL_ERROR_DOES_NOT_EXIST; + + int i; + int j; + for (i=0; isizeA) + SetLocalInt(store,name+"_A",i+2); + if (j+2>sizeB) + SetLocalInt(store,name+"_B",j+2); + + return SDL_SUCCESS; +} + + +int array_2d_set_int(object store, string name, int i, int j, int entry) +{ + return array_2d_set_string(store,name,i,j,IntToString(entry)); +} + +int array_2d_set_float(object store, string name, int i, int j, float entry) +{ + return array_2d_set_string(store,name,i,j,FloatToString(entry)); +} + +int array_2d_set_object(object store, string name, int i, int j, object entry) +{ + // object is a little more complicated. + // we want to create an object as a local variable too + if (!GetIsObjectValid(entry)) + return SDL_ERROR_NOT_VALID_OBJECT; + + int results=array_2d_set_string(store,name,i,j,"OBJECT"); + if (results==SDL_SUCCESS) + SetLocalObject(store,name+"_"+IntToString(i)+"_"+IntToString(j)+"_OBJECT",entry); + + return results; +} + + +string array_2d_get_string(object store, string name, int i, int j) +{ + int sizeA=GetLocalInt(store,name+"_A"); + if (sizeA==0 || i>sizeA) + return ""; + int sizeB=GetLocalInt(store,name+"_B"); + if (sizeB==0 || j>sizeB) + return ""; + + return GetLocalString(store,name+"_"+IntToString(i)+"_"+IntToString(j)); +} + +int array_2d_get_int(object store, string name, int i, int j) +{ + return StringToInt(array_2d_get_string(store,name,i,j)); +} + +float array_2d_get_float(object store, string name, int i, int j) +{ + return StringToFloat(array_2d_get_string(store,name,i,j)); +} + +object array_2d_get_object(object store, string name, int i, int j) +{ + return GetLocalObject(store,name+"_"+IntToString(i)+"_"+IntToString(j)+"_OBJECT"); +} + + +int array_2d_shrink(object store, string name, int size_new, int axis) +{ + // error checking + int sizeA=GetLocalInt(store,name+"_A"); + if (sizeA==0) + return SDL_ERROR_DOES_NOT_EXIST; + int sizeB=GetLocalInt(store,name+"_B"); + if (sizeB==0) + return SDL_ERROR_DOES_NOT_EXIST; + + if (axis == 1 && + (sizeA==size_new || sizeA= 12) + { + eDR = EffectDamageReduction(10, DAMAGE_POWER_PLUS_THREE); + nResist = 20; + } + else if (12 > nHD && nHD >= 8) + { + eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_TWO); + nResist = 15; + } + else if (8 > nHD && nHD >= 4) + { + eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_ONE); + nResist = 10; + } + else if (4 > nHD) + { + nResist = 5; + } + + effect eFire = EffectDamageResistance(DAMAGE_TYPE_FIRE, nResist); + effect eCold = EffectDamageResistance(DAMAGE_TYPE_COLD, nResist); + effect eVis = EffectUltravision(); + effect eLink = EffectLinkEffects(eDR, eFire); + eLink = EffectLinkEffects(eLink, eCold); + eLink = EffectLinkEffects(eLink, eVis); + eLink = SupernaturalEffect(eLink); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget); + + itemproperty ipIP = PRCItemPropertyBonusFeat(FEAT_TEMPLATE_FIENDISH_SMITE_GOOD); + IPSafeAddItemProperty(oCompSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_EVIL) + AdjustAlignment(oTarget, ALIGNMENT_EVIL, 80, FALSE); + + SetSubRace(oTarget, "Undead (Extraplanar)"); + + SetLocalInt(oTarget, "FiendishTemplate", 1); + +} + + +void ApplyExaltedCompanion(object oCompanion, object oCompSkin) +{ + int nHD = GetHitDice(oCompanion); + effect eDR; + int nResist; + + if (nHD >= 12) + { + eDR = EffectDamageReduction(10, DAMAGE_POWER_PLUS_THREE); + nResist = 20; + } + else if (12 > nHD && nHD >= 8) + { + eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_TWO); + nResist = 15; + } + else if (8 > nHD && nHD >= 4) + { + eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_ONE); + nResist = 10; + } + else if (4 > nHD) + { + nResist = 5; + } + + effect eAcid = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist); + effect eCold = EffectDamageResistance(DAMAGE_TYPE_COLD, nResist); + effect eElec = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist); + effect eVis = EffectUltravision(); + effect eLink = EffectLinkEffects(eDR, eAcid); + eLink = EffectLinkEffects(eLink, eCold); + eLink = EffectLinkEffects(eLink, eElec); + eLink = EffectLinkEffects(eLink, eVis); + eLink = SupernaturalEffect(eLink); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oCompanion); + + itemproperty ipIP = PRCItemPropertyBonusFeat(IP_CONST_FEAT_TEMPLATE_CELESTIAL_SMITE_EVIL); + IPSafeAddItemProperty(oCompSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + if(GetAlignmentGoodEvil(oCompanion) != ALIGNMENT_GOOD) + AdjustAlignment(oCompanion, ALIGNMENT_GOOD, 80, FALSE); + + SetLocalInt(oCompanion, "CelestialTemplate", 1); +} + +void ApplyPseudonatural(object oFamiliar, object oFamSkin) +{ + if(GetLocalInt(oFamiliar, "PseudonaturalTemplate")) + return; + + int nHD = GetHitDice(oFamiliar); + int nResist; + effect eDR; + if (nHD >= 12) + { + eDR = EffectDamageReduction(10, DAMAGE_POWER_PLUS_THREE); + nResist = 20; + } + else if (12 > nHD && nHD >= 8) + { + eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_TWO); + nResist = 15; + } + else if (8 > nHD && nHD >= 4) + { + eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_ONE); + nResist = 10; + } + else if (4 > nHD) + { + nResist = 5; + } + + effect eAcid = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist); + effect eElec = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist); + effect eAC = EffectACIncrease(1,AC_NATURAL_BONUS); + effect eLink = EffectLinkEffects(eDR, eAcid); + eLink = EffectLinkEffects(eLink, eElec); + eLink = EffectLinkEffects(eLink, eAC); + eLink = SupernaturalEffect(eLink); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oFamiliar); + +// itemproperty ipIP = PRCItemPropertyBonusFeat(IP_CONST_FEAT_TEMPLATE_PSEUDONATURAL_TRUESTRIKE); +// IPSafeAddItemProperty(oFamSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + SetLocalInt(oFamiliar, "PseudonaturalTemplate", 1); +} + +void ApplyIllmaster(object oCompanion, object oCompSkin) +{ + //Give the companion permanent Str +4, Con +2, Wis -2, and Cha -2 + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + { + PRC_Funcs_ModAbilityScore(oCompanion, ABILITY_STRENGTH, 4); + PRC_Funcs_ModAbilityScore(oCompanion, ABILITY_CONSTITUTION, 2); + PRC_Funcs_ModAbilityScore(oCompanion, ABILITY_WISDOM, -2); + PRC_Funcs_ModAbilityScore(oCompanion, ABILITY_CHARISMA, -2); + } + else + { + string sFlag = "Illmaster"; + SetCompositeBonus(oCompSkin, sFlag, 4, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_STR); + SetCompositeBonus(oCompSkin, sFlag, 2, ITEM_PROPERTY_ABILITY_BONUS, IP_CONST_ABILITY_CON); + SetCompositeBonus(oCompSkin, sFlag, 2, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_WIS); + SetCompositeBonus(oCompSkin, sFlag, 2, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_CHA); + } + + //Set PLANT immunities and add low light vision + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertySpellImmunitySpecific(IP_CONST_IMMUNITYSPELL_CHARM_PERSON), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertySpellImmunitySpecific(IP_CONST_IMMUNITYSPELL_DOMINATE_PERSON), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertySpellImmunitySpecific(IP_CONST_IMMUNITYSPELL_HOLD_PERSON), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertySpellImmunitySpecific(IP_CONST_IMMUNITYSPELL_MASS_CHARM), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_MINDSPELLS), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_POISON), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_PARALYSIS), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_DISEASE), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB), oCompSkin); + AddItemProperty(DURATION_TYPE_PERMANENT, PRCItemPropertyBonusFeat(FEAT_LOWLIGHTVISION), oCompSkin); + + //Compute the DC for this companion's blight touch + int iHD = GetHitDice(oCompanion); + int iCons = GetAbilityModifier(ABILITY_CONSTITUTION, oCompanion); + int iDC = 10 + (iHD / 2) + iCons; + //Create the onhit item property for causing the blight touch disease + itemproperty ipBlightTouch = ItemPropertyOnHitProps(IP_CONST_ONHIT_DISEASE, IPOnHitSaveDC(iDC), DISEASE_TALONAS_BLIGHT); + //Get the companion's creature weapons + object oBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCompanion); + object oLClaw = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCompanion); + object oRClaw = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCompanion); + //Apply blight touch to each weapon + if (GetIsObjectValid(oBite)) + IPSafeAddItemProperty(oBite, ipBlightTouch); + if (GetIsObjectValid(oLClaw)) + IPSafeAddItemProperty(oLClaw, ipBlightTouch); + if (GetIsObjectValid(oRClaw)) + IPSafeAddItemProperty(oRClaw, ipBlightTouch); + + //Adjust alignment to Neutral Evil + if(GetAlignmentLawChaos(oCompanion) != ALIGNMENT_NEUTRAL) + AdjustAlignment(oCompanion, ALIGNMENT_NEUTRAL, 50, FALSE); + if (GetAlignmentGoodEvil(oCompanion) != ALIGNMENT_EVIL) + AdjustAlignment(oCompanion, ALIGNMENT_EVIL, 80, FALSE); +} + +void WinterWolfProperties(object oCompanion, int nLevel) +{ + int iStr = nLevel >= 30 ? (nLevel-30)/2 : (nLevel-4)/2; + int iDex = nLevel >= 30 ? (nLevel-31)/2 : (nLevel-5)/2; + + object oCreR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCompanion); + + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + { + if(iStr > 0) + PRC_Funcs_ModAbilityScore(oCompanion, ABILITY_STRENGTH, iStr); + if(iDex > 0) + PRC_Funcs_ModAbilityScore(oCompanion, ABILITY_DEXTERITY, iStr); + } + else + { + if(iStr > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(IP_CONST_ABILITY_STR, iStr), oCreR); + if(iDex > 0) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(IP_CONST_ABILITY_DEX, iDex), oCreR); + } + + int nBonusDmg; + + if(nLevel > 24) + nBonusDmg = IP_CONST_DAMAGEBONUS_1d12; + else if(nLevel > 16) + nBonusDmg = IP_CONST_DAMAGEBONUS_1d10; + else if(nLevel > 7) + nBonusDmg = IP_CONST_DAMAGEBONUS_1d8; + else + nBonusDmg = IP_CONST_DAMAGEBONUS_1d6; + + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_COLD, nBonusDmg), oCreR); + //winter wolf properties + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyDamageVulnerability(IP_CONST_DAMAGETYPE_FIRE, IP_CONST_DAMAGEVULNERABILITY_50_PERCENT), oCreR); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyDamageImmunity(IP_CONST_DAMAGETYPE_COLD, IP_CONST_DAMAGEIMMUNITY_100_PERCENT), oCreR); + + int iMax, i; + int coneAbi = nLevel / 5; + switch(coneAbi) + { + case 1 : iMax = 7; break; + case 2 : iMax = 6; break; + case 3 : iMax = 5; break; + case 4 : iMax = 4; break; + case 5 : iMax = 3; break; + case 6 : iMax = 2; break; + case 7 : iMax = 1; break; + } + + if(iMax > 0) + { + for(i = 1; i <= iMax; i++) + DecrementRemainingSpellUses(oCompanion, 230); + } +} + +void ApplyPnPFamiliarProperties(object oPC, object oFam) +{ + int bFuncs = GetPRCSwitch(PRC_NWNX_FUNCS); + effect eBonus; + + //get familiar level + int nFamLevel = GetLevelByClass(CLASS_TYPE_WIZARD, oPC) + + GetLevelByClass(CLASS_TYPE_SORCERER, oPC) + + GetLevelByClass(CLASS_TYPE_WITCH, oPC) + + GetLevelByClass(CLASS_TYPE_ALIENIST, oPC) + + GetLevelByClass(CLASS_TYPE_HEXBLADE, oPC) + + GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC); + + if (GetHasFeat(FEAT_SHADOW_FAMILIAR, oPC)) nFamLevel = GetLevelByTypeArcane(oPC) + GetShadowcasterLevel(oPC); // For the purpose of determining familiar abilities that depend on your arcane caster level, + // your levels in all classes that allow you to cast mysteries or arcane spells stack + + //scaling bonuses + int nAdjustLevel = nFamLevel - GetHitDice(oFam); + int n; + for(n = 1; nAdjustLevel >= n; n++) + LevelUpHenchman(oFam, CLASS_TYPE_INVALID, TRUE); + + int nACBonus = nFamLevel / 2; + int nIntBonus = nFamLevel / 2; + int nSRBonus = nFamLevel > 11 ? nFamLevel + 5 : 0; + + //saving throws + int nSaveRefBonus = GetReflexSavingThrow(oPC) - GetReflexSavingThrow(oFam); + int nSaveFortBonus = GetFortitudeSavingThrow(oPC) - GetFortitudeSavingThrow(oFam); + int nSaveWillBonus = GetWillSavingThrow(oPC) - GetWillSavingThrow(oFam); + + int nHPBonus; + + if(bFuncs) + { + nHPBonus = GetMaxHitPoints(oPC) / 2; + + int i, nBonus; + for(i = 0; i < GetPRCSwitch(FILE_END_SKILLS); i++) + { + nBonus = GetSkillRank(i, oPC) - GetSkillRank(i, oFam); + if(nBonus > 0) + PRC_Funcs_ModSkill(oFam, i, nBonus); + } + + PRC_Funcs_SetBaseNaturalAC(oFam, PRC_Funcs_GetBaseNaturalAC(oFam) + nACBonus); + PRC_Funcs_ModAbilityScore(oFam, ABILITY_INTELLIGENCE, nIntBonus); + PRC_Funcs_ModSavingThrowBonus(oFam, SAVING_THROW_REFLEX, nSaveRefBonus); + PRC_Funcs_ModSavingThrowBonus(oFam, SAVING_THROW_FORT, nSaveFortBonus); + PRC_Funcs_ModSavingThrowBonus(oFam, SAVING_THROW_WILL, nSaveWillBonus); + PRC_Funcs_SetMaxHitPoints(oFam, nHPBonus); + PRC_Funcs_SetCurrentHitPoints(oFam, nHPBonus); + } + else + { + //temporary HP for the moment, have to think of a better idea later + nHPBonus = (GetMaxHitPoints(oPC) / 2) - GetMaxHitPoints(oFam); + + int i, nBonus; + for(i = 0; i < GetPRCSwitch(FILE_END_SKILLS); i++) + { + nBonus = GetSkillRank(i, oPC) - GetSkillRank(i, oFam); + eBonus = EffectLinkEffects(eBonus, EffectSkillIncrease(i, nBonus)); + } + + eBonus = EffectLinkEffects(eBonus, EffectACIncrease(nACBonus, AC_NATURAL_BONUS)); + eBonus = EffectLinkEffects(eBonus, EffectAbilityIncrease(ABILITY_INTELLIGENCE, nIntBonus)); + eBonus = EffectLinkEffects(eBonus, EffectSavingThrowIncrease(SAVING_THROW_REFLEX, nSaveRefBonus)); + eBonus = EffectLinkEffects(eBonus, EffectSavingThrowIncrease(SAVING_THROW_FORT, nSaveFortBonus)); + eBonus = EffectLinkEffects(eBonus, EffectSavingThrowIncrease(SAVING_THROW_WILL, nSaveWillBonus)); + eBonus = EffectLinkEffects(eBonus, EffectTemporaryHitpoints(nHPBonus)); + } + + int nABBonus = GetBaseAttackBonus(oPC) - GetBaseAttackBonus(oFam); + int nAttacks = (GetBaseAttackBonus(oPC)/5)+1; + if(nAttacks > 5) + nAttacks = 5; + SetBaseAttackBonus(nAttacks, oFam); + + //effect doing + if(nSRBonus > 0) eBonus = EffectLinkEffects(eBonus, EffectSpellResistanceIncrease(nSRBonus)); + eBonus = EffectLinkEffects(eBonus, EffectAttackIncrease(nABBonus)); + //skills were linked earlier + eBonus = SupernaturalEffect(eBonus); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBonus, oFam); +} + +void CheckIsValidFamiliar(object oMaster, effect eBonus) +{ + object oFam = GetAssociateNPC(ASSOCIATE_TYPE_FAMILIAR); + object oFamToken = GetItemPossessedBy(oMaster, "prc_pnp_familiar"); + + if(GetIsObjectValid(oFam)) + { + if(!GetIsDead(oFam)) + { + DelayCommand(30.0, CheckIsValidFamiliar(oMaster, eBonus)); + return; + } + } + else + { + if(GetIsObjectValid(oFamToken)) + { + DelayCommand(30.0, CheckIsValidFamiliar(oMaster, eBonus)); + return; + } + } + + RemoveEffect(oMaster, eBonus); +} + +effect GetMasterBonus(int nFamiliarType) +{ + effect eBonus; + string sFile = "prc_familiar"; + string sTemp = Get2DACache(sFile, "BONUS", nFamiliarType); + int nParam1 = StringToInt(Get2DACache(sFile, "PARAM1", nFamiliarType)); + int nParam2 = StringToInt(Get2DACache(sFile, "PARAM2", nFamiliarType)); + + if(sTemp == "sk") + eBonus = EffectSkillIncrease(nParam1, nParam2); + else if(sTemp == "sv") + eBonus = EffectSavingThrowIncrease(nParam1, nParam2); + else if(sTemp == "hp") + eBonus = EffectTemporaryHitpoints(nParam1); + + return eBonus; +} + +int GetCompanionBaseDamage(int nLevel, int bBite) +{ + int nBite, nClaw; + if(nLevel > 35) + { + nBite = IP_CONST_MONSTERDAMAGE_2d10; + nClaw = IP_CONST_MONSTERDAMAGE_1d12; + } + else if(nLevel > 26) + { + nBite = IP_CONST_MONSTERDAMAGE_2d8; + nClaw = IP_CONST_MONSTERDAMAGE_1d10; + } + else if(nLevel > 18) + { + nBite = IP_CONST_MONSTERDAMAGE_2d6; + nClaw = IP_CONST_MONSTERDAMAGE_1d8; + } + else if(nLevel > 11) + { + nBite = IP_CONST_MONSTERDAMAGE_1d10; + nClaw = IP_CONST_MONSTERDAMAGE_1d6; + } + else if(nLevel > 5) + { + nBite = IP_CONST_MONSTERDAMAGE_1d8; + nClaw = IP_CONST_MONSTERDAMAGE_1d4; + } + else + { + nBite = IP_CONST_MONSTERDAMAGE_1d6; + nClaw = IP_CONST_MONSTERDAMAGE_1d3; + } + + if(bBite) + return nBite; + + return nClaw; +} + +void SetNaturalWeaponDamage(object oCompanion, int nLvlMod = 0) +{ + int nLevel = GetHitDice(oCompanion); + nLevel += nLvlMod; + int Enh = (nLevel - 15) / 5; + int nDamage; + + //Get the companion's creature weapons + object oBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCompanion); + object oLClaw = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCompanion); + object oRClaw = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCompanion); + + //Apply properties to each weapon + if(GetIsObjectValid(oBite)) + { + CleanProperties(oBite); + nDamage = GetCompanionBaseDamage(nLevel, TRUE); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyMonsterDamage(nDamage), oBite); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonus(Enh), oBite); + } + if(GetIsObjectValid(oLClaw)) + { + CleanProperties(oLClaw); + nDamage = GetCompanionBaseDamage(nLevel, FALSE); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyMonsterDamage(nDamage), oLClaw); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonus(Enh), oLClaw); + } + if(GetIsObjectValid(oRClaw)) + { + CleanProperties(oRClaw); + nDamage = GetCompanionBaseDamage(nLevel, FALSE); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyMonsterDamage(nDamage), oRClaw); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonus(Enh), oRClaw); + } +} + +void UnsummonCompanions(object oPC) +{ + int i; + object oAsso; + for(i = 1; i <= 5; i++) + { + oAsso = GetAssociateNPC(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC, i); + if(GetIsObjectValid(oAsso)) + DestroyAssociate(oAsso); + } + for(i = 1; i <= 5; i++) + { + oAsso = GetAssociateNPC(ASSOCIATE_TYPE_FAMILIAR, oPC, i); + if(GetIsObjectValid(oAsso)) + DestroyAssociate(oAsso); + } +} + +//:: void main(){} \ No newline at end of file diff --git a/src/include/prc_inc_breath.nss b/src/include/prc_inc_breath.nss new file mode 100644 index 0000000..0d439f9 --- /dev/null +++ b/src/include/prc_inc_breath.nss @@ -0,0 +1,988 @@ +//:://///////////////////////////////////////////// +//:: Breath Weapon Include +//:: prc_inc_breath +//:://///////////////////////////////////////////// +/** @file + Centralizes handling for most breath weapons for + implementing Metabreath and the like. + + @author Fox + @date Created - 2008.1.19 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure that contains common data used during power manifestation. + */ +struct breath{ + /* Generic stuff */ + /// The creature breathing + object oDragon; + + /* Basic info */ + /// The shape of the breath + int bLine; + /// The size of the breath in feet + float fRange; + /// The element of the breath + int nDamageType; + /// Type of dice + int nDiceType; + /// Number of dice + int nDiceNumber; + /// The stat relavant for DC calculating + int nDCStat; + /// Any other DC mods, like class levels for DragDis + int nOtherDCMod; + /// Save type - needed in case of Fort Save + int nSaveUsed; + /// Rounds until next use + int nRoundsUntilRecharge; + + /* Metabreath */ + /// Number of rounds Clinging Breaths last + int nClinging; + /// Number of rounds Lingering Breaths last + int nLingering; + /// Whether Enlarge Breath was used + int bEnlarge; + /// How much the DC is increased by Heighten Breath - max of Con + int nHeighten; + /// Whether Maximize Breath was used + int bMaximize; + /// Whether Spreading Breath was used + int bSpread; + /// Whether Tempest Breath was used + int bTempest; + + /* Breath Channeling */ + /// Whether Entangling Exhalation was used + int bEntangling; + /// Whether Exhaled Barrier was used + int bBarrier; + /// Whether Exhaled Immunity was used + int bExhaleImmune; + + + /* Special Cases */ + /// Special case referance number - used for things like Pyroclastic + int nOverrideSpecial; + +}; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Creates the breath struct, taking metabreath and breath channeling into account. + * + * @param oDragon The creature breathing the breath weapon + * @param bLine True if breath is a line breath, false if a cone breath + * @param fRange The range of the breath weapon in feet. + * @param nDamageType The element of the breath weapon, if it has one. + * @param nDiceType The type of damage dice for the breath weapon. + * @param nDiceNumber The number of damage dice for the breath weapon. + * @param nDCStat The constant for the stat the breath weapon uses for DC + * calculation. + * @param nOtherDCMod Any other applicable modifications to the DC. + * e.g. Dragon Disciple levels. + * @param nOverrideSpecial Indicates any special case breath weapons, like the Pyroclastic's + * split damage or Shadow's negative level breath. Defaults to 0. + * @param nBaseRecharge The die size of the recharge "lock." Defaults to d4. + * @param nSaveUsed The constant to determine the save used. Defaults to Reflex. + * Diamond Dragon uses Fort saves for cold breath for example. + * + * @return Returns a struct that describes the breath being used, including + * applicable metabreath and breath channeling feats. + */ +struct breath CreateBreath(object oDragon, int bLine, float fRange, int nDamageType, int nDiceType, int nDiceNumber, int nDCStat, int nOtherDCMod, int nOverrideSpecial = 0, int nBaseRecharge = 4, int nSaveUsed = SAVING_THROW_REFLEX); + +/** + * Applies the breath effect on the targeted location. Brought into the include since + * there is so much common code. + * + * @param BreathUsed A struct describing the details of the breath weapon + * @param lTargetArea The targeted location for the breath. + * @param bLinger Determines if this breath was applied by Lingering Breath metabreath. + * Defaults to FALSE. + * @param vSource The source of a Lingering breath. This does not matter except when + * bLinger is set to TRUE. + * + */ +void ApplyBreath(struct breath BreathUsed, location lTargetArea, int bLinger = FALSE, float vSourceX = 0.0, float vSourceY = 0.0, float vSourceZ = 0.0); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_alterations" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +// Exhaled Immunity is different enough to break off on it's own, as it only +// applies to one creature, and nothing else happens. +void ExhaleImmunity(object oDragon, int nDamageType, location lTargetArea) +{ + //since there's no "GetObjectAtLocation," grab a tiny AoE to find the creature + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 1.0, lTargetArea, TRUE, + OBJECT_TYPE_CREATURE, GetPosition(oDragon)); + + //make sure damage type is valid, default to fire for non-energy-damage-based breaths + int nImmuneType = DAMAGE_TYPE_FIRE; + + if (nDamageType == DAMAGE_TYPE_ACID) nImmuneType = DAMAGE_TYPE_ACID; + else if (nDamageType == DAMAGE_TYPE_COLD) nImmuneType = DAMAGE_TYPE_COLD; + else if (nDamageType == DAMAGE_TYPE_SONIC) nImmuneType = DAMAGE_TYPE_SONIC; + else if (nDamageType == DAMAGE_TYPE_ELECTRICAL) nImmuneType = DAMAGE_TYPE_ELECTRICAL; + + if((oTarget == OBJECT_INVALID) || (oTarget == oDragon)) + { + SendMessageToPC(oDragon, "You must target another creature for Exhaled Immunity to work."); + return; + } + + else + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(oDragon, GetSpellId(), FALSE)); + //Determine effect delay + float fDelay = GetDistanceBetween(oDragon, oTarget)/20; + float fDuration = RoundsToSeconds(d4()); + + //set effects + effect eImmune = EffectDamageImmunityIncrease(nDamageType, 100); + effect eVis = EffectVisualEffect(VFX_IMP_ELEMENTAL_PROTECTION); + effect eVis2 = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE); + effect eLink = EffectLinkEffects(eImmune, eVis2); + + //apply effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eImmune, oTarget, fDuration)); + } +} + +//fucntion to check for Adept breath +int IsAdeptBreath() +{ + int nBreath = GetSpellId(); + if(nBreath == BREATH_FIRE_CONE + || nBreath == BREATH_FIRE_LINE + || nBreath == BREATH_FROST_CONE + || nBreath == BREATH_ELECTRIC_LINE + || nBreath == BREATH_SICKENING_CONE + || nBreath == BREATH_ACID_CONE + || nBreath == BREATH_ACID_LINE + || nBreath == BREATH_SLOW_CONE + || nBreath == BREATH_WEAKENING_CONE + || nBreath == BREATH_SLEEP_CONE + || nBreath == BREATH_THUNDER_CONE + || nBreath == BREATH_BAHAMUT_LINE + || nBreath == BREATH_FORCE_LINE + || nBreath == BREATH_PARALYZE_CONE + || nBreath == BREATH_FIVEFOLD_TIAMAT) + return TRUE; + + return FALSE; +} + +int IsBreathProtected(object oDragon, object oTarget) +{ + int nBPIndex = 0; + while(array_get_object(oTarget, "BreathProtected", nBPIndex) != OBJECT_INVALID) + { + if(array_get_object(oTarget, "BreathProtected", nBPIndex) == oDragon) + { + return TRUE; + } + else + nBPIndex++; + } + + return FALSE; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct breath CreateBreath(object oDragon, int bLine, float fRange, int nDamageType, int nDiceType, int nDiceNumber, int nDCStat, int nOtherDCMod, int nOverrideSpecial = 0, int nBaseRecharge = 4, int nSaveUsed = SAVING_THROW_REFLEX) +{ + struct breath BreathUsed; + + //gather base data + BreathUsed.oDragon = oDragon; + BreathUsed.bLine = bLine; + BreathUsed.fRange = fRange; + BreathUsed.nDamageType = nDamageType; + BreathUsed.nDiceType = nDiceType; + BreathUsed.nDiceNumber = nDiceNumber; + BreathUsed.nDCStat = nDCStat; + BreathUsed.nOtherDCMod = nOtherDCMod; + int nFinalRecharge; + switch(nBaseRecharge) + { + case 4: + nFinalRecharge = d4(); break; + case 1: + nFinalRecharge = 1; break; + default: + nFinalRecharge = 0; break; + } + BreathUsed.nRoundsUntilRecharge = nFinalRecharge; + BreathUsed.nSaveUsed = nSaveUsed; + BreathUsed.nOverrideSpecial = nOverrideSpecial; + + //Energy Aura bonus checking + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + if(GetLocalInt(oDragon,"AcidEnergyAura")) + BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"AcidEnergyAura"); + break; + case DAMAGE_TYPE_COLD: + if(GetLocalInt(oDragon,"ColdEnergyAura")) + BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"ColdEnergyAura"); + break; + case DAMAGE_TYPE_ELECTRICAL: + if(GetLocalInt(oDragon,"ElecEnergyAura")) + BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"ElecEnergyAura"); + break; + case DAMAGE_TYPE_FIRE: + if(GetLocalInt(oDragon,"FireEnergyAura")) + BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"FireEnergyAura"); + } + + /* Initialize metabreath/channeling tracking */ + BreathUsed.nClinging = 0; + BreathUsed.nLingering = 0; + BreathUsed.bEnlarge = FALSE; + BreathUsed.nHeighten = 0; + BreathUsed.bMaximize = FALSE; + BreathUsed.bSpread = FALSE; + BreathUsed.bTempest = FALSE; + BreathUsed.bEntangling = FALSE; + BreathUsed.bBarrier = FALSE; + BreathUsed.bExhaleImmune = FALSE; + + /* breath channeling */ + + // Whether Entangling Exhalation was used + if(GetLocalInt(oDragon, "EntangleBreath")) + BreathUsed.bEntangling = TRUE; + // Whether Exhaled Barrier was used + if(GetLocalInt(oDragon, "ExhaleBarrier")) + BreathUsed.bBarrier = TRUE; + // Whether Exhaled Immunity was used + if(GetLocalInt(oDragon, "ExhaleImmunity")) + BreathUsed.bExhaleImmune = TRUE; + + //breath effect checks + + //Cloud only works with 1 other effect applied + //this one handles combination with damage effects and enduring + if(GetLocalInt(oDragon, "AdeptCloudBreath") + && !GetLocalInt(oDragon, "AdeptShapedBreath") + && (GetSpellId() == BREATH_FIRE_CONE + || GetSpellId() == BREATH_FROST_CONE + || GetSpellId() == BREATH_SICKENING_CONE + || GetSpellId() == BREATH_ACID_CONE + || GetSpellId() == BREATH_SLOW_CONE + || GetSpellId() == BREATH_WEAKENING_CONE + || GetSpellId() == BREATH_SLEEP_CONE + || GetSpellId() == BREATH_THUNDER_CONE + || GetSpellId() == BREATH_PARALYZE_CONE)) + { + BreathUsed.bSpread = TRUE; + } + //this one is Cloud + Shaped + if(GetLocalInt(oDragon, "AdeptCloudBreath") + && GetLocalInt(oDragon, "AdeptShapedBreath") + && !GetLocalInt(oDragon, "AdeptEnduringBreath") + && GetSpellId() == BREATH_FIRE_CONE) + { + BreathUsed.bSpread = TRUE; + } + if(GetLocalInt(oDragon, "AdeptEnduringBreath") + && !(GetLocalInt(oDragon, "AdeptCloudBreath") && GetLocalInt(oDragon, "AdeptShapedBreath")) + && (GetSpellId() == BREATH_FIRE_CONE || GetSpellId() == BREATH_FIRE_LINE)) + { + BreathUsed.nLingering = 1; + } + + //breaths without recharge times can't use metabreath + if(BreathUsed.nRoundsUntilRecharge == 0) + return BreathUsed; + + /* metabreath calculation*/ + + //Clinging breath - Main feat increments uses, secondary feat cancels. + if(GetLocalInt(oDragon, "ClingingBreath") > 0) + { + BreathUsed.nClinging = GetLocalInt(oDragon, "ClingingBreath"); + BreathUsed.nRoundsUntilRecharge += BreathUsed.nClinging; + } + //Lingering breath - Main feat increments uses, secondary feat cancels. + if(GetLocalInt(oDragon, "LingeringBreath") > 0) + { + BreathUsed.nLingering = GetLocalInt(oDragon, "LingeringBreath"); + BreathUsed.nRoundsUntilRecharge += (BreathUsed.nLingering * 2); + } + //Enlarge breath + if(GetLocalInt(oDragon, "EnlargeBreath")) + { + BreathUsed.bEnlarge = TRUE; + BreathUsed.nRoundsUntilRecharge += 1; + } + //Heighten breath - Feat increments uses, resets if incremented at max. + if(GetLocalInt(oDragon, "HeightenBreath") > 0) + { + BreathUsed.nHeighten = GetLocalInt(oDragon, "HeightenBreath"); + BreathUsed.nRoundsUntilRecharge += BreathUsed.nHeighten; + } + //Maximize breath + if(GetLocalInt(oDragon, "MaximizeBreath")) + { + BreathUsed.bMaximize = TRUE; + BreathUsed.nRoundsUntilRecharge += 3; + } + //Shape breath + if(GetLocalInt(oDragon, "ShapeBreath")) + { + if(bLine) BreathUsed.bLine = FALSE; + else BreathUsed.bLine = TRUE; + BreathUsed.nRoundsUntilRecharge += 1; + } + //Spreading breath + if(GetLocalInt(oDragon, "SpreadingBreath")) + { + BreathUsed.bSpread = TRUE; + BreathUsed.nRoundsUntilRecharge += 2; + } + //Tempest breath + if(GetLocalInt(oDragon, "TempestBreath")) + { + BreathUsed.bTempest = TRUE; + BreathUsed.nRoundsUntilRecharge += 1; + } + //Recover Breath + if(GetHasFeat(FEAT_RECOVER_BREATH, oDragon) + && (BreathUsed.nRoundsUntilRecharge > 1)) + BreathUsed.nRoundsUntilRecharge += -1; + + + // Exhaled Barrier and Immunity don't apply Metabreath feats, so recharge time should be unaffected + if(BreathUsed.bBarrier || BreathUsed.bExhaleImmune) + BreathUsed.nRoundsUntilRecharge = nFinalRecharge; + + return BreathUsed; +} + +void ApplyBreath(struct breath BreathUsed, location lTargetArea, int bLinger = FALSE, float vSourceX = 0.0, float vSourceY = 0.0, float vSourceZ = 0.0) +{ + //if using Exhaled Immunity, jump straight to it and ignore the rest + if(BreathUsed.bExhaleImmune) + { + ExhaleImmunity(BreathUsed.oDragon, BreathUsed.nDamageType, lTargetArea); + return; + } + + //init variables + effect eBreath; + effect eVis; + float fDelay; + int nDamage = 0; + int nAdjustedDamage; + int nSaveDamageType = -1; + int nVisualType = -1; + int nEnergyAura = 0; + int bCanCling; + int nSaveDC; + int nShapedSpotsUsed = GetLocalInt(BreathUsed.oDragon, "AdeptShapedBreath") ? 0 : 4; + int nBreathShape = BreathUsed.bLine ? SHAPE_SPELLCYLINDER : SHAPE_SPELLCONE; + float fRange = FeetToMeters(BreathUsed.fRange); + int nKnockdownDC = 0; + vector vSource = Vector(vSourceX, vSourceY, vSourceZ); + vector vSourceOfBreath = bLinger ? vSource : GetPosition(BreathUsed.oDragon); + + //Saving Throw setup + if (BreathUsed.nDCStat >= 10) + nSaveDC = BreathUsed.nDCStat; + else + nSaveDC = 10 + PRCMax(GetAbilityModifier(BreathUsed.nDCStat), 0) + BreathUsed.nOtherDCMod; + + //Set up variables that depend on damage type + switch (BreathUsed.nDamageType) + { + case DAMAGE_TYPE_ACID: + nSaveDamageType = SAVING_THROW_TYPE_ACID; + nVisualType = VFX_IMP_ACID_S; + nEnergyAura = GetLocalInt(BreathUsed.oDragon, "AcidEnergyAura"); break; + + case DAMAGE_TYPE_COLD: + nSaveDamageType = SAVING_THROW_TYPE_COLD; + nVisualType = VFX_IMP_FROST_S; + nEnergyAura = GetLocalInt(BreathUsed.oDragon, "ColdEnergyAura"); break; + + case DAMAGE_TYPE_ELECTRICAL: + nSaveDamageType = SAVING_THROW_TYPE_ELECTRICITY; + nVisualType = VFX_IMP_LIGHTNING_S; + nEnergyAura = GetLocalInt(BreathUsed.oDragon, "ElecEnergyAura"); break; + + case DAMAGE_TYPE_FIRE: + nSaveDamageType = SAVING_THROW_TYPE_FIRE; + nVisualType = VFX_IMP_FLAME_M; + nEnergyAura = GetLocalInt(BreathUsed.oDragon, "FireEnergyAura"); break; + + case DAMAGE_TYPE_MAGICAL: + nSaveDamageType = SAVING_THROW_TYPE_NONE; + nVisualType = VFX_IMP_KNOCK; break; + + case DAMAGE_TYPE_NEGATIVE: + nSaveDamageType = SAVING_THROW_TYPE_NEGATIVE; + nVisualType = VFX_IMP_NEGATIVE_ENERGY; break; + + case DAMAGE_TYPE_POSITIVE: + nSaveDamageType = SAVING_THROW_TYPE_POSITIVE; + nVisualType = VFX_IMP_HOLY_AID; break; + + case DAMAGE_TYPE_SONIC: + nSaveDamageType = SAVING_THROW_TYPE_SONIC; + nVisualType = VFX_IMP_SILENCE; break; + } + + //apply Energy Draconic Aura bonus + if(nEnergyAura < 0) nEnergyAura = 0; + nSaveDC += nEnergyAura; + + if(BreathUsed.nOverrideSpecial == BREATH_TOPAZ) + nVisualType = VFX_IMP_POISON_L; + + if(BreathUsed.bBarrier) + { + //2d6 fire damage if the breath doesn't do damage + if(BreathUsed.nOverrideSpecial == BREATH_SHADOW + || BreathUsed.nOverrideSpecial == BREATH_SLEEP + || BreathUsed.nOverrideSpecial == BREATH_SLOW + || BreathUsed.nOverrideSpecial == BREATH_PARALYZE + || BreathUsed.nOverrideSpecial == BREATH_WEAKENING) + { + BreathUsed.nDiceType = 6; + BreathUsed.nDiceNumber = 2; + BreathUsed.nDamageType = DAMAGE_TYPE_FIRE; + } + + //fire damage if damage isn't energy damage + if(BreathUsed.nDamageType == DAMAGE_TYPE_MAGICAL || BreathUsed.nDamageType == DAMAGE_TYPE_BLUDGEONING) + BreathUsed.nDamageType = DAMAGE_TYPE_FIRE; + + //get the breath parameters and pass to the wall's scripts + SetLocalInt(BreathUsed.oDragon, "BarrierDiceType", BreathUsed.nDiceType); + SetLocalInt(BreathUsed.oDragon, "BarrierDiceNumber", BreathUsed.nDiceNumber); + SetLocalInt(BreathUsed.oDragon, "BarrierDamageType", BreathUsed.nDamageType); + + //create the wall + effect eAOE = EffectAreaOfEffect(AOE_PER_WALLBREATH); + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAOE, lTargetArea, RoundsToSeconds(d4())); + + //this overrides all other breath effects, so exit + return; + } + + + //roll damage + switch(BreathUsed.nDiceType) + { + case 4: + nDamage = d4(BreathUsed.nDiceNumber); break; + case 6: + nDamage = d6(BreathUsed.nDiceNumber); break; + case 8: + nDamage = d8(BreathUsed.nDiceNumber); break; + case 10: + nDamage = d10(BreathUsed.nDiceNumber); break; + } + if(DEBUG) DoDebug("prc_inc_breath: Rolling damage: " + IntToString(BreathUsed.nDiceNumber) + + "d" + IntToString(BreathUsed.nDiceType) + "= " + IntToString(nDamage)); + + //Shadow breath does 1 neg level instead of damage + if(BreathUsed.nOverrideSpecial == BREATH_SHADOW) + nDamage = 1; + + //adjust for metabreaths + if(BreathUsed.bEnlarge) + fRange = fRange * 1.5; + if(BreathUsed.nHeighten > 0) + nSaveDC += BreathUsed.nHeighten; + if(BreathUsed.bMaximize) + nDamage = BreathUsed.nDiceType * BreathUsed.nDiceNumber; + if(BreathUsed.bSpread) + { + fRange = PRCGetCreatureSize(BreathUsed.oDragon) * 5.0; + if(fRange < 1.0) fRange = 5.0; + fRange = FeetToMeters(fRange); + nBreathShape = SHAPE_SPHERE; + lTargetArea = GetLocation(BreathUsed.oDragon); + eVis = EffectVisualEffect(VFX_FNF_DRAGBREATHGROUND); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, lTargetArea); + } + + //Lingering Breath's effects are 1/2 of normal when lingering + if(bLinger) + { + nDamage /= 2; + BreathUsed.nDiceNumber /= 2; + } + + //DCs for Tempest Breath + switch(PRCGetCreatureSize(BreathUsed.oDragon)) + { + case CREATURE_SIZE_LARGE: + nKnockdownDC = 15; break; + + case CREATURE_SIZE_HUGE: + nKnockdownDC = 18; break; + + case CREATURE_SIZE_GARGANTUAN: + nKnockdownDC = 20; break; + + case CREATURE_SIZE_COLOSSAL: + nKnockdownDC = 30; break; + + } + + //adjust for breath channeling + if(BreathUsed.bEntangling) + nDamage = nDamage / 2; + + /* Begin application */ + int nObjectFilter = OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE; + //Adept's Breath of Bahamut does not affect objects + if(GetSpellId() == BREATH_BAHAMUT_LINE) nObjectFilter = OBJECT_TYPE_CREATURE; + + //Get first target in spell area + object oTarget = GetFirstObjectInShape(nBreathShape, fRange, lTargetArea, TRUE, nObjectFilter, vSourceOfBreath); + if(DEBUG) DoDebug("prc_inc_breath: Target Name: " + GetName(oTarget)); + while(GetIsObjectValid(oTarget)) + { + if(DEBUG) DoDebug("prc_inc_breath: Valid Target: " + GetName(oTarget)); + if(oTarget != BreathUsed.oDragon && !IsBreathProtected(BreathUsed.oDragon, oTarget) && spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, BreathUsed.oDragon)) + { + if(DEBUG) DoDebug("prc_inc_breath: Not Self, Not Protected"); + //Adjust the damage based on the Reflex Save, Evasion and Improved Evasion. + //Determine effect delay + fDelay = GetDistanceBetween(BreathUsed.oDragon, oTarget)/20; + + //check for Shaped Breath effect + if(IsAdeptBreath() && (nShapedSpotsUsed < 4) && !GetIsReactionTypeHostile(oTarget) && GetLocalInt(BreathUsed.oDragon, "AdeptShapedBreath")) + { + if(DEBUG) DoDebug("prc_inc_breath: Entering Shaped Breath. Slots used: " + IntToString(nShapedSpotsUsed)); + nShapedSpotsUsed++; + } + + //Adept's Sickening Breath breath effect + else if(BreathUsed.nOverrideSpecial == BREATH_SICKENING) + { + if(DEBUG) DoDebug("prc_inc_breath: sickening breath attack"); + + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + //2 rounds on a failed save + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE)) + { + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_POISON_L), oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSickened(), oTarget, 12.0)); + } + //1 round otherwise + else + { + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_POISON_L), oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSickened(), oTarget, 6.0)); + } + } + + //Brass alternate breath - sleep + else if(BreathUsed.nOverrideSpecial == BREATH_SLEEP) + { + if(DEBUG) DoDebug("prc_inc_breath: sleep breath attack"); + //prepare effects + effect eBreath = EffectSleep(); + effect eVis = EffectVisualEffect(VFX_IMP_SLEEP); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + effect eLink = EffectLinkEffects(eBreath, eDur); + + //get duration + int nSleepDuration = BreathUsed.nDiceNumber; + + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE)) + { + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nSleepDuration))); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + } + + //Copper alternate breath - slow + else if(BreathUsed.nOverrideSpecial == BREATH_SLOW) + { + if(DEBUG) DoDebug("prc_inc_breath: slowing breath attack"); + //prepare effects + effect eBreath = EffectSlow(); + effect eVis = EffectVisualEffect(VFX_IMP_SLOW); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + effect eLink = EffectLinkEffects(eBreath, eDur); + + //get duration + int nSlowDuration = BreathUsed.nDiceNumber; + + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE)) + { + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nSlowDuration))); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + //Adept breath effect still lasts for one round if save is made + else if(IsAdeptBreath()) + { + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(1))); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + } + + //Gold alternate breath - drains strength + else if(BreathUsed.nOverrideSpecial == BREATH_WEAKENING) + { + if(DEBUG) DoDebug("prc_inc_breath: weakening breath attack"); + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE)) + { + //Set Damage and VFX - Bioware Gold used VFX_IMP_REDUCE_ABILITY_SCORE originally + eVis = EffectVisualEffect(VFX_IMP_POISON_L); + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + //adept breath penalty only last 4 rounds on failure, gold breath has no duration + if(IsAdeptBreath()) + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAbilityDecrease(ABILITY_STRENGTH, BreathUsed.nDiceNumber), oTarget, 24.0)); + else + DelayCommand(fDelay, ApplyAbilityDamage(oTarget, ABILITY_STRENGTH, BreathUsed.nDiceNumber, DURATION_TYPE_PERMANENT, TRUE)); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + //Adept breath still happens for 2 rounds on successful save + else if(IsAdeptBreath()) + { + //Set Damage and VFX - Bioware Gold used VFX_IMP_REDUCE_ABILITY_SCORE originally + eVis = EffectVisualEffect(VFX_IMP_POISON_L); + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAbilityDecrease(ABILITY_STRENGTH, BreathUsed.nDiceNumber), oTarget, 12.0)); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + bCanCling = TRUE; + } + + //Silver alternate breath - paralyze + else if(BreathUsed.nOverrideSpecial == BREATH_PARALYZE) + { + if(DEBUG) DoDebug("prc_inc_breath: paralyzing breath attack"); + //prepare effects + effect eBreath = EffectParalyze(); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + effect eDur2 = EffectVisualEffect(VFX_DUR_PARALYZE_HOLD); + effect eParal = EffectVisualEffect(VFX_DUR_PARALYZED); + + effect eLink = EffectLinkEffects(eBreath, eDur); + eLink = EffectLinkEffects(eLink, eDur2); + eLink = EffectLinkEffects(eLink, eParal); + + //get duration + int nParalDuration = BreathUsed.nDiceNumber; + + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE)) + { + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nParalDuration))); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + } + + //Shadow Dragon breath - drains levels + else if(BreathUsed.nOverrideSpecial == BREATH_SHADOW) + { + if(DEBUG) DoDebug("prc_inc_breath: shadow breath attack"); + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + int nLevelDrain = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, SAVING_THROW_TYPE_NEGATIVE); + + if (nLevelDrain > 0) + { + //Set Damage and VFX + eBreath = EffectNegativeLevel(nLevelDrain); + eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget)); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + } + } + + //Swift Wing Breath of Life - Positive to Undead only, heals living creatures + else if(BreathUsed.nOverrideSpecial == BREATH_SWIFT_WING) + { + if(DEBUG) DoDebug("prc_inc_breath: swift wing breath attack"); + if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || (GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD)) + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + + nAdjustedDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, SAVING_THROW_TYPE_POSITIVE); + + if (nAdjustedDamage > 0) + { + //Set Damage and VFX + eBreath = EffectDamage(nAdjustedDamage, BreathUsed.nDamageType); + eVis = EffectVisualEffect(VFX_IMP_SUNSTRIKE); + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget)); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + bCanCling = TRUE; + } + } + } + + //normal damaging-type breath + else + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId())); + if(DEBUG) DoDebug("prc_inc_breath: normal breath attack"); + + if(BreathUsed.nSaveUsed == SAVING_THROW_FORT) + { + nAdjustedDamage = nDamage; + //make a fort save + if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, nSaveDamageType)) + { + //Mettle is Evasion for Fort saves + if (GetHasMettle(oTarget, SAVING_THROW_FORT)) + nAdjustedDamage = 0; + + nAdjustedDamage /= 2; + } + } + else + nAdjustedDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, nSaveDamageType); + + if (nAdjustedDamage > 0) + { + if(DEBUG) DoDebug("prc_inc_breath: Damage > 0"); + //Set Damage and VFX + if(BreathUsed.nOverrideSpecial == BREATH_PYROCLASTIC) + { + int chSaveth; + int chVisual; + int eleRoll; + + int nNumDice = d2(); + //Sets the random Element factor of the Chaos Dragons Breath Weapon. + //Affects damage, saving throw, and impact visual. + if (nNumDice==1) + { + chSaveth = SAVING_THROW_TYPE_SONIC; + chVisual = VFX_IMP_SILENCE; + } + else if (nNumDice==2) + { + chSaveth = SAVING_THROW_TYPE_FIRE; + chVisual = VFX_IMP_FLAME_M; + } + + if(GetLocalInt(oTarget, "DragonWard")) + nAdjustedDamage -= 40; + + effect eBreath1 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_FIRE); + effect eBreath2 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_SONIC); + eBreath = EffectLinkEffects(eBreath1, eBreath2); + eVis = EffectVisualEffect(chVisual); + } + else + { + if(GetLocalInt(oTarget, "DragonWard")) + nAdjustedDamage -= 20; + eBreath = EffectDamage(nAdjustedDamage, BreathUsed.nDamageType); + eVis = EffectVisualEffect(nVisualType); + } + //Apply the VFX impact and effects + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget)); + SetLocalInt(oTarget, "AffectedByBreath", TRUE); + bCanCling = TRUE; + } + } + + //Knockdown check for Tempest Breath + if(BreathUsed.bTempest && (PRCGetCreatureSize(BreathUsed.oDragon) > CREATURE_SIZE_MEDIUM)) + { + if(DEBUG) DoDebug("prc_inc_breath: tempest breath attack"); + if(PRCGetCreatureSize(BreathUsed.oDragon) - PRCGetCreatureSize(oTarget) > 1) + { + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nKnockdownDC, SAVING_THROW_TYPE_NONE)) + { + effect eWindblown = EffectKnockdown(); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eWindblown, oTarget, 6.0)); + } + } + } + + //Breath of Life healing + if(oTarget != BreathUsed.oDragon && BreathUsed.nOverrideSpecial == BREATH_SWIFT_WING && MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD && MyPRCGetRacialType(oTarget) != RACIAL_TYPE_CONSTRUCT + && !(GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD)) + { + if(DEBUG) DoDebug("prc_inc_breath: breath of life"); + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId(), FALSE)); + //Adjust the damage based on the Reflex Save, Evasion and Improved Evasion. + //Determine effect delay + fDelay = GetDistanceBetween(BreathUsed.oDragon, oTarget)/20; + int nHeal = BreathUsed.nDiceNumber; + + //Warforged are only healed for half + if(GetIsWarforged(oTarget)) nHeal /= 2; + + eBreath = EffectHeal(nHeal); + effect eHealVis = EffectVisualEffect(VFX_IMP_HEALING_S); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget)); + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eHealVis, oTarget)); + } + + //Entangling Exhalation + if(GetLocalInt(oTarget, "AffectedByBreath") && BreathUsed.bEntangling) + { + if(DEBUG) DoDebug("prc_inc_breath: entangling breath attack"); + effect eEntangled = EffectEntangle(); + effect eEntangleVis = EffectVisualEffect(VFX_DUR_ENTANGLE); + eEntangled = EffectLinkEffects(eEntangled, eEntangleVis); + int nEntangleRounds = d4(); + //only does damage if the original breath did damage + if(BreathUsed.nDamageType > 0) + { + effect eDamage = EffectDamage(d6(), BreathUsed.nDamageType); + switch(nEntangleRounds) + { + case 4: + DelayCommand(fDelay + RoundsToSeconds(4), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); + case 3: + DelayCommand(fDelay + RoundsToSeconds(3), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); + case 2: + DelayCommand(fDelay + RoundsToSeconds(2), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); + case 1: + DelayCommand(fDelay + RoundsToSeconds(1), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); break; + } + } + + //but always entangles if the breath affects the target + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEntangled, oTarget, RoundsToSeconds(nEntangleRounds))); + } + + DeleteLocalInt(oTarget, "AffectedByBreath"); + + //Clinging Breath check + if(BreathUsed.nClinging > 0 && bCanCling) + { + if(DEBUG) DoDebug("prc_inc_breath: clinging breath attack"); + int nCount; + int AdjustedAbilityDamage = BreathUsed.nDiceNumber; + + for(nCount = 0; nCount < BreathUsed.nClinging; nCount++) + { + float fNewDelay = RoundsToSeconds(nCount + 1); + if(BreathUsed.nOverrideSpecial == BREATH_WEAKENING) + { + AdjustedAbilityDamage /= 2; + DelayCommand(fNewDelay, ApplyAbilityDamage(oTarget, ABILITY_STRENGTH, BreathUsed.nDiceNumber, DURATION_TYPE_PERMANENT, TRUE)); + if(AdjustedAbilityDamage = 1) nCount = BreathUsed.nClinging; + } + nAdjustedDamage /= 2; + if(BreathUsed.nOverrideSpecial == BREATH_PYROCLASTIC) + { + if(nAdjustedDamage < 3) nCount = BreathUsed.nClinging; + + effect eBreath1 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_FIRE); + effect eBreath2 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_SONIC); + effect eAfterBreath = EffectLinkEffects(eBreath1, eBreath2); + DelayCommand(fNewDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eAfterBreath, oTarget)); + } + else + { + if(nAdjustedDamage < 2) nCount = BreathUsed.nClinging; + effect eAfterBreath = EffectDamage(nAdjustedDamage, BreathUsed.nDamageType); + DelayCommand(fNewDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eAfterBreath, oTarget)); + } + DelayCommand(fNewDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + } + } + } + + //Get next target in spell area + if(DEBUG) DoDebug("prc_inc_breath: Next target"); + oTarget = GetNextObjectInShape(nBreathShape, fRange, lTargetArea, TRUE, nObjectFilter, vSourceOfBreath); + } + + //Lingering Breath check + if(BreathUsed.nLingering > 0) + { + int nCount; + int nLingerDuration = BreathUsed.nLingering; + BreathUsed.nLingering = 0; + BreathUsed.nClinging = 0; + vector vOrigin = GetPosition(BreathUsed.oDragon); + effect eGroundVis = EffectVisualEffect(VFX_FNF_DRAGBREATHGROUND); + + for(nCount = 0; nCount < nLingerDuration; nCount++) + { + float fNewDelay = RoundsToSeconds(nCount + 1); + DelayCommand(fNewDelay, ApplyBreath(BreathUsed, lTargetArea, TRUE, vOrigin.x, vOrigin.y, vOrigin.z)); + DelayCommand(fNewDelay, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eGroundVis, lTargetArea)); + } + } +} + +void PRCPlayDragonBattleCry() +{ + if(d100() > 50) + { + PlayVoiceChat(VOICE_CHAT_BATTLECRY1); + } + else + { + PlayVoiceChat(VOICE_CHAT_BATTLECRY2); + } +} diff --git a/src/include/prc_inc_burn.nss b/src/include/prc_inc_burn.nss new file mode 100644 index 0000000..30e8e4d --- /dev/null +++ b/src/include/prc_inc_burn.nss @@ -0,0 +1,92 @@ +/* For burning spells to use in abilities */ + +#include "prc_inc_spells" +#include "prc_getbest_inc" + +////////////////////////////////////////////////// +/* Function Prototypes */ +////////////////////////////////////////////////// + +// Burns a spell of the selected spell level +// Spell level picked by prc_burnselect.nss +// +// Returns FALSE if it failed, or the spell level if it worked +int BurnSpell(object oPC); + +// Burns a spell of the highest spell level the character has +// +// Returns FALSE if it failed, or the spell if it worked +int BurnBestSpell(object oPC); + +int BurnSpell(object oPC) +{ + int nSpellLevel = GetLocalInt(oPC, "BurnSpellLevel"); + int nSpell; + if (nSpellLevel == 0) + nSpell = GetBestL0Spell(oPC, -1); + else if (nSpellLevel == 1) + nSpell = GetBestL1Spell(oPC, -1); + else if (nSpellLevel == 2) + nSpell = GetBestL2Spell(oPC, -1); + else if (nSpellLevel == 3) + nSpell = GetBestL3Spell(oPC, -1); + else if (nSpellLevel == 4) + nSpell = GetBestL4Spell(oPC, -1); + else if (nSpellLevel == 5) + nSpell = GetBestL5Spell(oPC, -1); + else if (nSpellLevel == 6) + nSpell = GetBestL6Spell(oPC, -1); + else if (nSpellLevel == 7) + nSpell = GetBestL7Spell(oPC, -1); + else if (nSpellLevel == 8) + nSpell = GetBestL8Spell(oPC, -1); + else if (nSpellLevel == 9) + nSpell = GetBestL9Spell(oPC, -1); + + if (nSpell == -1) + { + FloatingTextStringOnCreature("You have no spells remaining of the selected level", oPC, FALSE); + return FALSE; + } + + if (DEBUG) FloatingTextStringOnCreature("Burning SpellID "+IntToString(nSpell), oPC, FALSE); + PRCDecrementRemainingSpellUses(oPC, nSpell); + if(GetLocalInt(oPC, "ReserveFeatsRunning")) + DelayCommand(0.1f, ExecuteScript("prc_reservefeat", oPC)); + return nSpellLevel; +} + +int BurnBestSpell(object oPC) +{ + int nSpell = GetBestL9Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL8Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL7Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL6Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL5Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL4Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL3Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL2Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL1Spell(oPC, -1); + if (nSpell == -1) + nSpell = GetBestL0Spell(oPC, -1); + + if (nSpell == -1) + { + FloatingTextStringOnCreature("You have no spells remaining", oPC, FALSE); + return FALSE; + } + + if (DEBUG) FloatingTextStringOnCreature("Burning SpellID "+IntToString(nSpell), oPC, FALSE); + PRCDecrementRemainingSpellUses(oPC, nSpell); + if(GetLocalInt(oPC, "ReserveFeatsRunning")) + DelayCommand(0.1f, ExecuteScript("prc_reservefeat", oPC)); + return nSpell; +} \ No newline at end of file diff --git a/src/include/prc_inc_castlvl.nss b/src/include/prc_inc_castlvl.nss new file mode 100644 index 0000000..876ac84 --- /dev/null +++ b/src/include/prc_inc_castlvl.nss @@ -0,0 +1,6743 @@ + +/** + * @file + * + * This file contains PRCGetCasterLevel() and all its accessory functions. + * Functions that modify caster level go in this include. Keep out irrelevent + * functions. If this ends up like prc_inc_spells, you get slapped. + */ + +//:: Updated for 8 class slots by Jaysyn 2024/02/05 + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the caster level when used in spells. You can use PRCGetCasterLevel() + * to determine a caster level from within a true spell script. In spell-like- + * abilities & items, it will only return GetCasterLevel. + * + * @param oCaster The creature casting the spell. + * + * @return The caster level the spell was cast at. + */ +int PRCGetCasterLevel(object oCaster = OBJECT_SELF); + +/** + * A lookup for caster level progression for divine and arcane base classes + * @return an int that can be used in caster level calculations note: these use int division + */ +int GetCasterLevelModifier(int nClass); + +/** + * To override for custom spellcasting classes. Looks for the + * local int "PRC_CASTERCLASS_OVERRIDE" on oCaster. If set, + * this is used as the casting class, else GetLastSpellCastClass() + * is used. + * + * @param oCaster The creature that last cast a spell + * + * @return The class used to cast the spell. + */ +int PRCGetLastSpellCastClass(object oCaster = OBJECT_SELF); + +/** + * Returns if the given class is an arcane class. + * + * Arcane base classes are *hardcoded* into here, so new arcane + * base classes need adding to this function. + * Note: PrCs with their own spellbook eg. assassin count as base casters for this function + * + * @param oCaster The creature to check (outsiders can have sorc caster levels) + * + * @return TRUE if nClass is an arcane spellcasting class, FALSE otherwise + */ +int GetIsArcaneClass(int nClass, object oCaster = OBJECT_SELF); + +/** + * Returns if the given class is an divine class. + * + * Divine base classes are *hardcoded* into here, so new divine + * base classes need adding to this function. + * Note: PrCs with their own spellbook eg. blackguard count as base casters for this function + * + * @param oCaster The creature to check (not currently used) + * + * @return TRUE if nClass is a divine spellcasting class, FALSE otherwise + */ +int GetIsDivineClass(int nClass, object oCaster = OBJECT_SELF); + +// Returns the best "natural" arcane levels of the PC in question. Does not +// consider feats that situationally adjust caster level. +int GetLevelByTypeArcane(object oCaster = OBJECT_SELF); + +// Returns the best "natural" divine levels of the PC in question. Does not +// consider feats that situationally adjust caster level. +int GetLevelByTypeDivine(object oCaster = OBJECT_SELF); + +/** + * Works out the total arcane caster levels from arcane PrCs. + * + * Arcane prestige classes are *hardcoded* into this function, so new arcane caster + * classes need adding to it. Rakshasa RHD count as sorc PrC levels if they also have some levels in sorc + * note: PrCs with their own spellbook eg. assassin are not PrCs for this function + * + * @param oCaster The creature to check + * @param nCastingClass Casting class + * + * @return Number of arcane caster levels contributed by PrCs. + */ +int GetArcanePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID); + +/** + * Works out the total divine caster levels from arcane PrCs. + * + * Divine prestige classes are *hardcoded* into this function, so new divine caster + * classes need adding to it. + * note: PrCs with their own spellbook eg. blackguard are not PrCs for this function + * + * @param oCaster The creature to check + * @param nCastingClass Casting class + + * @return Number of divine caster levels contributed by PrCs. + */ +int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID); + +/** + * Gets the position of the first arcane base class. + * + * @param oCaster The creature to check + * + * @return The position (1-8) of the first arcane *base* class of oCaster + */ +int GetFirstArcaneClassPosition(object oCaster = OBJECT_SELF); + +/** + * Gets the position of the first divine base class. + * + * @param oCaster The creature to check + * + * @return The position (1-8) of the first divine *base* class of oCaster + */ +int GetFirstDivineClassPosition(object oCaster = OBJECT_SELF); + +/** + * Gets the highest or first (by position) *base* arcane class type or, + * if oCaster has no arcane class levels, returns CLASS_TYPE_INVALID. + * + * This will get rakshasa RHD 'class' if oCaster doesn't have sorc levels. + * + * @param oCaster The creature to check + * + * @return CLASS_TYPE_* of first base arcane class or CLASS_TYPE_INVALID + */ +int GetPrimaryArcaneClass(object oCaster = OBJECT_SELF); + +/** + * Gets the highest first (by position) *base* divine class type or, + * if oCaster has no divine class levels, returns CLASS_TYPE_INVALID. + * + * @param oCaster The creature to check + * + * @return CLASS_TYPE_* of first base divine class or CLASS_TYPE_INVALID + */ +int GetPrimaryDivineClass(object oCaster = OBJECT_SELF); + +/** + * Gets the highest *base* arcane or divine class type or, + * if oCaster has no spellcasting class levels, returns CLASS_TYPE_INVALID. + * + * @param oCaster The creature to check + * + * @return CLASS_TYPE_* of first base arcane/divine class or CLASS_TYPE_INVALID + */ +int GetPrimarySpellcastingClass(object oCaster = OBJECT_SELF); + +/** + * Gets the caster level adjustment from the Practiced Spellcaster feats. + * + * @param oCaster The creature to check + * @param iCastingClass The CLASS_TYPE* that the spell was cast by. + * @param iCastingLevels The caster level for the spell calculated so far + * ie. BEFORE Practiced Spellcaster. + */ +int PracticedSpellcasting(object oCaster, int iCastingClass, int iCastingLevels); + +/** + * Returns the spell school of the spell passed to it. + * + * @param iSpellId The spell to get the school of. + * + * @return The SPELL_SCHOOL_* of the spell. + */ +int GetSpellSchool(int iSpellId); + +/** + * Healing spell filter. + * + * Gets if the given spell is a healing spell based on a hardcoded list. New + * healing spells need to be added to this. + * + * @author GaiaWerewolf + * @date 18 July 2005 + * + * @param nSpellId The spell to check + * + * @return TRUE if it is a healing spell, FALSE otherwise. + */ +int GetIsHealingSpell(int nSpellId); + +/** + * Gets the contribution of the archmage's High Arcana Spell Power + * feat to caster level. + * + * @param oCaster The creature to check + * + * @return caster level modifier from archmage Spell Power feats. + */ +int ArchmageSpellPower(object oCaster); + +/** + * Gets the caster level modifier from the Shadow Weave feat. + * + * Schools of Enchantment, Illusion, and Necromancy, and spells with the darkness + * descriptor altered by +1, Evocation or Transmutation (except spells with the + * darkness descriptor) altered by -1. + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier for Shadow Weave feat. + */ +int ShadowWeave(object oCaster, int iSpellID, int nSpellSchool = -1); + +/** + * Gets the caster level modifier from the Divination Power class feature. + * + * Divination spells +1/3 Unseen Seer levels, all others -1/3 Unseer Seer levels + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier for Divination Power feat. + */ +int DivinationPower(object oCaster, int nSpellSchool); + +/** + * Handles feats that modify caster level of spells with the fire + * descriptor. + * + * Currently this is Disciple of Meph's Fire Adept feat and Bloodline of Fire feat. + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for fire related feats. + */ +int FireAdept(object oCaster, int iSpellID); + +/** + * Handles feats that modify caster level of spells with the air + * descriptor. + * + * Currently this is the Air Mephling's Type feat + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for fire related feats. + */ +int AirAdept(object oCaster, int iSpellID); + +/** + * Handles feats that modify caster level of spells with the air + * descriptor. + * + * Currently this is the Air Mephling's Type feat + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for fire related feats. + */ +int WaterAdept(object oCaster, int iSpellID); + +/** + * Handles feats that modify caster level of spells with the earth + * descriptor. + * + * Currently this is Drift Magic feat. + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier for earth related feats. + */ +int DriftMagic(object oCaster, int iSpellID); + +/** + * Soulcaster boost to caster level based on invested essentia + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * + * @return Caster level modifier + */ +int Soulcaster(object oCaster, int iSpellID); + +/** + * Gets the caster level modifier from the Storm Magic feat. + * + * Get +1 caster level if raining or snowing in area + * + * @param oCaster The creature to check + * + * @return Caster level modifier for Storm Magic feat. + */ +int StormMagic(object oCaster); + +/** + * Gets the caster level modifier from the Cormanthyran Moon Magic feat. + * + * Get +1 caster level if outdoors, at night, with no rain. + * + * @param oCaster The creature to check + * + * @return Caster level modifier for Cormanthyran Moon Magic feat. + */ +int CormanthyranMoonMagic(object oCaster); + +/** + * Gets the caster level modifier from various domains. + * + * @param oCaster The creature to check + * @param nSpellID The spell ID of the spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier from domain powers + */ +int DomainPower(object oCaster, int nSpellID, int nSpellSchool = -1); + +/** + * Gets the caster level modifier from the Therapeutic Mantle Meld. + * + * @param oCaster The creature to check + * + * @return caster level modifier + */ +int TherapeuticMantle(object oCaster, int nSpellID); + +/** + * Gets the caster level modifier from the antipaladin's Death Knell SLA. + * + * @param oCaster The creature to check + * + * @return caster level modifier from the Death Knell SLA + */ +int DeathKnell(object oCaster); + +/** + * Gets the caster level modifier from the Draconic Power feat. + * + * Feat gives +1 to caster level. + * + * @param oCaster The creature to check + * + * @return caster level modifier from the Draconic power feat. + */ +int DraconicPower(object oCaster = OBJECT_SELF); + +/** + * Gets the caster level modifier from Song of Arcane Power effect. + * + * @param oCaster The creature to check + * + * @return caster level modifier from the Draconic power feat. + */ +int SongOfArcanePower(object oCaster = OBJECT_SELF); + +/** + * Gets the caster level modifier to necromancy spells for the + * True Necromancer PrC (all spellcasting levels are counted, both + * arcane and divine). + * + * @param oCaster The creature to check + * @param iSpellID The spell ID of the spell + * @param sType "ARCANE" or "DIVINE" spell + * @param nSpellSchool The spell school the cast spell is from + * if none is specified, uses GetSpellSchool() + * + * @return caster level modifier for True Necro + */ +int TrueNecromancy(object oCaster, int iSpellID, string sType, int nSpellSchool = -1); + +// Nentyar Hunter casting boost +int Nentyar(object oCaster, int nCastingClass); + +// +1 on spells that target armor or shields +int ShieldDwarfWarder(object oCaster); + +// +1 while this feat is active +int DarkSpeech(object oCaster); + +// Adds 1/2 level in all other casting classes. +int UrPriestCL(object oCaster, int nCastingClass); + +// Adds Druid levels to Blighter caster level +int BlighterCL(object oCaster, int nCastingClass); + +//ebonfowl: Adds CL boosts from reserve feats +int ReserveFeatCL(object oCaster, int iSpellId); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "prc_racial_const" +// Not needed as it has acccess via prc_inc_newip +//#include "prc_inc_nwscript" // gets inc_2da_cache, inc_debug, prc_inc_switch +#include "prc_inc_newip" +//#include "prc_inc_spells" +#include "prc_inc_descrptr" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +// stolen from prcsp_archmaginc.nss, modified to work in FireAdept() function +string _GetChangedElementalType(int nSpellID, object oCaster = OBJECT_SELF) +{ + string spellType = Get2DACache("spells", "ImmunityType", nSpellID);//lookup_spell_type(spell_id); + string sType = GetLocalString(oCaster, "archmage_mastery_elements_name"); + + if (sType == "") sType = spellType; + + return sType; +} + +//ebonfowl: Adding this function to check if a spell belongs to a given domain based on the Reserve Feat 2das +//Only works with Death, Destruction and War domains as only those domain 2das have been created +int GetIsFromDomain (int iSpellId, string sDomain) +{ + string sFile = "prc_desc_" + sDomain; + + int i; + int nListSpellID; + + for (i = 0; i < 15; i++) // Adjust max i to reflect something close to the highest row number in the 2das + { + nListSpellID = StringToInt(Get2DACache(sFile, "SpellID", i)); + if (nListSpellID == iSpellId) return TRUE; + } + return FALSE; +} + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +int GetCasterLevelModifier(int nClass) +{ + switch(nClass) // do not change to return zero as this is used as a divisor + { + // add in new base half-caster classes here + case CLASS_TYPE_HEXBLADE: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_ANTI_PALADIN: + return 2; + } + return 1; // normal progression +} + +int PRCGetCasterLevel(object oCaster = OBJECT_SELF) +{ + int nAdjust = GetLocalInt(oCaster, PRC_CASTERLEVEL_ADJUSTMENT);//this is for builder use + nAdjust += GetLocalInt(oCaster, "TrueCasterLens"); + nAdjust += GetHasSpellEffect(SPELL_VIRTUOSO_MAGICAL_MELODY, oCaster); + + // For when you want to assign the caster level. + int iReturnLevel = GetLocalInt(oCaster, PRC_CASTERLEVEL_OVERRIDE); + if (iReturnLevel) + { + if (DEBUG) DoDebug("PRCGetCasterLevel: found override caster level = "+IntToString(iReturnLevel)+" with adjustment = " + IntToString(nAdjust)+", original level = "+IntToString(GetCasterLevel(oCaster))); + return iReturnLevel+nAdjust; + } + + // if we made it here, iReturnLevel = 0; + + int iCastingClass = PRCGetLastSpellCastClass(oCaster); // might be CLASS_TYPE_INVALID + if(iCastingClass == CLASS_TYPE_SUBLIME_CHORD) + iCastingClass = GetPrimaryArcaneClass(oCaster); + int iSpellId = PRCGetSpellId(oCaster); + object oItem = PRCGetSpellCastItem(oCaster); + + // Item Spells + // this check is unreliable because of Bioware's implementation (GetSpellCastItem returns + // the last object from which a spell was cast, even if we are not casting from an item) + if(GetIsObjectValid(oItem)) + { + int nType = GetBaseItemType(oItem); + if(DEBUG) DoDebug("PRCGetCasterLevel: found valid item = "+GetName(oItem)); + // double check, just to make sure + if(GetItemPossessor(oItem) == oCaster) // likely item casting + { + if(GetPRCSwitch(PRC_STAFF_CASTER_LEVEL) + && ((nType == BASE_ITEM_MAGICSTAFF) || + (nType == BASE_ITEM_CRAFTED_STAFF)) + ) + { + iCastingClass = GetPrimarySpellcastingClass(oCaster); + } + else + { + //code for getting new ip type + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL) + { + int nSubType = GetItemPropertySubType(ipTest); + nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType)); + if(nSubType == iSpellId) + { + iReturnLevel = GetItemPropertyCostTableValue(ipTest); + if (DEBUG) DoDebug("PRCGetCasterLevel: caster level from item = "+IntToString(iReturnLevel)); + break; // exit the while loop + } + } + ipTest = GetNextItemProperty(oItem); + } + // if we didn't find a caster level on the item, it must be Bioware item casting + if(!iReturnLevel) + { + iReturnLevel = GetCasterLevel(oCaster); + if (DEBUG) DoDebug("PRCGetCasterLevel: bioware item casting with caster level = "+IntToString(iReturnLevel)); + } + } + + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) + { + if (DEBUG) DoDebug("PRCGetCasterLevel - Casting Item is a Wand at level "+IntToString(iReturnLevel)); + if (GetHasFeat(FEAT_RECKLESS_WAND_WIELDER, oCaster) && GetLocalInt(oCaster, "RecklessWand")) // This burns an extra charge to increase caster level by 2 + { + if (DEBUG) DoDebug("PRCGetCasterLevel - Reckless Wand Active"); + if (GetItemCharges(oItem) > 0) // Make sure we have an extra charge to burn + { + iReturnLevel += 2; + if (!GetLocalInt(oCaster, "RecklessWandDelay")) SetItemCharges(oItem, GetItemCharges(oItem)-1); + SetLocalInt(oCaster, "RecklessWandDelay", TRUE); + DelayCommand(0.5, DeleteLocalInt(oCaster, "RecklessWandDelay")); + if (DEBUG) DoDebug("PRCGetCasterLevel - Reckless Wand Triggered at level "+IntToString(iReturnLevel)); + } + } + if (GetHasFeat(FEAT_WAND_MASTERY, oCaster)) + iReturnLevel += 2; + } + } + if (DEBUG) DoDebug("PRCGetCasterLevel: total item casting caster level = "+IntToString(iReturnLevel)); + } + + // get spell school here as many of the following fns use it + int nSpellSchool = GetSpellSchool(iSpellId); + + // no item casting, and arcane caster? + if(!iReturnLevel && GetIsArcaneClass(iCastingClass, oCaster)) + { + iReturnLevel = GetLevelByClass(iCastingClass, oCaster) / GetCasterLevelModifier(iCastingClass); + + // Casting as a sorc but don't have any levels in the class + if(iCastingClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oCaster)) + { + int nRace = GetRacialType(oCaster); + + //if the player has sorcerer levels, then it counts as a prestige class + //otherwise use RHD instead of sorc levels + if(nRace == RACIAL_TYPE_RAKSHASA) + iReturnLevel = GetLevelByClass(CLASS_TYPE_OUTSIDER); + else if(nRace == RACIAL_TYPE_DRIDER) + iReturnLevel = GetLevelByClass(CLASS_TYPE_ABERRATION); + else if(nRace == RACIAL_TYPE_ARKAMOI) + iReturnLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS); + else if(nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + iReturnLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS); + else if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + iReturnLevel = GetLevelByClass(CLASS_TYPE_MONSTROUS)*3/4; + else if(nRace == RACIAL_TYPE_MARRUTACT) + iReturnLevel = (GetLevelByClass(CLASS_TYPE_MONSTROUS)*6/7)-1; + else if(nRace == RACIAL_TYPE_ARANEA) + iReturnLevel = GetLevelByClass(CLASS_TYPE_SHAPECHANGER); + + } + // Casting as a bard but don't have any levels in the class + if(iCastingClass == CLASS_TYPE_BARD && !GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + int nRace = GetRacialType(oCaster); + + //if the player has bard levels, then it counts as a prestige class + //otherwise use RHD instead of bard levels + if(nRace == RACIAL_TYPE_GLOURA) + iReturnLevel = GetLevelByClass(CLASS_TYPE_FEY); + } + + //Spell Rage ability + if(GetHasSpellEffect(SPELL_SPELL_RAGE, oCaster) + && (nSpellSchool == SPELL_SCHOOL_ABJURATION + || nSpellSchool == SPELL_SCHOOL_CONJURATION + || nSpellSchool == SPELL_SCHOOL_EVOCATION + || nSpellSchool == SPELL_SCHOOL_NECROMANCY + || nSpellSchool == SPELL_SCHOOL_TRANSMUTATION)) + { + iReturnLevel = GetHitDice(oCaster); + } + + else if(GetPrimaryArcaneClass(oCaster) == iCastingClass) + iReturnLevel += GetArcanePRCLevels(oCaster, iCastingClass); + else if(GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster)) + iReturnLevel += GetArcanePRCLevels(oCaster, iCastingClass); + + iReturnLevel += PracticedSpellcasting(oCaster, iCastingClass, iReturnLevel); + + iReturnLevel += TrueNecromancy(oCaster, iSpellId, "ARCANE", nSpellSchool) + + ShadowWeave(oCaster, iSpellId, nSpellSchool) + + FireAdept(oCaster, iSpellId) + + AirAdept(oCaster, iSpellId) + + WaterAdept(oCaster, iSpellId) + + ArchmageSpellPower(oCaster) + + StormMagic(oCaster) + + CormanthyranMoonMagic(oCaster) + + DomainPower(oCaster, iSpellId, nSpellSchool) + + DivinationPower(oCaster, nSpellSchool) + + DeathKnell(oCaster) + + DraconicPower(oCaster) + + DriftMagic(oCaster, iSpellId) + + Soulcaster(oCaster, iSpellId) + + TherapeuticMantle(oCaster, iSpellId) + + DarkSpeech(oCaster) + + ShieldDwarfWarder(oCaster) + + SongOfArcanePower(oCaster) + + ReserveFeatCL(oCaster, iSpellId); + + if (GetLocalInt(oCaster, "CaptureMagic")) + { + iReturnLevel += GetLocalInt(oCaster, "CaptureMagic"); + DeleteLocalInt(oCaster, "CaptureMagic"); + } + + // Get stance level bonus for Jade Phoenix Mage + if(GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster)) + { + if (_GetChangedElementalType(iSpellId, oCaster) == "Fire" && GetLocalInt(oCaster, "ToB_JPM_FireB")) + iReturnLevel += 3; + iReturnLevel += GetLocalInt(oCaster, "ToB_JPM_MystP"); + } + // Abjurant Champion uses its Base AB as Caster Level if higher + if(GetHasFeat(FEAT_MARTIAL_ARCANIST)) + { + //Get the caster's base AB + int nBaseAB = GetBaseAttackBonus(oCaster); + if(nBaseAB > iReturnLevel) + { + iReturnLevel = nBaseAB; + } + } + if (DEBUG) DoDebug("PRCGetCasterLevel: total arcane caster level = "+IntToString(iReturnLevel)); + } + + // no item casting and divine caster? + else if(!iReturnLevel && GetIsDivineClass(iCastingClass, oCaster)) + { + iReturnLevel = GetLevelByClass(iCastingClass, oCaster) / GetCasterLevelModifier(iCastingClass); + if(GetPrimaryDivineClass(oCaster) == iCastingClass) + iReturnLevel += GetDivinePRCLevels(oCaster, iCastingClass); + + iReturnLevel += PracticedSpellcasting(oCaster, iCastingClass, iReturnLevel); + + iReturnLevel += TrueNecromancy(oCaster, iSpellId, "DIVINE", nSpellSchool) + + ShadowWeave(oCaster, iSpellId, nSpellSchool) + + FireAdept(oCaster, iSpellId) + + StormMagic(oCaster) + + CormanthyranMoonMagic(oCaster) + + Nentyar(oCaster, iCastingClass) + + DomainPower(oCaster, iSpellId, nSpellSchool) + + DriftMagic(oCaster, iSpellId) + + AirAdept(oCaster, iSpellId) + + WaterAdept(oCaster, iSpellId) + + Soulcaster(oCaster, iSpellId) + + ShieldDwarfWarder(oCaster) + + DarkSpeech(oCaster) + + DeathKnell(oCaster) + + UrPriestCL(oCaster, iCastingClass) + + BlighterCL(oCaster, iCastingClass) + + ReserveFeatCL(oCaster, iSpellId); + + if (DEBUG) DoDebug("PRCGetCasterLevel: total divine caster level = "+IntToString(iReturnLevel)); + } + + //at this point it must be a SLA or similar + if(!iReturnLevel) + { + iReturnLevel = GetCasterLevel(oCaster); + if (DEBUG) DoDebug("PRCGetCasterLevel: bioware caster level = "+IntToString(iReturnLevel)); + } + + iReturnLevel -= GetLocalInt(oCaster, "WoLCasterPenalty"); + if (GetLocalInt(oCaster, "EldritchDisrupt")) + iReturnLevel -= 4; + if (GetLocalInt(oCaster, "EldritchVortex")) + iReturnLevel -= 4; + if (DEBUG) DoDebug("PRCGetCasterLevel: caster level pre adjust = "+IntToString(iReturnLevel)); + iReturnLevel += nAdjust; + if (DEBUG) DoDebug("PRCGetCasterLevel: total caster level = "+IntToString(iReturnLevel)); + + return iReturnLevel; +} + +int PRCGetLastSpellCastClass(object oCaster = OBJECT_SELF) +{ + // note that a barbarian has a class type constant of zero. So nClass == 0 could in principle mean + // that a barbarian cast the spell, However, barbarians cannot cast spells, so it doesn't really matter + // beware of Barbarians with UMD, though. Also watch out for spell like abilities + // might have to provide a fix for these (for instance: if(nClass == -1) nClass = 0; + int nClass = GetLocalInt(oCaster, PRC_CASTERCLASS_OVERRIDE); + if(nClass) + { + if(DEBUG) DoDebug("PRCGetLastSpellCastClass: found override caster class = "+IntToString(nClass)+", original class = "+IntToString(GetLastSpellCastClass())); + return nClass; + } + nClass = GetLastSpellCastClass(); + //if casting class is invalid and the spell was not cast form an item it was probably cast from the new spellbook + int NSB_Class = GetLocalInt(oCaster, "NSB_Class"); + if(nClass == CLASS_TYPE_INVALID && GetSpellCastItem() == OBJECT_INVALID && NSB_Class) + nClass = NSB_Class; + + if(DEBUG) DoDebug("PRCGetLastSpellCastClass: returning caster class = "+IntToString(nClass)+" NSB_Class = "+IntToString(NSB_Class)); + return nClass; +} + +int GetIsArcaneClass(int nClass, object oCaster = OBJECT_SELF) +{ + return nClass == CLASS_TYPE_ASSASSIN + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_BEGUILER + || nClass == CLASS_TYPE_CELEBRANT_SHARESS + || nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK + || nClass == CLASS_TYPE_DREAD_NECROMANCER + || nClass == CLASS_TYPE_DUSKBLADE + || nClass == CLASS_TYPE_HARPER + || nClass == CLASS_TYPE_HEXBLADE + || nClass == CLASS_TYPE_KNIGHT_WEAVE + || nClass == CLASS_TYPE_SHADOWLORD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_SUBLIME_CHORD + || nClass == CLASS_TYPE_SUEL_ARCHANAMACH + || nClass == CLASS_TYPE_WARMAGE + || nClass == CLASS_TYPE_WIZARD + || (nClass == CLASS_TYPE_SHAPECHANGER + && GetRacialType(oCaster) == RACIAL_TYPE_ARANEA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_OUTSIDER + && GetRacialType(oCaster) == RACIAL_TYPE_RAKSHASA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_ABERRATION + && GetRacialType(oCaster) == RACIAL_TYPE_DRIDER + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_ARKAMOI + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_HOBGOBLIN_WARSOUL + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_REDSPAWN_ARCANISS + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oCaster) == RACIAL_TYPE_MARRUTACT + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_FEY + && GetRacialType(oCaster) == RACIAL_TYPE_GLOURA + && !GetLevelByClass(CLASS_TYPE_BARD)); +} + +int GetIsDivineClass(int nClass, object oCaster = OBJECT_SELF) +{ + return nClass == CLASS_TYPE_ARCHIVIST + || nClass == CLASS_TYPE_BLACKGUARD + || nClass == CLASS_TYPE_BLIGHTER + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_HEALER + || nClass == CLASS_TYPE_JUSTICEWW + || nClass == CLASS_TYPE_KNIGHT_CHALICE + || nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE + || nClass == CLASS_TYPE_NENTYAR_HUNTER + || nClass == CLASS_TYPE_OCULAR + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER + || nClass == CLASS_TYPE_SHAMAN + || nClass == CLASS_TYPE_SLAYER_OF_DOMIEL + || nClass == CLASS_TYPE_SOHEI + || nClass == CLASS_TYPE_SOLDIER_OF_LIGHT + || nClass == CLASS_TYPE_UR_PRIEST + || nClass == CLASS_TYPE_VASSAL; +} + +int GetArcanePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) +{ + int nArcane; + int nClass; + int nRace = GetRacialType(oCaster); + + if (nCastingClass == CLASS_TYPE_BARD || GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + //:: Includes RHD as bard. If they started with bard levels, then it + //:: counts as a prestige class, otherwise RHD is used instead of bard levels. + if(nRace == RACIAL_TYPE_GLOURA) + nArcane += GetLevelByClass(CLASS_TYPE_FEY, oCaster); + + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + // if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_BARD, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + // if(GetHasFeat(FEAT_FMM_SPELLCASTING_BARD, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + + // if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_BARD, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + // if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_BARD, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + + // if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_BARD, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SUBLIME_CHORD_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_BARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + // if(GetHasFeat(FEAT_BONDED_SPELLCASTING_BARD, oCaster)) + // nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_BARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_BARD, oCaster)) + { + if (nClass) + { + nArcane += nClass - 3 + d6(); + } + } + } +//:: End Bard Arcane PrC casting calculations + + if(nCastingClass == CLASS_TYPE_BARD && nRace == RACIAL_TYPE_GLOURA && !GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + // if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + // if(GetHasFeat(FEAT_FMM_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + // if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + + // if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + // if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + + // if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + // if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_FEY, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_FEY, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + // if(GetHasFeat(FEAT_BONDED_SPELLCASTING_FEY, oCaster)) + // nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_FEY, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_FEY, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6(); } + } + } +//:: End Fey Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_ASSASSIN || GetLevelByClass(CLASS_TYPE_ASSASSIN, oCaster)) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_ASSASSIN, oCaster)) //:: Requires Assassin 4 + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + // if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + // if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + // if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster ); + + // if(GetHasFeat(FEAT_FMM_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + // if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + // if(GetHasFeat(FEAT_JPM_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + // if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + // if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_ASSASSIN, oCaster)) + // nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + +/* if(GetHasFeat(FEAT_WWOC_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); */ + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_ASSASSIN, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_ASSASSIN, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) + nArcane += nClass - 3 + d6(); + } + } +//:: End Assassin Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_BEGUILER) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_BEGUILER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_BEGUILER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_BEGUILER, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6(); } + } + } +//:: End Beguiler Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_CELEBRANT_SHARESS) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_BONDED_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + +/* if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); */ + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); */ + +/* if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); */ + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); */ + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BONDED_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + +/* if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_CELEBRANT_SHARESS, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6(); } + } + } +//:: End Celebrant of Sharess Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + +/* if(GetHasFeat(FEAT_DHEART_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); */ + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); */ + +/* if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); */ + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_CULTIST_PEAK, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_CULTIST_PEAK, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6();} + } + } +//:: End Cultist of the Shattered Peaks Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_DREAD_NECROMANCER) + { +/* if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION); */ + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_DNECRO, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_DNECRO, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_DNECRO, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6(); } + } + } +//:: End Dread Necromancer Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_DUSKBLADE) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + + if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_DUSKBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_DUSKBLADE, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6();} + } + } +//:: End Duskblade Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_HARPER) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_HARPER, oCaster)) //:: enter after 5th Harper Scout lvl + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + +/* if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); */ + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_HARPER, oCaster)) //:: enter after 5th Harper Scout lvl + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); */ + +/* if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + +/* if(GetHasFeat(FEAT_WWOC_SPELLCASTING_HARPER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); */ + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + +/* if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_HARPER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_HARPER, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6();} + } + } +//:: End Harper Scout Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_HEXBLADE) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); */ + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BONDED_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_HEXBLADE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_HEXBLADE, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6(); } + } + } +//:: End Hexblade Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_KNIGHT_WEAVE) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + +/* if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); */ + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_FMM_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + +/* if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); */ + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + +/* if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_KNIGHT_WEAVE, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); } + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End Knight of the Weave Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_SORCERER)) + { +//:: Includes RHD as sorcerer. If they already have sorcerer levels, then it +//:: counts as a prestige class, otherwise RHD is used instead of sorc levels. + if(nRace == RACIAL_TYPE_ARANEA) + nArcane += GetLevelByClass(CLASS_TYPE_SHAPECHANGER); + if(nRace == RACIAL_TYPE_RAKSHASA) + nArcane += GetLevelByClass(CLASS_TYPE_OUTSIDER); + if(nRace == RACIAL_TYPE_DRIDER) + nArcane += GetLevelByClass(CLASS_TYPE_ABERRATION); + if(nRace == RACIAL_TYPE_ARKAMOI) + nArcane += GetLevelByClass(CLASS_TYPE_MONSTROUS); + if(nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + nArcane += GetLevelByClass(CLASS_TYPE_MONSTROUS); + if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + nArcane += GetLevelByClass(CLASS_TYPE_MONSTROUS)*3/4; + if(nRace == RACIAL_TYPE_MARRUTACT) + nArcane += (GetLevelByClass(CLASS_TYPE_MONSTROUS)*6/7)-1; + + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_FMM_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BONDED_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SORCERER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SORCERER, oCaster)) + { int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); } + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End Sorcerer Arcane PrC casting calculations + + + if(nCastingClass == CLASS_TYPE_SORCERER && nRace == RACIAL_TYPE_DRIDER + || nRace == RACIAL_TYPE_ARKAMOI + || nRace == RACIAL_TYPE_MARRUTACT + || nRace == RACIAL_TYPE_REDSPAWN_ARCANISS + || nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL + || nRace == RACIAL_TYPE_RAKSHASA + || nRace == RACIAL_TYPE_ARANEA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + { +//:: Adding PrC caster levels to the racial caster level. + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ALIENIST_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ALIENIST_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ALIENIST_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ANIMA_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ANIMA_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ANIMA_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_MHARPER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_MHARPER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_MHARPER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_CMANCER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_CMANCER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_CMANCER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_DHEART_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_DHEART_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_DHEART_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_DSONG_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_DSONG_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_DSONG_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_FMM_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_FMM_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_FMM_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_FMM_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_HARPERM_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_HARPERM_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_HARPERM_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_JPM_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_JPM_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_JPM_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_MONSTROUS, oCaster) //:: Shouldn't be possible + || GetHasFeat(FEAT_MAESTER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_MAESTER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_MAESTER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ALCHEM_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ALCHEM_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ALCHEM_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_TNECRO_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_TNECRO_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_TNECRO_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_SORCERER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_UNSEEN_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_UNSEEN_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_UNSEEN_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_WWOC_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_WWOC_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_WWOC_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_AOTS_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_AOTS_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_AOTS_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_MONSTROUS, oCaster) //:: Shouldn't be possible + || GetHasFeat(FEAT_BSINGER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_BSINGER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_BSINGER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_MONSTROUS, oCaster) //:: Shouldn't be possible + || GetHasFeat(FEAT_HATHRAN_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_HATHRAN_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_HAVOC_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_HAVOC_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_HAVOC_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_SSWORD_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_SSWORD_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_SSWORD_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_GRAZZT_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_GRAZZT_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_GRAZZT_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_TIAMAT_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_TIAMAT_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_TIAMAT_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_WAYFARER_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_WAYFARER_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_WAYFARER_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_MONSTROUS, oCaster) //:: Shouldn't be possible + || GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SHAPECHANGER, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_MONSTROUS, oCaster) + || GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_ABERRATION, oCaster) + || GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_OUTSIDER, oCaster) + || GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SHAPECHANGER, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); } + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End Aberration / Monstrous / Outsider / Shapechanger Arcane PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_SUBLIME_CHORD) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_SUBLIME_CHORD, oCaster)) // no cantrips! + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_SUBLIME_CHORD, oCaster)) //: No familiar! + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SUBLIME_CHORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SUBLIME_CHORD, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); } + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End SUBLIME_CHORD Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_SUEL_ARCHANAMACH) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); */ + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SUEL_ARCHANAMACH, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); } + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End Suel Archanamach Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_SHADOWLORD) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_SHADOWLORD, oCaster)) //:: Enter after 4th lvl + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); */ + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + +/* if(GetHasFeat(FEAT_FMM_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + +/* if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); */ + +/* if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + +/* if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); */ + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BONDED_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SHADOWLORD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SHADOWLORD, oCaster)) + { + nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); + if (nClass) { nArcane += nClass - 3 + d6();} + } + } +//:: End Telflammar Shadowlord Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_WARMAGE) + { +/* if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oCaster); */ + +/* if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); */ + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + + if(GetHasFeat(FEAT_DHEART_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_FMM_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + +/* if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); */ + +/* if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); */ + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BONDED_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; */ + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_WARMAGE, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_WARMAGE, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster); } + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End Warmage Arcane PrC casting calculations + + if (nCastingClass == CLASS_TYPE_WIZARD) + { + if(GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION); + + if(GetHasFeat(FEAT_ALIENIST_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ALIENIST, oCaster); + + if(GetHasFeat(FEAT_ANIMA_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oCaster); + + if(GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCHMAGE, oCaster); + + if(GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ARCTRICK, oCaster); + + if(GetHasFeat(FEAT_MHARPER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_HARPER, oCaster); + + if(GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oCaster); + + if(GetHasFeat(FEAT_CMANCER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCaster); + + if(GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + +/* if(GetHasFeat(FEAT_DHEART_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_DRAGONHEART_MAGE, oCaster); */ + + if(GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_KNIGHT, oCaster); + + if(GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELDRITCH_THEURGE, oCaster); + + if(GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster); + + if(GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCaster); + + if(GetHasFeat(FEAT_FMM_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FMM, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster); + + if(GetHasFeat(FEAT_HARPERM_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_HARPERMAGE, oCaster); + + if(GetHasFeat(FEAT_JPM_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oCaster); + + if(GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAGEKILLER, oCaster); + + if(GetHasFeat(FEAT_MAESTER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MAESTER, oCaster); + + if(GetHasFeat(FEAT_ALCHEM_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MASTER_ALCHEMIST, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster); + + if(GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SPELLDANCER, oCaster); + + if(GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_WARMAGE, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCaster); + + if(GetHasFeat(FEAT_TNECRO_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + + if(GetHasFeat(FEAT_REDWIZ_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster); + + if(GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWLORD_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SHADOWLORD, oCaster); */ + +/* if(GetHasFeat(FEAT_SUBCHORD_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster); */ + + if(GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + + if(GetHasFeat(FEAT_UNSEEN_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + + if(GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oCaster); + + if(GetHasFeat(FEAT_WWOC_SPELLCASTING_WIZARD, oCaster)) + nArcane += GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oCaster); + + if(GetHasFeat(FEAT_AOTS_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BSINGER_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BLADESINGER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BONDED_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_DSONG_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_PALEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HAVOC_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SSWORD_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_SPELLSWORD, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_GRAZZT_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WAYFARER_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster) + 1) /2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_WIZARD, oCaster)) + nArcane += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + if(GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_WIZARD, oCaster)) + { nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oCaster);} + if (nClass) { nArcane += nClass - 3 + d6(); } + } +//:: End Wizard Arcane PrC casting calculations + + return nArcane; +} + +int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) +{ + int nDivine = 0; + + if (nCastingClass == CLASS_TYPE_ARCHIVIST) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_ARCHIVIST, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Archivist Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_BLACKGUARD) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_BLACKGUARD, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + +/* if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); */ + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + +/* if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + // if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_BLACKGUARD, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + +/* if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + +/* if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); */ + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); */ + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_BLACKGUARD, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Blackguard Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_BLIGHTER) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_BLIGHTER, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + +/* if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); */ + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + +/* if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); */ + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + +/* if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + +/* if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); */ + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); */ + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_OLLAM, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster + 1) / 2 */ + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_BLIGHTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Blighter Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_CLERIC) + { + + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_CLERIC, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_CLERIC, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Cleric Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_DRUID) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_DRUID, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + /* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); */ + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + // if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_DRUID, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + +/* if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + +/* if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); */ + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); */ + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + // if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_DRUID, oCaster)) + // nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + +/* if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; */ + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_DRUID, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Druid Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_FAVOURED_SOUL) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_FAVOURED_SOUL, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + // if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_FAVOURED_SOUL, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Favoured Soul Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_HEALER) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_HEALER, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); */ + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + // if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_HEALER, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + // if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_HEALER, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); */ + +/* if(GetHasFeat(FEAT_BFZ_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_HEALER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Healer Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_JUSTICEWW) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_JUSTICEWW, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + // if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_JUSTICEWW, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + + // if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_JUSTICEWW, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + // if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_JUSTICEWW, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_JUSTICEWW, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Justice of Weald & Woe Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_KNIGHT_CHALICE) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); */ + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + // if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + +/* if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; ; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_KNIGHT_CHALICE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Knight of the Chalice Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); */ + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + // if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Knight of the Middle Circle Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_NENTYAR_HUNTER) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); */ + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + // if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + // if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); */ + +/* if(GetHasFeat(FEAT_BFZ_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Nentyar Hunter Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_OCULAR) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_OCULAR, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + // if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_OCULAR, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + +/* if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); */ + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + +/* if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + +/* if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_OCULAR, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + // if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_OCULAR, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + +/* if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); */ + + // if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_OCULAR, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); */ + + // if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_OCULAR, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + // if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_OCULAR, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + // if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_OCULAR, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_OCULAR, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster + 1) / 2 */ + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + +/* if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_OLLAM, oCaster + 1) / 2 */ + +/* if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_OCULAR, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_OCULAR, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + } +//:: End Ocular Adept Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_PALADIN) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_PALADIN, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + // if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_PALADIN, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + // if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_PALADIN, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + +/* if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); */ + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_PALADIN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Paladin Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_RANGER) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_RANGER, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_RANGER, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Ranger Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_SHAMAN) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_OASHAMAN, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + + if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Shaman Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_SLAYER_OF_DOMIEL) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_DOMIEL, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); */ + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + // if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_DOMIEL, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_DOMIEL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_DOMIEL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Slayer of Domiel Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_SOHEI) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_SOHEI, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + + if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + +/* if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); */ + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + // if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_SOHEI, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + + // if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_SOHEI, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); + + if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + + // if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_SOHEI, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + +/* if(GetHasFeat(FEAT_RUBY_VINDICATOR_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oCaster); */ + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); */ + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_ORCUS, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SOHEI, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Sohei Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_SOLDIER_OF_LIGHT) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_SOL, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); */ + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + // if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_SOL, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); */ + + if(GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + +/* if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SOL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + +/* if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); */ + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster);*/ + + /*if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_SOL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SOL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_OLLAM, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_SOL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SOL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Soldier of Light Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_UR_PRIEST) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_UR_PRIEST, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); */ + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + + if(GetHasFeat(FEAT_ELDISCIPLE_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + +/* if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); */ + +/* if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + + if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); */ + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + + if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + +/* if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); */ + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + +/* if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); */ + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_KORD_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_OLLAM, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster + 1) / 2 + + if(GetHasFeat(FEAT_TIAMAT_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_UR_PRIEST, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; + + } +//:: End Ur-Priest Divine PrC casting calculations + + + if (nCastingClass == CLASS_TYPE_VASSAL) + { + if (!GetHasFeat(FEAT_SF_CODE, oCaster) && GetHasFeat(FEAT_SACREDFIST_SPELLCASTING_VASSAL, oCaster)) + { + nDivine += GetLevelByClass(CLASS_TYPE_SACREDFIST, oCaster); + } + +/* if(GetHasFeat(FEAT_BLIGHTLORD_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + + if(GetHasFeat(FEAT_COMBAT_MEDIC_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); */ + + if(GetHasFeat(FEAT_CONTEMPLATIVE_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCaster); + +/* if(GetHasFeat(FEAT_FORESTMASTER_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCaster); */ + + // if(GetHasFeat(FEAT_FISTRAZIEL_SPELLCASTING_VASSAL, oCaster)) + // nDivine += GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oCaster); + +/* if(GetHasFeat(FEAT_HEARTWARDER_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCaster); + + if(GetHasFeat(FEAT_HIEROPHANT_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HIEROPHANT, oCaster); */ + + if(GetHasFeat(FEAT_HOSPITALER_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster); + +/* if(GetHasFeat(FEAT_MASTER_OF_SHROUDS_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); */ + +/* if(GetHasFeat(FEAT_MORNINGLORD_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); */ + + if(GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_PSYCHIC_THEURGE_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCaster); + + if(GetHasFeat(FEAT_RUNECASTER_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + if(GetHasFeat(FEAT_SACREDPURIFIER_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + + if(GetHasFeat(FEAT_SAPPHIRE_HIERARCH_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oCaster); + + if(GetHasFeat(FEAT_SHADOWBANE_STALKER_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER,oCaster); + +/* if(GetHasFeat(FEAT_STORMLORD_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_STORMLORD, oCaster); */ + + if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + +/* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_BFZ_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; + + if(GetHasFeat(FEAT_HATHRAN_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_KORD_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_OLLAM_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_OLLAM, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_ORCUS_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_ORCUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_SHINING_BLADE_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_SHINING_BLADE, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_TEMPUS_SPELLCASTING_VASSAL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_TEMPUS, oCaster + 1) / 2 */ + + if(GetHasFeat(FEAT_WARPRIEST_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_WARPRIEST, oCaster) + 1) / 2; + +/* if(GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_VASSAL, oCaster)) + nDivine += (GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster) + 1) / 3; */ + + } +//:: End Vassal of Bahamut Divine PrC casting calculations + + return nDivine; +} + +int GetFirstArcaneClassPosition(object oCaster = OBJECT_SELF) +{ + int i; + for(i = 1; i <= 8; i++) + { + if(GetIsArcaneClass(GetClassByPosition(i, oCaster), oCaster)) + return i; + } + + return 0; +} + +int GetFirstDivineClassPosition(object oCaster = OBJECT_SELF) +{ + int i; + for(i = 1; i <= 8; i++) + { + if(GetIsDivineClass(GetClassByPosition(i, oCaster), oCaster)) + return i; + } + + return 0; +} + +int GetPrimaryArcaneClass(object oCaster = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int iArcanePos = GetFirstArcaneClassPosition(oCaster); + if (!iArcanePos) return CLASS_TYPE_INVALID; // no arcane casting class + + nClass = GetClassByPosition(iArcanePos, oCaster); + } + else + { + int nClassLvl = 0; + + int i, nClassTmp; + for(i = 1; i <= 8; i++) + { + nClassTmp = GetClassByPosition(i, oCaster); + if(GetIsArcaneClass(nClassTmp, oCaster) && nClassTmp != CLASS_TYPE_SUBLIME_CHORD) + { + if(GetLevelByClass(nClassTmp, oCaster) > nClassLvl) + { + nClass = nClassTmp; + nClassLvl = GetLevelByClass(nClass, oCaster); + } + } + } + if(!nClassLvl) + return CLASS_TYPE_INVALID; + } + + //raks, Arkamoi, driders and dragons cast as sorcs + if(nClass == CLASS_TYPE_OUTSIDER + || nClass == CLASS_TYPE_SHAPECHANGER + || nClass == CLASS_TYPE_ABERRATION + || nClass == CLASS_TYPE_DRAGON + || nClass == CLASS_TYPE_MONSTROUS) + nClass = CLASS_TYPE_SORCERER; + + if(nClass == CLASS_TYPE_FEY && GetRacialType(oCaster) == RACIAL_TYPE_GLOURA) + { + nClass = CLASS_TYPE_BARD; + } + + return nClass; +} + +int GetPrimaryDivineClass(object oCaster = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int iDivinePos = GetFirstDivineClassPosition(oCaster); + if (!iDivinePos) return CLASS_TYPE_INVALID; // no Divine casting class + + nClass = GetClassByPosition(iDivinePos, oCaster); + } + else + { + int i, nClassTmp, nClassLvl; + for(i = 1; i <= 8; i++) + { + nClassTmp = GetClassByPosition(i, oCaster); + if(GetIsDivineClass(nClassTmp, oCaster)) + { + if(GetLevelByClass(nClassTmp, oCaster) > nClassLvl) + { + nClass = nClassTmp; + nClassLvl = GetLevelByClass(nClass, oCaster); + } + } + } + if(!nClassLvl) + return CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetPrimarySpellcastingClass(object oCaster = OBJECT_SELF) +{ + int bFirst = GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE); + int nClass; + + int i, nClassTmp, nClassLvl; + for(i = 1; i <= 8; i++) + { + nClassTmp = GetClassByPosition(i, oCaster); + if(GetIsArcaneClass(nClassTmp, oCaster) + || GetIsDivineClass(nClassTmp, oCaster) + && nClassTmp != CLASS_TYPE_SUBLIME_CHORD) + { + if(bFirst) + { + return nClass; + } + else if(GetLevelByClass(nClassTmp, oCaster) > nClassLvl) + { + nClass = nClassTmp; + nClassLvl = GetLevelByClass(nClass, oCaster); + } + } + } + if(!nClassLvl) + return CLASS_TYPE_INVALID; + + return nClass; +} + +int PracticedSpellcasting(object oCaster, int iCastingClass, int iCastingLevels) +{ + int nFeat; + int iAdjustment = GetHitDice(oCaster) - iCastingLevels; + if (iAdjustment > 4) iAdjustment = 4; + if (iAdjustment < 0) iAdjustment = 0; + + switch(iCastingClass) + { + case CLASS_TYPE_BARD: nFeat = FEAT_PRACTICED_SPELLCASTER_BARD; break; + case CLASS_TYPE_SORCERER: nFeat = FEAT_PRACTICED_SPELLCASTER_SORCERER; break; + case CLASS_TYPE_WIZARD: nFeat = FEAT_PRACTICED_SPELLCASTER_WIZARD; break; + case CLASS_TYPE_CLERIC: nFeat = FEAT_PRACTICED_SPELLCASTER_CLERIC; break; + case CLASS_TYPE_DRUID: nFeat = FEAT_PRACTICED_SPELLCASTER_DRUID; break; + case CLASS_TYPE_PALADIN: nFeat = FEAT_PRACTICED_SPELLCASTER_PALADIN; break; + case CLASS_TYPE_RANGER: nFeat = FEAT_PRACTICED_SPELLCASTER_RANGER; break; + case CLASS_TYPE_ASSASSIN: nFeat = FEAT_PRACTICED_SPELLCASTER_ASSASSIN; break; + case CLASS_TYPE_BLACKGUARD: nFeat = FEAT_PRACTICED_SPELLCASTER_BLACKGUARD; break; + case CLASS_TYPE_KNIGHT_WEAVE: nFeat = FEAT_PRACTICED_SPELLCASTER_KOTW; break; + case CLASS_TYPE_OCULAR: nFeat = FEAT_PRACTICED_SPELLCASTER_OCULAR; break; + case CLASS_TYPE_HEXBLADE: nFeat = FEAT_PRACTICED_SPELLCASTER_HEXBLADE; break; + case CLASS_TYPE_DUSKBLADE: nFeat = FEAT_PRACTICED_SPELLCASTER_DUSKBLADE; break; + case CLASS_TYPE_HEALER: nFeat = FEAT_PRACTICED_SPELLCASTER_HEALER; break; + case CLASS_TYPE_KNIGHT_CHALICE: nFeat = FEAT_PRACTICED_SPELLCASTER_KNIGHT_CHALICE; break; + case CLASS_TYPE_NENTYAR_HUNTER: nFeat = FEAT_PRACTICED_SPELLCASTER_NENTYAR; break; + case CLASS_TYPE_VASSAL: nFeat = FEAT_PRACTICED_SPELLCASTER_VASSAL; break; + case CLASS_TYPE_UR_PRIEST: nFeat = FEAT_PRACTICED_SPELLCASTER_UR_PRIEST; break; + case CLASS_TYPE_SOLDIER_OF_LIGHT: nFeat = FEAT_PRACTICED_SPELLCASTER_SOLDIER_OF_LIGHT; break; + case CLASS_TYPE_SHADOWLORD: nFeat = FEAT_PRACTICED_SPELLCASTER_SHADOWLORD; break; + case CLASS_TYPE_JUSTICEWW: nFeat = FEAT_PRACTICED_SPELLCASTER_JUSTICEWW; break; + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: nFeat = FEAT_PRACTICED_SPELLCASTER_KNIGHT_MIDDLECIRCLE; break; + case CLASS_TYPE_SHAMAN: nFeat = FEAT_PRACTICED_SPELLCASTER_SHAMAN; break; + case CLASS_TYPE_SLAYER_OF_DOMIEL: nFeat = FEAT_PRACTICED_SPELLCASTER_SLAYER_OF_DOMIEL; break; + case CLASS_TYPE_SUEL_ARCHANAMACH: nFeat = FEAT_PRACTICED_SPELLCASTER_SUEL_ARCHANAMACH; break; + case CLASS_TYPE_FAVOURED_SOUL: nFeat = FEAT_PRACTICED_SPELLCASTER_FAVOURED_SOUL; break; + case CLASS_TYPE_SOHEI: nFeat = FEAT_PRACTICED_SPELLCASTER_SOHEI; break; + case CLASS_TYPE_CELEBRANT_SHARESS: nFeat = FEAT_PRACTICED_SPELLCASTER_CELEBRANT_SHARESS; break; + case CLASS_TYPE_WARMAGE: nFeat = FEAT_PRACTICED_SPELLCASTER_WARMAGE; break; + case CLASS_TYPE_DREAD_NECROMANCER: nFeat = FEAT_PRACTICED_SPELLCASTER_DREAD_NECROMANCER; break; + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: nFeat = FEAT_PRACTICED_SPELLCASTER_CULTIST; break; + case CLASS_TYPE_ARCHIVIST: nFeat = FEAT_PRACTICED_SPELLCASTER_ARCHIVIST; break; + case CLASS_TYPE_BEGUILER: nFeat = FEAT_PRACTICED_SPELLCASTER_BEGUILER; break; + case CLASS_TYPE_BLIGHTER: nFeat = FEAT_PRACTICED_SPELLCASTER_BLIGHTER; break; + case CLASS_TYPE_HARPER: nFeat = FEAT_PRACTICED_SPELLCASTER_HARPER; break; + default: return 0; + } + + if(GetHasFeat(nFeat, oCaster)) + return iAdjustment; + + return 0; +} + +int GetSpellSchool(int iSpellId) +{ + string sSpellSchool = Get2DACache("spells", "School", iSpellId);//lookup_spell_school(iSpellId); + + if (sSpellSchool == "A") return SPELL_SCHOOL_ABJURATION; + else if (sSpellSchool == "C") return SPELL_SCHOOL_CONJURATION; + else if (sSpellSchool == "D") return SPELL_SCHOOL_DIVINATION; + else if (sSpellSchool == "E") return SPELL_SCHOOL_ENCHANTMENT; + else if (sSpellSchool == "V") return SPELL_SCHOOL_EVOCATION; + else if (sSpellSchool == "I") return SPELL_SCHOOL_ILLUSION; + else if (sSpellSchool == "N") return SPELL_SCHOOL_NECROMANCY; + else if (sSpellSchool == "T") return SPELL_SCHOOL_TRANSMUTATION; + else return SPELL_SCHOOL_GENERAL; + + return -1; +} + +/*int GetIsHealingSpell(int nSpellId) +{ + if ( nSpellId == SPELL_CURE_CRITICAL_WOUNDS + || nSpellId == SPELL_CURE_LIGHT_WOUNDS + || nSpellId == SPELL_CURE_MINOR_WOUNDS + || nSpellId == SPELL_CURE_MODERATE_WOUNDS + || nSpellId == SPELL_CURE_SERIOUS_WOUNDS + || nSpellId == SPELL_GREATER_RESTORATION + || nSpellId == SPELL_HEAL + || nSpellId == SPELL_HEALING_CIRCLE + || nSpellId == SPELL_MASS_HEAL + || nSpellId == SPELL_MONSTROUS_REGENERATION + || nSpellId == SPELL_REGENERATE + //End of stock NWN spells + || nSpellId == SPELL_MASS_CURE_LIGHT + || nSpellId == SPELL_MASS_CURE_MODERATE + || nSpellId == SPELL_MASS_CURE_SERIOUS + || nSpellId == SPELL_MASS_CURE_CRITICAL + || nSpellId == SPELL_PANACEA + ) + return TRUE; + + return FALSE; +}*/ + +int ArchmageSpellPower(object oCaster) +{ + if(GetHasFeat(FEAT_SPELL_POWER_V, oCaster)) + return 5; + if(GetHasFeat(FEAT_SPELL_POWER_IV, oCaster)) + return 4; + if(GetHasFeat(FEAT_SPELL_POWER_III, oCaster)) + return 3; + if(GetHasFeat(FEAT_SPELL_POWER_II, oCaster)) + return 2; + if(GetHasFeat(FEAT_SPELL_POWER_I, oCaster)) + return 1; + + return 0; +} + +int ShadowWeave(object oCaster, int iSpellID, int nSpellSchool = -1) +{ + if(!GetHasFeat(FEAT_SHADOWWEAVE,oCaster)) + return 0; + + if (nSpellSchool == -1) + nSpellSchool = GetSpellSchool(iSpellID); + + // Bonus for spells of Enhancement, Necromancy and Illusion schools and spells with Darkness descriptor + if(nSpellSchool == SPELL_SCHOOL_ENCHANTMENT + || nSpellSchool == SPELL_SCHOOL_NECROMANCY + || nSpellSchool == SPELL_SCHOOL_ILLUSION + || GetHasDescriptor(iSpellID, DESCRIPTOR_DARKNESS)) + { + return 1; + } + // Penalty to spells of Evocation and Transmutation schools, except for those with Darkness descriptor + else if(nSpellSchool == SPELL_SCHOOL_EVOCATION + || nSpellSchool == SPELL_SCHOOL_TRANSMUTATION) + { + return -1; + } + + return 0; +} + +int AirAdept(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_AIR)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_AIR_MEPHLING, oCaster)) + nBoost += 1; + + return nBoost; +} + +int WaterAdept(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_WATER)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_WATER_MEPHLING, oCaster)) + nBoost += 1; + + return nBoost; +} + +int FireAdept(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_FIRE)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_FIRE_ADEPT, oCaster)) + nBoost += 1; + + if(GetHasFeat(FEAT_FIRE_MEPHLING, oCaster)) + nBoost += 1; + + if(GetHasFeat(FEAT_BLOODLINE_OF_FIRE, oCaster)) + nBoost += 2; + + if(GetRacialType(oCaster) == RACIAL_TYPE_REDSPAWN_ARCANISS) + nBoost += 2; + + return nBoost; +} + +int DriftMagic(object oCaster, int iSpellID) +{ + if(!GetHasDescriptor(iSpellID, DESCRIPTOR_EARTH)) + return 0; + + int nBoost = 0; + + if(GetHasFeat(FEAT_DRIFT_MAGIC, oCaster)) + nBoost += 1; + + if(GetHasFeat(FEAT_EARTH_MEPHLING, oCaster)) + nBoost += 1; + + return nBoost; +} +/*int DriftMagic(object oCaster, int iSpellID) +{ + if(GetHasDescriptor(iSpellID, DESCRIPTOR_EARTH) && GetHasFeat(FEAT_DRIFT_MAGIC, oCaster)) + return 1; + + else if(GetHasDescriptor(iSpellID, DESCRIPTOR_EARTH) && GetHasFeat(FEAT_EARTH_MEPHLING, oCaster)) + return 1; + + return 0; +}*/ + +int Soulcaster(object oCaster, int iSpellID) +{ + if(GetLocalInt(oCaster, "SpellEssentia"+IntToString(iSpellID))) + { + int nReturn = GetLocalInt(oCaster, "SpellEssentia"+IntToString(iSpellID)); + DelayCommand(1.0, DeleteLocalInt(oCaster, "SpellEssentia"+IntToString(iSpellID))); + return nReturn; + } + + return 0; +} + +int StormMagic(object oCaster) +{ + if(!GetHasFeat(FEAT_STORMMAGIC, oCaster) && !GetHasFeat(FEAT_FROZEN_MAGIC, oCaster)) return 0; + + int nBoost = 0; + + int nWeather = GetWeather(GetArea(oCaster)); + if(nWeather == WEATHER_RAIN && GetHasFeat(FEAT_STORMMAGIC, oCaster)) + nBoost += 1; + if(nWeather == WEATHER_SNOW && GetHasFeat(FEAT_STORMMAGIC, oCaster)) + nBoost += 1; + if(nWeather == WEATHER_SNOW && GetHasFeat(FEAT_FROZEN_MAGIC, oCaster)) + nBoost += 2; + if (GetLocalInt(GetArea(oCaster),"FrozenMagic") && GetHasFeat(FEAT_FROZEN_MAGIC, oCaster) && nWeather != WEATHER_SNOW) + nBoost += 2; + + return nBoost; +} + +int DivinationPower(object oCaster, int nSpellSchool) +{ + int nClass = GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oCaster); + if(!nClass) return 0; + + int nBoost = (nClass + 2) / 3; + + if (nSpellSchool != SPELL_SCHOOL_DIVINATION) + nBoost *= -1; // Negative if it's not a divination spell + + return nBoost; +} + +int CormanthyranMoonMagic(object oCaster) +{ + if (!GetHasFeat(FEAT_CORMANTHYRAN_MOON_MAGIC, oCaster)) return 0; + + object oArea = GetArea(oCaster); + + // The moon must be visible. Thus, outdoors, at night, with no rain. + if (GetWeather(oArea) != WEATHER_RAIN && GetWeather(oArea) != WEATHER_SNOW && + GetIsNight() && !GetIsAreaInterior(oArea)) + { + return 2; + } + return 0; +} + +int Nentyar(object oCaster, int nCastingClass) +{ + if (nCastingClass == CLASS_TYPE_NENTYAR_HUNTER) + { + int nBonus = GetLevelByClass(CLASS_TYPE_DRUID, oCaster) + GetLevelByClass(CLASS_TYPE_CLERIC, oCaster) + GetLevelByClass(CLASS_TYPE_RANGER, oCaster)/2; + return nBonus; + } + return 0; +} + +int ShieldDwarfWarder(object oCaster) +{ + if (!GetHasFeat(FEAT_SHIELD_DWARF_WARDER, oCaster)) return 0; + + object oTarget = PRCGetSpellTargetObject(); + // If it's an item that grants an AC bonus + int nBase = GetBaseItemType(oTarget); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD || nBase == BASE_ITEM_ARMOR) + return 1; + + return 0; +} + +int DarkSpeech(object oCaster) +{ + if (GetHasSpellEffect(SPELL_DARK_SPEECH_POWER, oCaster)) + { + ExecuteScript("prc_dark_power", oCaster); + return 1; + } + return 0; +} + +int DomainPower(object oCaster, int nSpellID, int nSpellSchool = -1) +{ + int nBonus = 0; + if (nSpellSchool == -1) + nSpellSchool = GetSpellSchool(nSpellID); + + // Boosts Caster level with the Illusion school by 1 + if (nSpellSchool == SPELL_SCHOOL_ILLUSION && GetHasFeat(FEAT_DOMAIN_POWER_GNOME, oCaster)) + nBonus += 1; + + // Boosts Caster level with the Illusion school by 1 + if (nSpellSchool == SPELL_SCHOOL_ILLUSION && GetHasFeat(FEAT_DOMAIN_POWER_ILLUSION, oCaster)) + nBonus += 1; + + // Boosts Caster level with healing spells + if (GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING) && GetHasFeat(FEAT_HEALING_DOMAIN_POWER, oCaster)) + nBonus += 1; + + // Boosts Caster level with the Divination school by 1 + if (nSpellSchool == SPELL_SCHOOL_DIVINATION && GetHasFeat(FEAT_KNOWLEDGE_DOMAIN_POWER, oCaster)) + nBonus += 1; + + // Boosts Caster level with evil spells by 1 + if (GetHasDescriptor(nSpellID, DESCRIPTOR_EVIL) && GetHasFeat(FEAT_EVIL_DOMAIN_POWER, oCaster)) + nBonus += 1; + + // Boosts Caster level with good spells by 1 + if (GetHasDescriptor(nSpellID, DESCRIPTOR_GOOD) && GetHasFeat(FEAT_GOOD_DOMAIN_POWER, oCaster)) + nBonus += 1; + + return nBonus; +} + +int TherapeuticMantle(object oCaster, int nSpellID) +{ + int nReturn; + // Boosts Caster level with healing spells + if (GetIsMeldBound(oCaster, MELD_THERAPEUTIC_MANTLE) == CHAKRA_SHOULDERS && GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) + nReturn = GetEssentiaInvested(oCaster, MELD_THERAPEUTIC_MANTLE); + + return nReturn; +} + +int DeathKnell(object oCaster) +{ + // If you do have the spell effect, return a +1 bonus to caster level + return GetHasSpellEffect(SPELL_DEATH_KNELL, oCaster); +} + +int DraconicPower(object oCaster = OBJECT_SELF) +{ + return GetHasFeat(FEAT_DRACONIC_POWER, oCaster); +} + +int SongOfArcanePower(object oCaster = OBJECT_SELF) +{ + int nBonus = GetLocalInt(oCaster, "SongOfArcanePower"); + DeleteLocalInt(oCaster, "SongOfArcanePower"); + + int nLevel = GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster); + nBonus += (nLevel + 2)/3; + + if(nBonus) + return nBonus; + + return 0; +} + +int TrueNecromancy(object oCaster, int iSpellID, string sType, int nSpellSchool = -1) +{ + int iTNLevel = GetLevelByClass(CLASS_TYPE_TRUENECRO, oCaster); + if (!iTNLevel) + return 0; + if (nSpellSchool == -1) + nSpellSchool = GetSpellSchool(iSpellID); + if (nSpellSchool != SPELL_SCHOOL_NECROMANCY) + return 0; + + if (sType == "ARCANE") + return GetLevelByTypeDivine(oCaster); // TN and arcane levels already added. + + if (sType == "DIVINE") + return GetLevelByTypeArcane(oCaster); + + return 0; +} + +int UrPriestCL(object oCaster, int nCastingClass) +{ + // Add 1/2 levels in all other casting classes except cleric + if (nCastingClass == CLASS_TYPE_UR_PRIEST) + { + int nTotal = 0; + int iFirstDivine = GetPrimaryDivineClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + /*if (iClass1 == CLASS_TYPE_PALADIN || iClass1 == CLASS_TYPE_RANGER) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_PALADIN || iClass2 == CLASS_TYPE_RANGER) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_PALADIN || iClass3 == CLASS_TYPE_RANGER) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + + if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster); + if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster); + if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + + if (!GetIsDivineClass(iClass1, oCaster) || iClass1 == CLASS_TYPE_UR_PRIEST) iClass1Lev = 0; + if (!GetIsDivineClass(iClass2, oCaster) || iClass2 == CLASS_TYPE_UR_PRIEST) iClass2Lev = 0; + if (!GetIsDivineClass(iClass3, oCaster) || iClass3 == CLASS_TYPE_UR_PRIEST) iClass3Lev = 0; + + nTotal += iClass1Lev + iClass2Lev + iClass3Lev; + if (DEBUG) DoDebug("UrPriestCL Divine - iClass1Lev "+IntToString(iClass1Lev)+" iClass2Lev "+IntToString(iClass2Lev)+" iClass3Lev "+IntToString(iClass3Lev));*/ + + int iFirstArcane = GetPrimaryArcaneClass(oCaster); + iClass1 = GetClassByPosition(1, oCaster); + iClass2 = GetClassByPosition(2, oCaster); + iClass3 = GetClassByPosition(3, oCaster); + iClass4 = GetClassByPosition(4, oCaster); + iClass5 = GetClassByPosition(5, oCaster); + iClass6 = GetClassByPosition(6, oCaster); + iClass7 = GetClassByPosition(7, oCaster); + iClass8 = GetClassByPosition(8, oCaster); + + iClass1Lev = GetLevelByPosition(1, oCaster); + iClass2Lev = GetLevelByPosition(2, oCaster); + iClass3Lev = GetLevelByPosition(3, oCaster); + iClass4Lev = GetLevelByPosition(4, oCaster); + iClass5Lev = GetLevelByPosition(5, oCaster); + iClass6Lev = GetLevelByPosition(6, oCaster); + iClass7Lev = GetLevelByPosition(7, oCaster); + iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster, iClass1); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster, iClass2); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster, iClass3); + if (iClass4 == iFirstArcane) iClass4Lev += GetArcanePRCLevels(oCaster, iClass4); + if (iClass5 == iFirstArcane) iClass5Lev += GetArcanePRCLevels(oCaster, iClass5); + if (iClass6 == iFirstArcane) iClass6Lev += GetArcanePRCLevels(oCaster, iClass6); + if (iClass7 == iFirstArcane) iClass7Lev += GetArcanePRCLevels(oCaster, iClass7); + if (iClass8 == iFirstArcane) iClass8Lev += GetArcanePRCLevels(oCaster, iClass8); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass5Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass6Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass7Lev); + + if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0; + + + nTotal += iClass1Lev + iClass2Lev + iClass3Lev + iClass4Lev + iClass5Lev + iClass6Lev + iClass7Lev + iClass8Lev; + + if (DEBUG) DoDebug("UrPriestCL Arcane - iClass1Lev "+IntToString(iClass1Lev)+" iClass2Lev " + +IntToString(iClass2Lev)+" iClass3Lev " + +IntToString(iClass3Lev)+" iClass4Lev " + +IntToString(iClass4Lev)+" iClass5Lev " + +IntToString(iClass5Lev)+" iClass6Lev " + +IntToString(iClass6Lev)+" iClass7Lev " + +IntToString(iClass7Lev)+" iClass8Lev " + +IntToString(iClass8Lev)); + + if (DEBUG) DoDebug("UrPriestCL Total - nTotal "+IntToString(nTotal)); + return nTotal/2; + } + return 0; +} + +int BlighterCL(object oCaster, int nCastingClass) +{ + if (nCastingClass == CLASS_TYPE_BLIGHTER) + { + int nBonus = GetLevelByClass(CLASS_TYPE_DRUID, oCaster); + return nBonus; + } + return 0; +} + +int ReserveFeatCL(object oCaster, int iSpellId) +{ + int nSpellSchool = GetSpellSchool(iSpellId); + int nCLBonus = 0; + + if (GetLocalInt(oCaster, "ReserveFeatsRunning") == TRUE) + { + if (GetHasDescriptor(iSpellId, DESCRIPTOR_ACID) && GetHasFeat(FEAT_ACIDIC_SPLATTER, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_FIRE) && GetHasFeat(FEAT_FIERY_BURST, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_COLD) && GetHasFeat(FEAT_WINTERS_BLAST, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_ELECTRICITY) && GetHasFeat(FEAT_STORM_BOLT, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_SONIC) && GetHasFeat(FEAT_CLAP_OF_THUNDER, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_HEALING) && GetHasFeat(FEAT_TOUCH_OF_HEALING, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_TELEPORTATION) && GetHasFeat(FEAT_DIMENSIONAL_JAUNT, oCaster)) nCLBonus += 1; + if (nSpellSchool == SPELL_SCHOOL_ABJURATION && GetHasFeat(FEAT_MYSTIC_BACKLASH, oCaster)) nCLBonus += 1; + if (nSpellSchool == SPELL_SCHOOL_NECROMANCY && GetHasFeat(FEAT_SICKENING_GRASP, oCaster)) nCLBonus += 1; + if (GetIsFromDomain(iSpellId, "wardom") && GetHasFeat(FEAT_HOLY_WARRIOR, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_EARTH) && GetHasFeat(FEAT_CLUTCH_OF_EARTH, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_AIR) && GetHasFeat(FEAT_BORNE_ALOFT, oCaster)) nCLBonus += 1; + if ((nSpellSchool == SPELL_SCHOOL_ABJURATION) && GetHasFeat(FEAT_PROTECTIVE_WARD, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_DARKNESS) && GetHasFeat(FEAT_SHADOW_VEIL, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_LIGHT) && GetHasFeat(FEAT_SUNLIGHT_EYES, oCaster)) nCLBonus += 1; + if ((nSpellSchool == SPELL_SCHOOL_ENCHANTMENT) && GetHasFeat(FEAT_TOUCH_OF_DISTRACTION, oCaster)) nCLBonus += 1; + if (GetIsFromDomain(iSpellId, "dethdom") && GetHasFeat(FEAT_CHARNEL_MIASMA, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_WATER) && GetHasFeat(FEAT_DROWNING_GLANCE, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_FORCE) && GetHasFeat(FEAT_INVISIBLE_NEEDLE, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_SUMMONING) && GetHasFeat(FEAT_SUMMON_ELEMENTAL, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_SUMMONING) && GetHasFeat(FEAT_DIMENSIONAL_REACH, oCaster)) nCLBonus += 1; + if (GetHasDescriptor(iSpellId, DESCRIPTOR_AIR) && GetHasFeat(FEAT_HURRICANE_BREATH, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_POLYMORPH) && GetHasFeat(FEAT_MINOR_SHAPESHIFT, oCaster)) nCLBonus += 1; + if (GetIsOfSubschool(iSpellId, SUBSCHOOL_GLAMER) && GetHasFeat(FEAT_FACECHANGER, oCaster)) nCLBonus += 1; + return nCLBonus; + } + else return 0; +} + +int GetLevelByTypeArcane(object oCaster = OBJECT_SELF) +{ + int iFirstArcane = GetPrimaryArcaneClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster, iClass1); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster, iClass2); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster, iClass3); + if (iClass4 == iFirstArcane) iClass4Lev += GetArcanePRCLevels(oCaster, iClass4); + if (iClass5 == iFirstArcane) iClass5Lev += GetArcanePRCLevels(oCaster, iClass5); + if (iClass6 == iFirstArcane) iClass6Lev += GetArcanePRCLevels(oCaster, iClass6); + if (iClass7 == iFirstArcane) iClass7Lev += GetArcanePRCLevels(oCaster, iClass7); + if (iClass8 == iFirstArcane) iClass8Lev += GetArcanePRCLevels(oCaster, iClass8); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass7Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass8Lev); + + if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +int GetLevelByTypeDivine(object oCaster = OBJECT_SELF) +{ + int iFirstDivine = GetPrimaryDivineClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iClass1 == CLASS_TYPE_PALADIN || iClass1 == CLASS_TYPE_RANGER) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_PALADIN || iClass2 == CLASS_TYPE_RANGER) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_PALADIN || iClass3 == CLASS_TYPE_RANGER) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_PALADIN || iClass4 == CLASS_TYPE_RANGER) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_PALADIN || iClass5 == CLASS_TYPE_RANGER) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_PALADIN || iClass6 == CLASS_TYPE_RANGER) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_PALADIN || iClass7 == CLASS_TYPE_RANGER) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_PALADIN || iClass8 == CLASS_TYPE_RANGER) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster, iClass1); + if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster, iClass2); + if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster, iClass3); + if (iClass4 == iFirstDivine) iClass4Lev += GetDivinePRCLevels(oCaster, iClass4); + if (iClass5 == iFirstDivine) iClass5Lev += GetDivinePRCLevels(oCaster, iClass5); + if (iClass6 == iFirstDivine) iClass6Lev += GetDivinePRCLevels(oCaster, iClass6); + if (iClass7 == iFirstDivine) iClass7Lev += GetDivinePRCLevels(oCaster, iClass7); + if (iClass8 == iFirstDivine) iClass8Lev += GetDivinePRCLevels(oCaster, iClass8); + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass7Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass8Lev); + + if (!GetIsDivineClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsDivineClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsDivineClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsDivineClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsDivineClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsDivineClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsDivineClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsDivineClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +//:: Test Void +//:: void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_chat.nss b/src/include/prc_inc_chat.nss new file mode 100644 index 0000000..02bde6e --- /dev/null +++ b/src/include/prc_inc_chat.nss @@ -0,0 +1,117 @@ +//:://///////////////////////////////////////////// +//:: Chat Command include +//:: prc_inc_chat +//:://///////////////////////////////////////////// + +const string PRC_CHAT_HOOK_ACTIVE = "prc_chat_hook"; +const string PRC_CHAT_HOOK_SCRIPT = "prc_chat_script"; +const string PRC_PLAYER_RESPONSE = "prc_player_response"; + +void AddChatEventHook(object oPC, string sScriptToCall, float fDur = 0.0f); + +struct _prc_inc_WordInfo{ + int nWordStart; + int nWordLength; +}; + +//we assume that sDivider is always 1 character +struct _prc_inc_WordInfo GetStringWordInfo(string sString, int nWordToGet, string sDivider = " ") +{ + struct _prc_inc_WordInfo info; + + // Safety checks + if(sString == "") + return info; + + if(sDivider == "") + sDivider = " "; + + int nStrLength = GetStringLength(sString); + + nWordToGet--; + + // Start with the first word. + int nCurrentWord = 0; + int nCurrentStart = 0; + int nCurrentEnd = FindSubString(sString, sDivider); + // Advance to the specified element. + while (nCurrentWord < nWordToGet && nCurrentEnd > -1) + { + nCurrentWord++; + nCurrentStart = nCurrentEnd + 1; + nCurrentEnd = FindSubString(sString, sDivider, nCurrentStart); + } + // Adjust the end point if this is the last element. + if (nCurrentEnd == -1) nCurrentEnd = nStrLength; + + if (nCurrentWord >= nWordToGet) + { + info.nWordStart = nCurrentStart; + info.nWordLength = nCurrentEnd-nCurrentStart; + } + + return info; +} + +string GetStringWord(string sString, int nWordToGet, string sDivider = " ") +{ + struct _prc_inc_WordInfo info = GetStringWordInfo(sString, nWordToGet, sDivider); + return GetSubString(sString, info.nWordStart, info.nWordLength); +} + +string GetStringWordToEnd(string sString, int nWordToGet, string sDivider = " ") +{ + struct _prc_inc_WordInfo info = GetStringWordInfo(sString, nWordToGet, sDivider); + return GetSubString(sString, info.nWordStart, GetStringLength(sString)-info.nWordStart); +} + +//Returns TRUE if sPrefix matches sWord or some part of the beginning of sWord +/*int GetStringMatchesAbbreviation(string sString, string sAbbreviationPattern) +{ + int nShortestAbbreviation = FindSubString(sAbbreviationPattern, "-"); + if(nShortestAbbreviation > 0) + sAbbreviationPattern = GetStringLeft(sAbbreviationPattern, nShortestAbbreviation) + GetStringRight(sAbbreviationPattern, GetStringLength(sAbbreviationPattern)-(nShortestAbbreviation+1)); + else if (nShortestAbbreviation == 0) + { + sAbbreviationPattern = GetStringRight(sAbbreviationPattern, GetStringLength(sAbbreviationPattern)-1); + nShortestAbbreviation = GetStringLength(sAbbreviationPattern); + } + else + nShortestAbbreviation = GetStringLength(sAbbreviationPattern); + + if(GetStringLength(sString) >= nShortestAbbreviation) + return GetStringLeft(sAbbreviationPattern, GetStringLength(sString)) == sString; + else + return FALSE; +}*/ + +int GetStringMatchesAbbreviation(string sString, string sAbbreviationPattern) +{ + string sTest; + int iAbbrevEnd = FindSubString(sAbbreviationPattern, "-"); + + if(iAbbrevEnd == -1) + sTest = sAbbreviationPattern; + else + sTest = GetSubString(sAbbreviationPattern, 0, iAbbrevEnd); + + return FindSubString(sString, sTest) == 0; +} + +void HelpText(object oPC, string sString) +{ + SendMessageToPC(oPC, PRC_TEXT_WHITE + sString + ""); +} + +void _clear_chat_vars(object oPC) +{ + DeleteLocalInt(oPC, PRC_CHAT_HOOK_ACTIVE); + DeleteLocalString(oPC, PRC_CHAT_HOOK_SCRIPT); +} + +void AddChatEventHook(object oPC, string sScriptToCall, float fDur = 0.0f) +{ + SetLocalInt(oPC, PRC_CHAT_HOOK_ACTIVE, TRUE); + SetLocalString(oPC, PRC_CHAT_HOOK_SCRIPT, sScriptToCall); + if(fDur > 0.0f) DelayCommand(fDur, _clear_chat_vars(oPC)); +} \ No newline at end of file diff --git a/src/include/prc_inc_chat_dm.nss b/src/include/prc_inc_chat_dm.nss new file mode 100644 index 0000000..45596a1 --- /dev/null +++ b/src/include/prc_inc_chat_dm.nss @@ -0,0 +1,828 @@ +//:://///////////////////////////////////////////// +//:: Debug Command include +//:: prc_inc_chat_dm.nss +//:://///////////////////////////////////////////// + +#include "prc_alterations" +#include "prc_inc_chat" +#include "prc_inc_shifting" //For _prc_inc_EffectString, etc. +#include "prc_inc_util" +#include "psi_inc_ppoints" +/* +execute +setlocalvar +dellocalvar + +set + xp + level + gold + abil + skill + +mod + xp + gold + abilit + skill + +*/ +const string CMD_CHK_PP = "checkp-owerpoints"; +const string CMD_CHK_SS = "checks-pellslots"; + +const string CMD_EXECUTE = "dm_exec-ute"; +const string CMD_VARIABLE = "dm_var-iable"; + const string CMD_PC = "pc"; + const string CMD_MODULE = "mod-ule"; + const string CMD_LOCAL = "loc-al"; + const string CMD_PERSISTANT = "per-sistant"; + +const string CMD_INFORMATION = "dm_info-rmation"; + const string CMD_ABILITIES = "abil-ities"; + const string CMD_EFFECTS = "eff-ects"; + const string CMD_PROPERTIES = "prop-erties"; + const string CMD_SKILLS = "skil-ls"; + +const string CMD_CHANGE = "dm_change"; + const string CMD_ABILITY = "abil-ity"; + const string CMD_STR = "str-ength"; + const string CMD_DEX = "dex-terity"; + const string CMD_CON = "con-stitution"; + const string CMD_INT = "int-elligence"; + const string CMD_WIS = "wis-dom"; + const string CMD_CHA = "cha-risma"; + const string CMD_LEVEL = "level"; + const string CMD_XP = "xp"; + const string CMD_GOLD = "gold"; + const string CMD_BY = "by"; + const string CMD_TO = "to"; + +const string CMD_SPAWN = "dm_spawn"; + const string CMD_CREATURE = "creature"; + const string CMD_ITEM = "item"; + +const string CMD_RELEVEL = "dm_relevel"; + +const string CMD_SPECIAL = "dm_special"; + const string CMD_REST = "rest"; + const string CMD_HANDLE_FORCE_REST_1 = "handle"; + const string CMD_HANDLE_FORCE_REST_2 = "force-d"; + const string CMD_HANDLE_FORCE_REST_3 = "rest-ing"; + +int Debug_ProcessChatCommand_Help(object oPC, string sCommand) +{ + string sCommandName = GetStringWord(sCommand, 2); + int nLevel = sCommandName != ""; + int bResult = FALSE; + + if(!nLevel) + { + HelpText(oPC, "=== DM COMMANDS"); + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_EXECUTE) || !nLevel) + { + if(nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_EXECUTE); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_EXECUTE + " "); + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_VARIABLE) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_VARIABLE); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_VARIABLE + " get "); + if (nLevel) + HelpText(oPC, " Print the value of the specified local variable"); + HelpText(oPC, "~~" + CMD_VARIABLE + " set "); + if (nLevel) + { + HelpText(oPC, " Set the value of the specified local variable and print the old value"); + HelpText(oPC, " can be one of: " + CMD_PC + ", " + CMD_MODULE + " (default:" + CMD_PC + " )"); + HelpText(oPC, " can be one of: " + CMD_LOCAL + ", " + CMD_PERSISTANT + " (default: " + CMD_LOCAL + ")"); + HelpText(oPC, " can be one of: int, string"); + } + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_INFORMATION) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_INFORMATION); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_INFORMATION + " " + CMD_ABILITIES); + if (nLevel) + HelpText(oPC, " Print the STR, DEX, CON, INT, WIS, and CHA of the PC"); + HelpText(oPC, "~~" + CMD_INFORMATION + " " + CMD_EFFECTS); + if (nLevel) + HelpText(oPC, " Print all effects that have been applied to the PC"); + HelpText(oPC, "~~" + CMD_INFORMATION + " " + CMD_PROPERTIES); + if (nLevel) + HelpText(oPC, " Print the item properties of all items the PC has equipped"); + HelpText(oPC, "~~" + CMD_INFORMATION + " " + CMD_SKILLS); + if (nLevel) + HelpText(oPC, " Print the number of ranks that the PC has in each skill"); + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_CHANGE) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_CHANGE + " "); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_LEVEL + " " + CMD_TO + " "); + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_LEVEL + " " + CMD_BY + " "); + if (nLevel) + { + HelpText(oPC, " Adjust the PC's level as specified (must be 1-40)"); + } + HelpText(oPC, ""); + + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_XP + " " + CMD_TO + " "); + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_XP + " " + CMD_BY + " "); + if (nLevel) + { + HelpText(oPC, " Adjust the PC's XP as specified"); + } + HelpText(oPC, ""); + + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_GOLD + " " + CMD_TO + " "); + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_GOLD + " " + CMD_BY + " "); + if (nLevel) + { + HelpText(oPC, " Adjust the PC's gold as specified"); + } + HelpText(oPC, ""); + + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_ABILITY + " " + CMD_TO + " (requires NWNX funcs)"); + HelpText(oPC, "~~" + CMD_CHANGE + " " + CMD_ABILITY + " " + CMD_BY + " (requires NWNX funcs)"); + if (nLevel) + { + HelpText(oPC, " Adjust the PC's abilities as specified"); + HelpText(oPC, " can be: " + CMD_STR + ", " + CMD_DEX + ", " + CMD_CON + ", " + CMD_INT + ", " + CMD_WIS + ", or " + CMD_CHA); + } + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_SPAWN) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_SPAWN); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_SPAWN + " " + CMD_CREATURE + " "); + if (nLevel) + { + HelpText(oPC, " Spawn the specified creature in the same location as the PC."); + HelpText(oPC, " It will be treated as a summoned creature--i.e., under the PC's control."); + } + + HelpText(oPC, "~~" + CMD_SPAWN + " " + CMD_ITEM + " "); + if (nLevel) + { + HelpText(oPC, " Spawn the specified item in the same location as the PC"); + HelpText(oPC, " Use: ~~" + CMD_SPAWN + " " + CMD_ITEM + "prc_target to spawn chat command target widget."); + } + + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_RELEVEL) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_RELEVEL + " "); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_RELEVEL + " "); + if (nLevel) + { + HelpText(oPC, " Relevel the PC starting from the specified level."); + HelpText(oPC, " The final result is a PC with exactly the same XP as before,"); + HelpText(oPC, " but with the feats, skills, etc. reselected starting with the specified level."); + } + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_SPECIAL) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== DM COMMAND: " + CMD_SPECIAL); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_SPECIAL + " " + CMD_REST); + if (nLevel) + { + HelpText(oPC, " Instantly rest the PC"); + } + + HelpText(oPC, "~~" + CMD_SPECIAL + " " + CMD_HANDLE_FORCE_REST_1 + " " + CMD_HANDLE_FORCE_REST_2 + " " + CMD_HANDLE_FORCE_REST_3); + if (nLevel) + { + HelpText(oPC, " Tell PRC that the module force rests PCs."); + HelpText(oPC, " Forced resting restores feat uses and spell uses for Bioware spellbooks,"); + HelpText(oPC, " but does not restore spell uses for PRC conversation-based spellbooks,"); + HelpText(oPC, " and may cause problems with some other PRC features."); + HelpText(oPC, " Start a detector that detects forced resting and fixes"); + HelpText(oPC, " these issues automatically."); + } + HelpText(oPC, ""); + } + + return bResult; +} + +void _prc_inc_DoLocalVar(object oTarget, object oPC, string sCommand, int nNextArg) +{ + string sDataType = GetStringWord(sCommand, nNextArg++); + + string sCommandName = GetStringWord(sCommand, nNextArg++); + if (sCommandName != "get" && sCommandName != "set") + { + DoDebug("Unrecognized command (expected 'get' or 'set'): " + sCommandName); + return; + } + + string sVarName = GetStringWord(sCommand, nNextArg++); + if (sVarName == "") + { + DoDebug("Invalid variable name: '" + sCommandName + "'"); + return; + } + + string sVarValue = GetStringWordToEnd(sCommand, nNextArg); + + if (sDataType == "string") + { + string sValue = GetLocalString(oTarget, sVarName); + DoDebug("Value: '" + sValue + "'"); + if (sCommandName == "set") + { + SetLocalString(oTarget, sVarName, sVarValue); + DoDebug("New Value: '" + sVarValue + "'"); + } + } + else if (sDataType == "int") + { + int nValue = GetLocalInt(oTarget, sVarName); + DoDebug("Value: " + IntToString(nValue)); + if (sCommandName == "set") + { + if(DEBUG || GetIsDM(oPC)) + { + int nVarValue = StringToInt(sVarValue); + if (sVarValue == IntToString(nVarValue)) + { + SetLocalInt(oTarget, sVarName, nVarValue); + DoDebug("New Value: " + sVarValue); + } + else + DoDebug("Can't set integer variable to non-integer value: " +sVarValue); + } + else + DoDebug("This command only works if DEBUG = TRUE"); + } + } + else + { + DoDebug("Unrecognized data type: " + sDataType); + } +} + +void _prc_inc_DoPersistantVar(object oTarget, object oPC, string sCommand, int nNextArg) +{ + string sDataType = GetStringWord(sCommand, nNextArg++); + + string sCommandName = GetStringWord(sCommand, nNextArg++); + if (sCommandName != "get" && sCommandName != "set") + { + DoDebug("Unrecognized command (expected 'get' or 'set'): " + sCommandName); + return; + } + + string sVarName = GetStringWord(sCommand, nNextArg++); + if (sVarName == "") + { + DoDebug("Invalid variable name: '" + sCommandName + "'"); + return; + } + + string sVarValue = GetStringWordToEnd(sCommand, nNextArg++); + + if (sDataType == "string") + { + string sValue = GetPersistantLocalString(oTarget, sVarName); + DoDebug("Value: '" + sValue + "'"); + if (sCommandName == "set") + SetPersistantLocalString(oTarget, sVarName, sVarValue); + } + else if (sDataType == "int") + { + int nValue = GetPersistantLocalInt(oTarget, sVarName); + DoDebug("Value: " + IntToString(nValue)); + if (sCommandName == "set") + { + if(DEBUG || GetIsDM(oPC)) + { + int nVarValue = StringToInt(sVarValue); + if (sVarValue == IntToString(nVarValue)) + SetPersistantLocalInt(oTarget, sVarName, nVarValue); + else + DoDebug("Can't set integer variable to non-integer value: " +sVarValue); + } + else + DoDebug("This command only works if DEBUG = TRUE"); + } + } + else + { + DoDebug("Unrecognized data type: " + sDataType); + } +} + +void _prc_inc_DumpItemProperty(string sPrefix, itemproperty iProp, object oPC) +{ + int nDurationType = GetItemPropertyDurationType(iProp); + string sPropString = _prc_inc_ItemPropertyString(iProp); + if(sPropString != "") + { + if (nDurationType == DURATION_TYPE_TEMPORARY) + sPropString = GetStringByStrRef(57473+0x01000000) + sPropString; //"TEMPORARY: " + DoDebug(sPrefix + sPropString); + } +} + +void _prc_inc_DumpAllItemProperties(string sPrefix, object oItem, object oPC) +{ + if(GetIsObjectValid(oItem)) + { + itemproperty iProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProp)) + { + _prc_inc_DumpItemProperty(sPrefix, iProp, oPC); + iProp = GetNextItemProperty(oItem); + } + } +} + +int _prc_inc_XPToLevel(int nXP) +{ + float fXP = IntToFloat(nXP); + float fLevel = (sqrt(8 * fXP / 1000 + 1) + 1) / 2; + return FloatToInt(fLevel); +} + +int _prc_inc_LevelToXP(int nLevel) +{ + return (nLevel * (nLevel - 1)) * 500; +} + +void DoPrintSummon(object oPC, string sResRef) +{ + object oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + if (GetIsObjectValid(oCreature)) + HelpText(oPC, "Created creature: " + GetName(oCreature)); + else + HelpText(oPC, "Failed to create creature--invalid resref?: " + sResRef); +} + +void DoSummon(object oPC, string sResRef) +{ + effect eSummon = EffectSummonCreature(sResRef); + ApplyEffectAtLocation(DURATION_TYPE_PERMANENT, eSummon, GetLocation(oPC)); + AssignCommand(oPC, DoPrintSummon(oPC, sResRef)); +} + +int Debug_ProcessChatCommand(object oPC, string sCommand) +{ + string sCommandName = GetStringWord(sCommand, 1); + int bResult = FALSE; + object oTarget = GetLocalObject(oPC, "prc_chatcmd_target"); + if(!GetIsObjectValid(oTarget)) + oTarget = oPC; + + + //Handle the commands we recognize no matter what, but only execute them if DEBUG is TRUE + if(GetStringMatchesAbbreviation(sCommandName, CMD_EXECUTE)) + { + bResult = TRUE; + if (DEBUG || GetIsDM(oPC)) + { + string sScript = GetStringWord(sCommand, 2); + HelpText(oPC, "Executing script: " + sScript); + ExecuteScript(sScript, oTarget); + } + else + HelpText(oPC, "This command only works if DEBUG = TRUE"); + } + else if (GetStringMatchesAbbreviation(sCommandName, CMD_VARIABLE)) + { + bResult = TRUE; + int nNextArg = 2; + string sVarType = GetStringWord(sCommand, nNextArg++); + if(GetStringMatchesAbbreviation(sVarType, CMD_MODULE)) + { + sVarType = GetStringWord(sCommand, nNextArg++); + oTarget = GetModule(); + } + else if(GetStringMatchesAbbreviation(sVarType, CMD_PC)) + { + sVarType = GetStringWord(sCommand, nNextArg++); + oTarget = oPC; + } + + if(GetStringMatchesAbbreviation(sVarType, CMD_LOCAL)) + _prc_inc_DoLocalVar(oTarget, oPC, sCommand, nNextArg); + else if(GetStringMatchesAbbreviation(sVarType, CMD_PERSISTANT)) + _prc_inc_DoPersistantVar(oTarget, oPC, sCommand, nNextArg); + else + _prc_inc_DoLocalVar(oTarget, oPC, sCommand, nNextArg); + } + else if(GetStringMatchesAbbreviation(sCommandName, CMD_INFORMATION)) + { + bResult = TRUE; + string sInfoType = GetStringWord(sCommand, 2); + if (GetStringMatchesAbbreviation(sInfoType, CMD_ABILITIES)) + { + HelpText(oPC, "====== ABILITIES ======"); + HelpText(oPC, "=== The first number is the base score; the second is the modified score, which includes bonuses and penalties from gear, etc."); + HelpText(oPC, "=== STR: " + IntToString(GetAbilityScore(oTarget, ABILITY_STRENGTH, TRUE)) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_STRENGTH, FALSE))); + HelpText(oPC, "=== DEX: " + IntToString(GetAbilityScore(oTarget, ABILITY_DEXTERITY, TRUE)) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_DEXTERITY, FALSE))); + HelpText(oPC, "=== CON: " + IntToString(GetAbilityScore(oTarget, ABILITY_CONSTITUTION, TRUE)) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_CONSTITUTION, FALSE))); + HelpText(oPC, "=== INT: " + IntToString(GetAbilityScore(oTarget, ABILITY_INTELLIGENCE, TRUE)) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_INTELLIGENCE, FALSE))); + HelpText(oPC, "=== WIS: " + IntToString(GetAbilityScore(oTarget, ABILITY_WISDOM, TRUE)) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_WISDOM, FALSE))); + HelpText(oPC, "=== CHA: " + IntToString(GetAbilityScore(oTarget, ABILITY_CHARISMA, TRUE)) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_CHARISMA, FALSE))); + if (GetPersistantLocalInt(oTarget, SHIFTER_ISSHIFTED_MARKER) && GetPRCSwitch(PRC_NWNX_FUNCS)) + { + int iSTR = GetPersistantLocalInt(oTarget, "Shifting_NWNXSTRAdjust"); + int iDEX = GetPersistantLocalInt(oTarget, "Shifting_NWNXDEXAdjust"); + int iCON = GetPersistantLocalInt(oTarget, "Shifting_NWNXCONAdjust"); + HelpText(oPC, "=== The first number is the base score when unshifted; the second is the modified score when unshifted."); + HelpText(oPC, "=== STR: " + IntToString(GetAbilityScore(oTarget, ABILITY_STRENGTH, TRUE)-iSTR) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_STRENGTH, FALSE)-iSTR)); + HelpText(oPC, "=== DEX: " + IntToString(GetAbilityScore(oTarget, ABILITY_DEXTERITY, TRUE)-iDEX) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_DEXTERITY, FALSE)-iDEX)); + HelpText(oPC, "=== CON: " + IntToString(GetAbilityScore(oTarget, ABILITY_CONSTITUTION, TRUE)-iCON) + " / " + IntToString(GetAbilityScore(oTarget, ABILITY_CONSTITUTION, FALSE)-iCON)); + } + } + else if (GetStringMatchesAbbreviation(sInfoType, CMD_EFFECTS)) + { + HelpText(oPC, "====== EFFECTS ======"); + effect eEffect = GetFirstEffect(oTarget); + while(GetIsEffectValid(eEffect)) + { + if (GetEffectType(eEffect) == EFFECT_TYPE_INVALIDEFFECT) + { + //An effect with type EFFECT_TYPE_INVALID is added for each item property + //They are also added for a couple of other things (Knockdown, summons, etc.) + //Just skip these + } + else + { + string sEffectString = _prc_inc_EffectString(eEffect); + if(sEffectString != "") + HelpText(oPC, "=== " + sEffectString); + } + eEffect = GetNextEffect(oTarget); + } + } + else if (GetStringMatchesAbbreviation(sInfoType, CMD_PROPERTIES)) + { + HelpText(oPC, "====== PROPERTIES ======"); + HelpText(oPC, "====== CREATURE"); + _prc_inc_DumpAllItemProperties("=== ", oTarget, oPC); + if(GetObjectType(oTarget) == OBJECT_TYPE_CREATURE) + { + HelpText(oPC, "====== CREATURE HIDE"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTarget), oPC); + HelpText(oPC, "====== RIGHT CREATURE WEAPON"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget), oPC); + HelpText(oPC, "====== LEFT CREATURE WEAPON"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget), oPC); + HelpText(oPC, "====== SPECIAL CREATURE WEAPON"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget), oPC); + HelpText(oPC, "====== RIGHT HAND"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget), oPC); + HelpText(oPC, "====== LEFT HAND"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget), oPC); + HelpText(oPC, "====== CHEST"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget), oPC); + HelpText(oPC, "====== HEAD"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_HEAD, oTarget), oPC); + HelpText(oPC, "====== CLOAK"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_CLOAK, oTarget), oPC); + HelpText(oPC, "====== ARMS"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_ARMS, oTarget), oPC); + HelpText(oPC, "====== BELT"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_BELT, oTarget), oPC); + HelpText(oPC, "====== BOOTS"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_BOOTS, oTarget), oPC); + HelpText(oPC, "====== RIGHT HAND RING"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oTarget), oPC); + HelpText(oPC, "====== LEFT HAND RING"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_LEFTRING, oTarget), oPC); + HelpText(oPC, "====== NECK"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_NECK, oTarget), oPC); + HelpText(oPC, "====== ARROWS"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_ARROWS, oTarget), oPC); + HelpText(oPC, "====== BOLTS"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_BOLTS, oTarget), oPC); + HelpText(oPC, "====== BULLETS"); + _prc_inc_DumpAllItemProperties("=== ", GetItemInSlot(INVENTORY_SLOT_BULLETS, oTarget), oPC); + } + } + else if (GetStringMatchesAbbreviation(sInfoType, CMD_SKILLS)) + { + HelpText(oPC, "====== SKILLS ======"); + HelpText(oPC, "=== The first number is the base score; the second is the modified score, which includes bonuses and penalties from gear, etc."); + int i = 0; + string sSkillName; + while((sSkillName = Get2DACache("skills", "Name", i)) != "") + { + sSkillName = GetStringByStrRef(StringToInt(sSkillName)); + HelpText(oPC, "=== " + sSkillName + ": " + IntToString(GetSkillRank(i, oTarget, TRUE)) + " / " + IntToString(GetSkillRank(i, oTarget, FALSE))); + i += 1; + } + } + else + HelpText(oPC, "Unrecognized information request: " + sInfoType); + } + else if (GetStringMatchesAbbreviation(sCommandName, CMD_CHANGE)) + { + bResult = TRUE; + if(!DEBUG && !GetIsDM(oPC)) + HelpText(oPC, "This command only works if DEBUG = TRUE"); + else + { + string sChangeWhat = GetStringWord(sCommand, 2); + string sChangeHow = GetStringWord(sCommand, 3); + string sNumber = GetStringWord(sCommand, 4); + int nNumber = StringToInt(sNumber); + if (GetStringMatchesAbbreviation(sChangeWhat, CMD_LEVEL)) + { + if (sNumber != IntToString(nNumber)) + HelpText(oPC, "Unrecognized level: " + sNumber); + else + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + { + int nCurrentLevel = _prc_inc_XPToLevel(GetXP(oTarget)); + if (nCurrentLevel > 40) + nCurrentLevel = 40; + nNumber = nCurrentLevel + nNumber; + if (nNumber < 1) + nNumber = 1; + else if (nNumber > 40) + nNumber = 40; + SetXP(oTarget, _prc_inc_LevelToXP(nNumber)); + } + else if (GetStringMatchesAbbreviation(sChangeHow, CMD_TO)) + { + if (nNumber < 1) + nNumber = 1; + else if (nNumber > 40) + nNumber = 40; + SetXP(oTarget, _prc_inc_LevelToXP(nNumber)); + } + else + HelpText(oPC, "Unrecognized word: " + sChangeHow); + } + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_XP)) + { + if (sNumber != IntToString(nNumber)) + HelpText(oPC, "Unrecognized xp: " + sNumber); + else + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + { + nNumber = GetXP(oTarget) + nNumber; + if (nNumber < 0) + nNumber = 0; + SetXP(oTarget, nNumber); + } + else if (GetStringMatchesAbbreviation(sChangeHow, CMD_TO)) + { + if (nNumber < 0) + nNumber = 0; + SetXP(oTarget, nNumber); + } + else + HelpText(oPC, "Unrecognized word: " + sChangeHow); + } + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_GOLD)) + { + if (sNumber != IntToString(nNumber)) + HelpText(oPC, "Unrecognized gold amount: " + sNumber); + else + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + { + if (nNumber > 0) + GiveGoldToCreature(oTarget, nNumber); + else if (nNumber < 0) + AssignCommand(oPC, TakeGoldFromCreature(-nNumber, oTarget, TRUE)); + } + else if (GetStringMatchesAbbreviation(sChangeHow, CMD_TO)) + { + nNumber = nNumber - GetGold(oTarget); + if (nNumber > 0) + GiveGoldToCreature(oTarget, nNumber); + else if (nNumber < 0) + AssignCommand(oPC, TakeGoldFromCreature(-nNumber, oTarget, TRUE)); + } + else + HelpText(oPC, "Unrecognized word: " + sChangeHow); + } + } +/* else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_ABILITY)) + { + if (!GetPRCSwitch(PRC_NWNX_FUNCS)) + HelpText(oPC, "This command only works if NWNX funcs is installed"); + else + { + sChangeWhat = GetStringWord(sCommand, 3); + sChangeHow = GetStringWord(sCommand, 4); + sNumber = GetStringWord(sCommand, 5); + nNumber = StringToInt(sNumber); + if (sNumber != IntToString(nNumber)) + HelpText(oPC, "Unrecognized ability value: " + sNumber); + else + { + if (GetStringMatchesAbbreviation(sChangeWhat, CMD_STR)) + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + nNumber += GetAbilityScore(oTarget, ABILITY_STRENGTH, TRUE); + if (nNumber < 3 || nNumber > 255) + HelpText(oPC, "Invalid " + CMD_STR + " value (must be between 3 and 255): " + sChangeWhat); + else + { + if (nNumber > 100 - 12) + HelpText(oPC, "NOTE: having a total " + CMD_STR + " above 100 can cause problems (the weight that you can carry goes to 0)"); + _prc_inc_shifting_SetSTR(oTarget, nNumber); + } + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_DEX)) + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + nNumber += GetAbilityScore(oTarget, ABILITY_DEXTERITY, TRUE); + if (nNumber < 3 || nNumber > 255) + HelpText(oPC, "Invalid " + CMD_DEX + " value (must be between 3 and 255): " + sChangeWhat); + else + _prc_inc_shifting_SetDEX(oTarget, nNumber); + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_CON)) + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + nNumber += GetAbilityScore(oTarget, ABILITY_CONSTITUTION, TRUE); + if (nNumber < 3 || nNumber > 255) + HelpText(oPC, "Invalid " + CMD_CON + " value (must be between 3 and 255): " + sChangeWhat); + else + _prc_inc_shifting_SetCON(oTarget, nNumber); + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_INT)) + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + nNumber += GetAbilityScore(oTarget, ABILITY_INTELLIGENCE, TRUE); + if (nNumber < 3 || nNumber > 255) + HelpText(oPC, "Invalid " + CMD_INT + " value (must be between 3 and 255): " + sChangeWhat); + else + _prc_inc_shifting_SetINT(oTarget, nNumber); + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_WIS)) + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + nNumber += GetAbilityScore(oTarget, ABILITY_WISDOM, TRUE); + if (nNumber < 3 || nNumber > 255) + HelpText(oPC, "Invalid " + CMD_WIS + " value (must be between 3 and 255): " + sChangeWhat); + else + _prc_inc_shifting_SetWIS(oTarget, nNumber); + } + else if (GetStringMatchesAbbreviation(sChangeWhat, CMD_CHA)) + { + if (GetStringMatchesAbbreviation(sChangeHow, CMD_BY)) + nNumber += GetAbilityScore(oTarget, ABILITY_CHARISMA, TRUE); + if (nNumber < 3 || nNumber > 255) + HelpText(oPC, "Invalid " + CMD_CHA + " value (must be between 3 and 255): " + sChangeWhat); + else + _prc_inc_shifting_SetCHA(oTarget, nNumber); + } + + else + HelpText(oPC, "Unrecognized ability to change: " + sChangeWhat); + } + } + } +*/ + else + { + HelpText(oPC, "Unrecognized value to change: " + sChangeWhat); + } + } + } + else if (GetStringMatchesAbbreviation(sCommandName, CMD_SPAWN)) + { + bResult = TRUE; + if (!DEBUG && !GetIsDM(oPC)) + HelpText(oPC, "This command only works if DEBUG = TRUE"); + else + { + string sSpawnType = GetStringWord(sCommand, 2); + string sResRef = GetStringWord(sCommand, 3); + if (GetStringMatchesAbbreviation(sSpawnType, CMD_CREATURE)) + { + AssignCommand(oPC, DoSummon(oPC, sResRef)); + } + else if (GetStringMatchesAbbreviation(sSpawnType, CMD_ITEM)) + { + object oItem = CreateItemOnObject(sResRef, oTarget); + SetIdentified(oItem, TRUE); + if(GetIsObjectValid(oItem)) + HelpText(oPC, "Created item: " + GetName(oItem)); + else + HelpText(oPC, "Faild to create item--invalid resref?: " + sResRef); + } + else + HelpText(oPC, "Unrecognized spawn type: " + sSpawnType); + } + } + else if (GetStringMatchesAbbreviation(sCommandName, CMD_RELEVEL)) + { + bResult = TRUE; + if (!DEBUG) + HelpText(oPC, "This command only works if DEBUG = TRUE"); + else + { + string sNumber = GetStringWord(sCommand, 2); + int nNumber = StringToInt(sNumber); + int nStartXP = GetXP(oTarget); + int nStartLevel = _prc_inc_XPToLevel(nStartXP); + if (sNumber != IntToString(nNumber)) + HelpText(oPC, "Unrecognized level: " + sNumber); + else if (nNumber > nStartLevel) + HelpText(oPC, "Nothing to do: specified level is higher than current level."); + else + { + if (nNumber < 1) + nNumber = 1; + SetXP(oTarget, _prc_inc_LevelToXP(nNumber-1)); //Level down to the the level before the 1st we want to change + SetXP(oTarget, nStartXP); //Level back up to our starting XP + } + } + } + else if (GetStringMatchesAbbreviation(sCommandName, CMD_SPECIAL)) + { + bResult = TRUE; + + string sSpecialCommandName = GetStringWord(sCommand, 2); + + if (GetStringMatchesAbbreviation(sSpecialCommandName, CMD_REST)) + { + if (!DEBUG) + HelpText(oPC, "This command only works if DEBUG = TRUE"); + else + PRCForceRest(oTarget); + } + else if (GetStringMatchesAbbreviation(sSpecialCommandName, CMD_HANDLE_FORCE_REST_1) && + GetStringMatchesAbbreviation(GetStringWord(sCommand, 3), CMD_HANDLE_FORCE_REST_2) && + GetStringMatchesAbbreviation(GetStringWord(sCommand, 4), CMD_HANDLE_FORCE_REST_3) + ) + { + StartForcedRestDetector(oTarget); + } + } + else if(GetStringMatchesAbbreviation(sCommandName, CMD_CHK_PP)) + { + bResult = TRUE; + TellCharacterPowerPointStatus(oPC); + } + + return bResult; +} diff --git a/src/include/prc_inc_chat_pow.nss b/src/include/prc_inc_chat_pow.nss new file mode 100644 index 0000000..aced79a --- /dev/null +++ b/src/include/prc_inc_chat_pow.nss @@ -0,0 +1,125 @@ +//:://///////////////////////////////////////////// +//:: Debug Command include +//:: prc_inc_chat_pow.nss +//:://///////////////////////////////////////////// + +/* +Command summary: + +~~pow [value] + Set Power Attack to the specified value +[value can be 0-5 for Power Attack, 0-24 for Improved Power Attack] + +~~pow [value] [q1|q2|q3] + Set Power Attack for the specified quickslot to the specified value +[value can be 0-5 for Power Attack, 0-24 for Improved Power Attack] +*/ + +#include "prc_inc_chat" + +const string CMD_POWER_ATTACK = "pow-erattack"; + +const string QS1_VAR_NAME = "PRC_PowerAttackQuickselect_2797"; +const string QS2_VAR_NAME = "PRC_PowerAttackQuickselect_2798"; +const string QS3_VAR_NAME = "PRC_PowerAttackQuickselect_2799"; + +int PowerAttack_ProcessChatCommand_Help(object oPC, string sCommand) +{ + string sCommandName = GetStringWord(sCommand, 2); + int nLevel = sCommandName != ""; + int bResult = FALSE; + + if (!nLevel) + { + HelpText(oPC, "=== PRC POWER ATTACK COMMANDS"); + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_POWER_ATTACK) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== POWER ATTACK COMMAND: " + CMD_POWER_ATTACK); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_POWER_ATTACK + " "); + if (nLevel) + HelpText(oPC, " Prints the current setting for the specified quickslot."); + HelpText(oPC, "~~" + CMD_POWER_ATTACK + " "); + if (nLevel) + HelpText(oPC, " Set the specified quickslot to the given value."); + HelpText(oPC, ""); + } + + //TODO: add a command to set the power attack value correctly without the quickslot + //To work correctly with the existing power attack code, the effects need to be added within a spell so that the + //spell id can be recorded and later used to remove them. + + return bResult; +} + +int PowerAttack_ProcessChatCommand(object oPC, string sCommand) +{ + int bResult = FALSE; + + string sCommandName = GetStringWord(sCommand, 1); + string sNewValue = GetStringWord(sCommand, 2); + int nNewValue = StringToInt(sNewValue); + + if(GetStringMatchesAbbreviation(sCommandName, CMD_POWER_ATTACK)) + { + bResult = TRUE; + + string sQuickslot = GetStringWord(sCommand, 2); + string sNewValue = GetStringWord(sCommand, 3); + if (sNewValue == "") + { + if (sQuickslot == "q1") + HelpText(oPC, "Power Attack Quickslot 1: " + IntToString(GetPersistantLocalInt(oPC, QS1_VAR_NAME))); + else if (sQuickslot == "q2") + HelpText(oPC, "Power Attack Quickslot 2: " + IntToString(GetPersistantLocalInt(oPC, QS2_VAR_NAME))); + else if (sQuickslot == "q3") + HelpText(oPC, "Power Attack Quickslot 3: " + IntToString(GetPersistantLocalInt(oPC, QS3_VAR_NAME))); + else + HelpText(oPC, "Invalid Power Attack Quickslot Number: " + sQuickslot); + } + else + { + int nNewValue = StringToInt(sNewValue); + if (sNewValue != IntToString(nNewValue)) + HelpText(oPC, "Power Attack value is not a number: " + sNewValue); + else if (nNewValue < 0) + HelpText(oPC, "Value is too small for Power Attack: " + sNewValue); + else if (!GetHasFeat(FEAT_POWER_ATTACK, oPC)) + HelpText(oPC, "Power Attack feat required for value: " + sNewValue); + else if (nNewValue > 5 && !GetHasFeat(FEAT_IMPROVED_POWER_ATTACK, oPC)) + HelpText(oPC, "Improved Power Attack feat required for value: " + sNewValue); + else if (nNewValue > 24) + HelpText(oPC, "Value is too large for Improved Power Attack: " + sNewValue); + else + { + if (sQuickslot == "q1") + { + SetPersistantLocalInt(oPC, QS1_VAR_NAME, nNewValue); + HelpText(oPC, "Power Attack Quickslot 1 set to: " + IntToString(nNewValue)); + } + else if (sQuickslot == "q2") + { + SetPersistantLocalInt(oPC, QS2_VAR_NAME, nNewValue); + HelpText(oPC, "Power Attack Quickslot 2 set to: " + IntToString(nNewValue)); + } + else if (sQuickslot == "q3") + { + SetPersistantLocalInt(oPC, QS3_VAR_NAME, nNewValue); + HelpText(oPC, "Power Attack Quickslot 3 set to: " + IntToString(nNewValue)); + } + else + HelpText(oPC, "Invalid Power Attack Quickslot Number: " + sQuickslot); + } + } + } + + return bResult; +} diff --git a/src/include/prc_inc_chat_shf.nss b/src/include/prc_inc_chat_shf.nss new file mode 100644 index 0000000..008ab1d --- /dev/null +++ b/src/include/prc_inc_chat_shf.nss @@ -0,0 +1,220 @@ +//:://///////////////////////////////////////////// +//:: PnP Shifter Chat Command include +//:: prc_inc_chat_shf +//:://///////////////////////////////////////////// + +#include "prc_inc_chat" +#include "prc_inc_shifting" + +const string CMD_GREATER_WILDSHAPE = "gw"; +const string CMD_SHIFT = "s-hift"; +const string CMD_EPIC_SHIFT = "e-pic"; +const string CMD_UNSHIFT = "u-nshift"; +const string CMD_LIST = "l-ist"; +const string CMD_INFO = "i-nfo"; +const string CMD_MARK = "mark"; +const string CMD_UNMARK = "unmark"; +const string CMD_DELETE = "delete"; + +int PnPShifter_ProcessChatCommand_Help(object oPC, string sCommand) +{ + string sCommandName = GetStringWord(sCommand, 2); + int nLevel = sCommandName != ""; + int bResult = FALSE; + + if (!nLevel) + { + HelpText(oPC, "=== PNP SHIFTER COMMANDS"); + HelpText(oPC, ""); + } + + if(GetStringMatchesAbbreviation(sCommandName, CMD_GREATER_WILDSHAPE) || !nLevel) + { + if (nLevel) + { + bResult = TRUE; + HelpText(oPC, "=== PNP SHIFTER COMMAND: " + CMD_GREATER_WILDSHAPE + " (Greater Wildshape)"); + HelpText(oPC, ""); + } + + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_LIST + " "); + if (nLevel) + HelpText(oPC, " Lists known shapes that match ; if is omitted, lists all known shapes."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_INFO + " "); + if (nLevel) + HelpText(oPC, " Lists shapes that match ; if an unambiguous match is found, prints information about it."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_SHIFT + " "); + if (nLevel) + HelpText(oPC, " Searches for shapes that match ; if an unambiguous match is found, shifts into it."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_EPIC_SHIFT + " "); + if (nLevel) + HelpText(oPC, " Searches for shapes that match ; if an unambiguous match is found, epic shifts into it."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_UNSHIFT); + if (nLevel) + HelpText(oPC, " Unshifts back into true form."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_MARK + " "); + if (nLevel) + HelpText(oPC, " Marks the specified shape for deletion."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_UNMARK + " "); + if (nLevel) + HelpText(oPC, " Removes the shape's deletion mark, if any."); + HelpText(oPC, "~~" + CMD_GREATER_WILDSHAPE + " " + CMD_DELETE + " yes"); + if (nLevel) + HelpText(oPC, " Deletes all shapes marked for deletion. Note that the word 'yes' is required as part of the command in order to confirm the deletion."); + if (nLevel) + { + HelpText(oPC, ""); + HelpText(oPC, "'" + CMD_GREATER_WILDSHAPE + "' stands for 'Greater Wildshape'"); + HelpText(oPC, " must match a known shape, is case-insenstive, and can be:"); + HelpText(oPC, " '.': matches the shape the PC is currently shifted into"); + HelpText(oPC, " A number: matches the shape with the given number. The numbers can be found found using command '~~gw list''."); + HelpText(oPC, " 'Q1' through 'Q10': matches the shape in the specified quickslot"); + HelpText(oPC, " A resref: if you don't know what this means, ignore this option. The resref can be found found using command '~~gw list'."); + HelpText(oPC, " Part of the name of a shape:"); + HelpText(oPC, " If there is exactly one exact match, that will be used."); + HelpText(oPC, " Otherwise, if there is exactly one shape whose name starts with , that will be used."); + HelpText(oPC, " Otherwise, if there is exactly one shape whose name contains , that will be used."); + HelpText(oPC, " Otherwise, no shape matches and nothing will happen."); + } + HelpText(oPC, ""); + } + + return bResult; +} + +void _prc_inc_ChatShift(object oPC, string sShapeName, int bEpic) +{ + //See if a valid shape was specified + if(sShapeName == "") + return; + + string sResRef = FindResRefFromString(oPC, SHIFTER_TYPE_SHIFTER, sShapeName, FALSE); + if(sResRef == "") + return; + + //Make sure we're not affected by a condition that prevents shifting + effect eTest = GetFirstEffect(oPC); + int nEType; + while(GetIsEffectValid(eTest)) + { + nEType = GetEffectType(eTest); + if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE + || nEType == EFFECT_TYPE_DAZED + || nEType == EFFECT_TYPE_PARALYZE + || nEType == EFFECT_TYPE_PETRIFY + || nEType == EFFECT_TYPE_SLEEP + || nEType == EFFECT_TYPE_STUNNED) + return; + eTest = GetNextEffect(oPC); + } + + //If we have at least one use of a suitable feat remaining, shift + int nPaidFeat = GWSPay(oPC, bEpic); + if(nPaidFeat) + { + if(!ShiftIntoResRef(oPC, SHIFTER_TYPE_SHIFTER, sResRef, bEpic)) + GWSRefund(oPC, nPaidFeat); + } + else + FloatingTextStrRefOnCreature(16828373, oPC, FALSE); // "You didn't have (Epic) Greater Wildshape uses available." +} + +void _prc_inc_ListShapes(object oShifter, int nShifterType, string sFindString) +{ + FindResRefFromString(oShifter, nShifterType, sFindString, TRUE); +} + +void _prc_inc_ChatMark(object oPC, string sShapeName, int bMark) +{ + if (sShapeName == "") + return; + + string sResRef = FindResRefFromString(oPC, SHIFTER_TYPE_SHIFTER, sShapeName, FALSE); + if (sResRef == "") + return; + + int nIndex = _prc_inc_shifting_GetIsTemplateStored(oPC, SHIFTER_TYPE_SHIFTER, sResRef); + if (!nIndex) + return; + + SetStoredTemplateDeleteMark(oPC, SHIFTER_TYPE_SHIFTER, nIndex-1, bMark); +} + +int PnPShifter_ProcessChatCommand(object oPC, string sCommand) +{ + if(!GetLevelByClass(CLASS_TYPE_PNP_SHIFTER, oPC)) + return FALSE; + + int bResult = FALSE; + if(GetStringWord(sCommand, 1) == CMD_GREATER_WILDSHAPE) + { + bResult = TRUE; + string sWord = GetStringWord(sCommand, 2); + + object oTemplate; + string sShape, sResRef; + if(GetStringMatchesAbbreviation(sWord, CMD_SHIFT)) + { + sShape = GetStringWordToEnd(sCommand, 3); + _prc_inc_ChatShift(oPC, sShape, FALSE); + } + else if(GetStringMatchesAbbreviation(sWord, CMD_EPIC_SHIFT)) + { + sShape = GetStringWordToEnd(sCommand, 3); + _prc_inc_ChatShift(oPC, sShape, TRUE); + } + else if(GetStringMatchesAbbreviation(sWord, CMD_UNSHIFT)) + { + UnShift(oPC); + } + else if(GetStringMatchesAbbreviation(sWord, CMD_LIST)) + { + sShape = GetStringWordToEnd(sCommand, 3); + DelayCommand(0.0f, _prc_inc_ListShapes(oPC, SHIFTER_TYPE_SHIFTER, sShape)); + } + else if(GetStringMatchesAbbreviation(sWord, CMD_INFO)) + { + sShape = GetStringWordToEnd(sCommand, 3); + if(sShape != "") + { + sResRef = FindResRefFromString(oPC, SHIFTER_TYPE_SHIFTER, sShape, FALSE); + if(sResRef != "") + { + oTemplate = _prc_inc_load_template_from_resref(sResRef, GetHitDice(oPC)); + if(GetIsObjectValid(oTemplate)) + { + DelayCommand(0.0, _prc_inc_PrintShape(oPC, oTemplate, FALSE)); + DelayCommand(10.0, MyDestroyObject(oTemplate)); + } + } + } + } + else if(GetStringMatchesAbbreviation(sWord, CMD_MARK)) + { + sShape = GetStringWordToEnd(sCommand, 3); + _prc_inc_ChatMark(oPC, sShape, TRUE); + HelpText(oPC, "Shape marked for deletion"); + } + else if(GetStringMatchesAbbreviation(sWord, CMD_UNMARK)) + { + sShape = GetStringWordToEnd(sCommand, 3); + _prc_inc_ChatMark(oPC, sShape, FALSE); + HelpText(oPC, "Shape no longer marked for deletion"); + } + else if(GetStringMatchesAbbreviation(sWord, CMD_DELETE)) + { + if (GetStringWordToEnd(sCommand, 3) == "yes") + { + DelayCommand(0.0f, DeleteMarkedStoredTemplates(oPC, SHIFTER_TYPE_SHIFTER)); + HelpText(oPC, "Marked shapes deleted"); + } + else + HelpText(oPC, "Marked shapes not deleted: please enter 'yes' after the word 'delete' to confirm"); + } + else + { + HelpText(oPC, "Unrecognize " + CMD_GREATER_WILDSHAPE + " command: " + sWord); + } + } + return bResult; +} diff --git a/src/include/prc_inc_clsfunc.nss b/src/include/prc_inc_clsfunc.nss new file mode 100644 index 0000000..3a6c251 --- /dev/null +++ b/src/include/prc_inc_clsfunc.nss @@ -0,0 +1,1738 @@ +/* + Class functions. + This scripts holds all functions used for classes in includes. + This prevents us from having one include for each class or set of classes. + + Stratovarius +*/ + + + +////////////////Begin Generic//////////////// + +// Function Definitions: + +// Include Files: +#include "prc_inc_spells" +//#include "prc_alterations" +//#include "prcsp_engine" +//#include "prc_inc_function" +//#include "prc_x2_itemprop" +//#include "prc_class_const" +//#include "prc_feat_const" +//#include "prc_ipfeat_const" +//#include "inc_utility" +// +//#include "pnp_shft_poly" +//#include "x2_inc_spellhook" +//#include "prc_inc_combat" +//#include "prc_inc_sp_tch" + +////////////////End Generic//////////////// + +//::void main (){} + + +////////////////Begin Drunken Master////////////////////// + + +// Function Definitions: + +// Searches oPC's inventory and finds the first valid alcoholic beverage container +// (empty) and returns TRUE if a proper container was found. This function takes +// action and returns a boolean. +int UseBottle(object oPC); + +// Searches oPC's inventory for an alcoholic beverage and if one is found it's +// destroyed and replaced by an empty container. This function is only used in +// the Breath of Fire spell script. +int UseAlcohol(object oPC = OBJECT_SELF); + +// Removes all Alcohol effects for oTarget. Used in B o Flame. +void RemoveAlcoholEffects(object oTarget = OBJECT_SELF); + +// Creates an empty bottle on oPC. +// sTag: the tag of the alcoholic beverage used (ale, spirits, wine) +void CreateBottleOnObject(object oPC, string sTag); + + +// Applies Drunk Like a Demno effects +void DrunkLikeDemon(); + +// Add the non-drunken master drinking effects. +void MakeDrunk(int nSpellID); + +// Have the drunken master say one of 6 phrases. +void DrunkenMasterSpeakString(); + +// Creates an empty bottle on oPC. +// nBeverage: the spell id of the alcoholic beverage used (ale, spirits, wine) +void DrunkenMasterCreateEmptyBottle(int nSpellID); + +// Determines the DC needed to save against the cast spell-like ability +// replace PRCGetSaveDC +int GetSpellDCSLA(object oCaster, int iSpelllvl,int iAbi = ABILITY_WISDOM); + +void DoArchmageHeirophantSLA(object oPC, object oTarget, location lTarget, int nSLAID); + +// Functions: +int UseBottle(object oPC) +{ + object oItem = GetFirstItemInInventory(oPC); + //search oPC for a bottle: + string sTag; + while(oItem != OBJECT_INVALID) + { + sTag = GetTag(oItem); + if(sTag == "NW_IT_THNMISC001" + || sTag == "NW_IT_THNMISC002" + || sTag == "NW_IT_THNMISC003" + || sTag == "NW_IT_THNMISC004") + { + SetPlotFlag(oItem, FALSE); + DestroyObject(oItem); + return TRUE; + } + else + oItem = GetNextItemInInventory(); + } + return FALSE; +} + +int UseAlcohol(object oPC = OBJECT_SELF) +{ + object oItem = GetFirstItemInInventory(oPC); + //search oPC for alcohol: + string sTag = GetTag(oItem); + while(oItem != OBJECT_INVALID) + { + if(sTag == "NW_IT_MPOTION021" + || sTag == "NW_IT_MPOTION022" + || sTag == "NW_IT_MPOTION023" + || sTag == "DragonsBreath") + { + SetPlotFlag(oItem, FALSE); + if(GetItemStackSize(oItem) > 1) + { + SetItemStackSize(oItem, GetItemStackSize(oItem) - 1); + // Create an Empty Bottle: + CreateBottleOnObject(oPC, sTag); + return TRUE; + } + else + { + DestroyObject(oItem); + // Create an Empty Bottle: + CreateBottleOnObject(oPC, sTag); + return TRUE; + } + } + else + oItem = GetNextItemInInventory(); + } + return FALSE; +} + +void CreateBottleOnObject(object oPC, string sTag) +{ + if(sTag == "NW_IT_MPOTION021") // Ale + { + CreateItemOnObject("nw_it_thnmisc002", oPC); + } + else if(sTag == "NW_IT_MPOTION022") // Spirits + { + CreateItemOnObject("nw_it_thnmisc003", oPC); + } + else if(sTag == "NW_IT_MPOTION023") // Wine + { + CreateItemOnObject("nw_it_thnmisc004", oPC); + } + else // Other beverage + { + CreateItemOnObject("nw_it_thnmisc001", oPC); + } +} + +int GetIsDrunk(object oTarget = OBJECT_SELF) +{ + return GetHasSpellEffect(406, oTarget) + || GetHasSpellEffect(407, oTarget) + || GetHasSpellEffect(408, oTarget); +} + +void RemoveAlcoholEffects(object oTarget = OBJECT_SELF) +{ + PRCRemoveSpellEffects(406, OBJECT_SELF, oTarget); + PRCRemoveSpellEffects(407, OBJECT_SELF, oTarget); + PRCRemoveSpellEffects(408, OBJECT_SELF, oTarget); +} + +void DrunkenRage() +{ + float fDuration = GetLevelByClass(CLASS_TYPE_DRUNKEN_MASTER) > 9 ? HoursToSeconds(3) : HoursToSeconds(1); + + effect eLink = EffectLinkEffects(EffectAbilityIncrease(ABILITY_STRENGTH, 4), EffectAbilityIncrease(ABILITY_CONSTITUTION, 4)); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_WILL, 2)); + eLink = EffectLinkEffects(eLink, EffectACDecrease(2)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_BLUR)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_AURA_FIRE)); + eLink = ExtraordinaryEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, OBJECT_SELF, fDuration); + + FloatingTextStringOnCreature("Drunken Rage Activated", OBJECT_SELF); +} + +void DrunkLikeDemon() +{ + // A Drunken Master has had a drink. Add effects: + effect eLink = EffectLinkEffects(EffectAbilityIncrease(ABILITY_STRENGTH, 1), EffectAbilityIncrease(ABILITY_CONSTITUTION, 1)); + eLink = EffectLinkEffects(eLink, EffectAbilityDecrease(ABILITY_WISDOM, 1)); + eLink = EffectLinkEffects(eLink, EffectAbilityDecrease(ABILITY_INTELLIGENCE, 1)); + eLink = EffectLinkEffects(eLink, EffectAbilityDecrease(ABILITY_DEXTERITY, 1)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_BLUR)); + + //run checks to see if Dex Modifier will be changed: + if(!(GetAbilityModifier(ABILITY_DEXTERITY) % 2)) + { + //restore AC, Ref save and Tumble to previous values + eLink = EffectLinkEffects(eLink, EffectACIncrease(1)); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_REFLEX, 1)); + eLink = EffectLinkEffects(eLink, EffectSkillIncrease(SKILL_TUMBLE, 1)); + } + eLink = ExtraordinaryEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, OBJECT_SELF, HoursToSeconds(1)); + + FloatingTextStringOnCreature("You are Drunk Like a Demon", OBJECT_SELF); +} + +void MakeDrunk(int nSpellID) +{ + if(Random(100) < 40) + AssignCommand(OBJECT_SELF, ActionPlayAnimation(ANIMATION_LOOPING_TALK_LAUGHING)); + else + AssignCommand(OBJECT_SELF, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_DRUNK)); + + int nPoints; + switch(nSpellID) + { + case 406: nPoints = 1; break;//Ale + case 407: nPoints = 2; break;//Wine + case 408: nPoints = 3; break;//Spirits + } + + //ApplyAbilityDamage(oTarget, ABILITY_INTELLIGENCE, nPoints, DURATION_TYPE_TEMPORARY, TRUE, 60.0); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nPoints)), OBJECT_SELF, 60.0); + AssignCommand(OBJECT_SELF, ActionSpeakStringByStrRef(10499)); +} + +void DrunkenMasterSpeakString() +{ + switch(d6()) + { + case 1: AssignCommand(OBJECT_SELF, ActionSpeakString("Now that's the stuff!")); break; + case 2: AssignCommand(OBJECT_SELF, ActionSpeakString("That one really hit the spot!")); break; + case 3: AssignCommand(OBJECT_SELF, ActionSpeakString("That should keep me warm!")); break; + case 4: AssignCommand(OBJECT_SELF, ActionSpeakString("Good stuff!")); break; + case 5: AssignCommand(OBJECT_SELF, ActionSpeakString("Bless the Wine Gods!")); break; + case 6: AssignCommand(OBJECT_SELF, ActionSpeakString("Just what I needed!")); break; + } +} + +void DrunkenMasterCreateEmptyBottle(int nSpellID) +{ + switch(nSpellID) + { + case 406: CreateItemOnObject("nw_it_thnmisc002", OBJECT_SELF); break;//Ale + case 407: CreateItemOnObject("nw_it_thnmisc004", OBJECT_SELF); break;//Wine + case 408: CreateItemOnObject("nw_it_thnmisc003", OBJECT_SELF); break;//Spirits + default: CreateItemOnObject("nw_it_thnmisc001", OBJECT_SELF); break;//Other + } +} + +////////////////End Drunken Master////////////////// + +////////////////Begin Samurai////////////////// + +// This function is probably utterly broken: the match found variable is not reset in the loop and the returned value will be equal to the last match - Ornedan +int GetPropertyValue(object oWeapon, int iType, int iSubType = -1, int bDebug = FALSE); + +int GetPropertyValue(object oWeapon, int iType, int iSubType = -1, int bDebug = FALSE) +{ + int bReturn = -1; + if(oWeapon == OBJECT_INVALID){return FALSE;} + int bMatch = FALSE; + if (GetItemHasItemProperty(oWeapon, iType)) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("It has the property."));} + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == iType) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Again..."));} + bMatch = TRUE; + if (iSubType > -1) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Subtype Required."));} + if(GetItemPropertySubType(ip) != iSubType) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Subtype wrong."));} + bMatch = FALSE; + } + else + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Subtype Correct."));} + } + } + } + if (bMatch) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Match found."));} + if (GetItemPropertyCostTableValue(ip) > -1) + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Cost value found, returning."));} + bReturn = GetItemPropertyCostTableValue(ip); + } + else + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("No cost value for property, returning TRUE."));} + bReturn = 1; + } + } + else + { + if(bDebug){AssignCommand(GetFirstPC(), SpeakString("Match not found."));} + } + ip = GetNextItemProperty(oWeapon); + } + } + return bReturn; +} + + +void WeaponUpgradeVisual(); + +object GetSamuraiToken(object oSamurai); + +void WeaponUpgradeVisual() +{ + object oPC = GetPCSpeaker(); + int iCost = GetLocalInt(oPC, "CODI_SAM_WEAPON_COST"); + object oToken = GetSamuraiToken(oPC); + int iToken = StringToInt(GetTag(oToken)); + int iGold = GetGold(oPC); + if(iGold + iToken < iCost) + { + SendMessageToPC(oPC, "You sense the gods are angered!"); + AdjustAlignment(oPC, ALIGNMENT_CHAOTIC, 25, FALSE); + object oWeapon = GetItemPossessedBy(oPC, "codi_sam_mw"); + DestroyObject(oWeapon); + return; + } + else if(iToken <= iCost) + { + iCost = iCost - iToken; + DestroyObject(oToken); + TakeGoldFromCreature(iCost, oPC, TRUE); + } + else if (iToken > iCost) + { + object oNewToken = CopyObject(oToken, GetLocation(oPC), oPC, IntToString(iToken - iCost)); + DestroyObject(oToken); + } + effect eVis = EffectVisualEffect(VFX_FNF_DISPEL_DISJUNCTION); + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE,1.0,6.0)); + AssignCommand(oPC, ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY2)); + DelayCommand(0.1, SetCommandable(FALSE, oPC)); + DelayCommand(6.5, SetCommandable(TRUE, oPC)); + DelayCommand(5.0,ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetLocation(oPC))); +} + +object GetSamuraiToken(object oSamurai) +{ + object oItem = GetFirstItemInInventory(oSamurai); + while(oItem != OBJECT_INVALID) + { + if(GetResRef(oItem) == "codi_sam_token") + { + return oItem; + } + oItem = GetNextItemInInventory(oSamurai); + } + return OBJECT_INVALID; +} + + + + +////////////////End Samurai////////////////// + +////////////////Begin Vile Feat////////////////// + + +int Vile_Feat(int iTypeWeap) +{ + switch(iTypeWeap) + { + case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_BASTARDSWORD); + case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_VILE_MARTIAL_BATTLEAXE); + case BASE_ITEM_CLUB: return GetHasFeat(FEAT_VILE_MARTIAL_CLUB); + case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_VILE_MARTIAL_DAGGER); + case BASE_ITEM_DART: return GetHasFeat(FEAT_VILE_MARTIAL_DART); + case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_VILE_MARTIAL_DIREMACE); + case BASE_ITEM_DOUBLEAXE: return GetHasFeat(FEAT_VILE_MARTIAL_DOUBLEAXE); + case BASE_ITEM_DWARVENWARAXE: return GetHasFeat(FEAT_VILE_MARTIAL_DWAXE); + case BASE_ITEM_GREATAXE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATAXE); + case BASE_ITEM_GREATSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD); + case BASE_ITEM_HALBERD: return GetHasFeat(FEAT_VILE_MARTIAL_HALBERD); + case BASE_ITEM_HANDAXE: return GetHasFeat(FEAT_VILE_MARTIAL_HANDAXE); + case BASE_ITEM_HEAVYCROSSBOW: return GetHasFeat(FEAT_VILE_MARTIAL_HEAVYCROSSBOW); + case BASE_ITEM_HEAVYFLAIL: return GetHasFeat(FEAT_VILE_MARTIAL_HEAVYFLAIL); + case BASE_ITEM_KAMA: return GetHasFeat(FEAT_VILE_MARTIAL_KAMA); + case BASE_ITEM_KATANA: return GetHasFeat(FEAT_VILE_MARTIAL_KATANA); + case BASE_ITEM_KUKRI: return GetHasFeat(FEAT_VILE_MARTIAL_KUKRI); + case BASE_ITEM_LIGHTCROSSBOW: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHTCROSSBOW); + case BASE_ITEM_LIGHTFLAIL: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHTFLAIL); + case BASE_ITEM_LIGHTHAMMER: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHTHAMMER); + case BASE_ITEM_LIGHTMACE: return GetHasFeat(FEAT_VILE_MARTIAL_MACE); + case BASE_ITEM_LONGBOW: return GetHasFeat(FEAT_VILE_MARTIAL_LONGBOW); + case BASE_ITEM_LONGSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_LONGSWORD); + case BASE_ITEM_MORNINGSTAR: return GetHasFeat(FEAT_VILE_MARTIAL_MORNINGSTAR); + case BASE_ITEM_QUARTERSTAFF: return GetHasFeat(FEAT_VILE_MARTIAL_QUARTERSTAFF); + case BASE_ITEM_RAPIER: return GetHasFeat(FEAT_VILE_MARTIAL_RAPIER); + case BASE_ITEM_SCIMITAR: return GetHasFeat(FEAT_VILE_MARTIAL_SCIMITAR); + case BASE_ITEM_SCYTHE: return GetHasFeat(FEAT_VILE_MARTIAL_SCYTHE); + case BASE_ITEM_SHORTBOW: return GetHasFeat(FEAT_VILE_MARTIAL_SHORTBOW); + case BASE_ITEM_SHORTSPEAR: return GetHasFeat(FEAT_VILE_MARTIAL_SPEAR); + case BASE_ITEM_SHORTSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_SHORTSWORD); + case BASE_ITEM_SHURIKEN: return GetHasFeat(FEAT_VILE_MARTIAL_SHURIKEN); + case BASE_ITEM_SLING: return GetHasFeat(FEAT_VILE_MARTIAL_SLING); + case BASE_ITEM_SICKLE: return GetHasFeat(FEAT_VILE_MARTIAL_SICKLE); + case BASE_ITEM_TWOBLADEDSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_TWOBLADED); + case BASE_ITEM_WARHAMMER: return GetHasFeat(FEAT_VILE_MARTIAL_WARHAMMER); + case BASE_ITEM_WHIP: return GetHasFeat(FEAT_VILE_MARTIAL_WHIP); + case BASE_ITEM_TRIDENT: return GetHasFeat(FEAT_VILE_MARTIAL_TRIDENT); + + //:: New items + case BASE_ITEM_ELVEN_LIGHTBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_SHORTSWORD) || + GetHasFeat(FEAT_VILE_MARTIAL_RAPIER) || + GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_LIGHTBLADE)); + + case BASE_ITEM_ELVEN_THINBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_LONGSWORD) || + GetHasFeat(FEAT_VILE_MARTIAL_RAPIER) || + GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_THINBLADE)); + + case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD || + GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_COURTBLADE)); + + case BASE_ITEM_DOUBLE_SCIMITAR: return GetHasFeat(FEAT_VILE_MARTIAL_DBL_SCIMITAR); + case BASE_ITEM_EAGLE_CLAW: return GetHasFeat(FEAT_VILE_MARTIAL_EAGLE_CLAW); + case BASE_ITEM_FALCHION: return GetHasFeat(FEAT_VILE_MARTIAL_FALCHION); + case BASE_ITEM_GOAD: return GetHasFeat(FEAT_VILE_MARTIAL_GOAD); + case BASE_ITEM_HEAVY_MACE: return GetHasFeat(FEAT_VILE_MARTIAL_HEAVY_MACE); + case BASE_ITEM_HEAVY_PICK: return GetHasFeat(FEAT_VILE_MARTIAL_HEAVY_PICK); + case BASE_ITEM_KATAR: return GetHasFeat(FEAT_VILE_MARTIAL_KATAR); + case BASE_ITEM_LIGHT_LANCE: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHT_LANCE); + case BASE_ITEM_LIGHT_PICK: return GetHasFeat(FEAT_VILE_MARTIAL_LIGHT_PICK); + case BASE_ITEM_MAUL: return GetHasFeat(FEAT_VILE_MARTIAL_MAUL); + case BASE_ITEM_NUNCHAKU: return GetHasFeat(FEAT_VILE_MARTIAL_NUNCHAKU); + case BASE_ITEM_SAI: return GetHasFeat(FEAT_VILE_MARTIAL_SAI); + case BASE_ITEM_SAP: return GetHasFeat(FEAT_VILE_MARTIAL_SAP); + } + + return FALSE; + +} + +////////////////End Vile Feat////////////////// + +////////////////Begin Soul Inc////////////////// + +const int IPRP_CONST_ONHIT_DURATION_5_PERCENT_1_ROUNDS = 20; + +int GetSanctifedMartialFeat(int iTypeWeap) +{ + switch(iTypeWeap) + { + case BASE_ITEM_BASTARDSWORD: return FEAT_SANCTIFY_MARTIAL_BASTARDSWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_SANCTIFY_MARTIAL_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_SANCTIFY_MARTIAL_CLUB; + case BASE_ITEM_DAGGER: return FEAT_SANCTIFY_MARTIAL_DAGGER; + case BASE_ITEM_DART: return FEAT_SANCTIFY_MARTIAL_DART; + case BASE_ITEM_DIREMACE: return FEAT_SANCTIFY_MARTIAL_DIREMACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_SANCTIFY_MARTIAL_DOUBLEAXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_SANCTIFY_MARTIAL_DWAXE; + case BASE_ITEM_GREATAXE: return FEAT_SANCTIFY_MARTIAL_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_SANCTIFY_MARTIAL_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_SANCTIFY_MARTIAL_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_SANCTIFY_MARTIAL_HANDAXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL; + case BASE_ITEM_KAMA: return FEAT_SANCTIFY_MARTIAL_KAMA; + case BASE_ITEM_KATANA: return FEAT_SANCTIFY_MARTIAL_KATANA; + case BASE_ITEM_KUKRI: return FEAT_SANCTIFY_MARTIAL_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_SANCTIFY_MARTIAL_MACE; + case BASE_ITEM_LONGBOW: return FEAT_SANCTIFY_MARTIAL_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_SANCTIFY_MARTIAL_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_SANCTIFY_MARTIAL_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_SANCTIFY_MARTIAL_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_SANCTIFY_MARTIAL_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_SANCTIFY_MARTIAL_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_SANCTIFY_MARTIAL_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_SANCTIFY_MARTIAL_SPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_SANCTIFY_MARTIAL_SHORTSWORD; + case BASE_ITEM_SHURIKEN: return FEAT_SANCTIFY_MARTIAL_SHURIKEN; + case BASE_ITEM_SLING: return FEAT_SANCTIFY_MARTIAL_SLING; + case BASE_ITEM_SICKLE: return FEAT_SANCTIFY_MARTIAL_SICKLE; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_SANCTIFY_MARTIAL_TWOBLADED; + case BASE_ITEM_WARHAMMER: return FEAT_SANCTIFY_MARTIAL_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_SANCTIFY_MARTIAL_WHIP; + case BASE_ITEM_TRIDENT: return FEAT_SANCTIFY_MARTIAL_TRIDENT; + + //new items + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_SANCTIFY_MARTIAL_SHORTSWORD || + FEAT_SANCTIFY_MARTIAL_RAPIER || + FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE; + + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_SANCTIFY_MARTIAL_LONGSWORD || + FEAT_SANCTIFY_MARTIAL_RAPIER || + FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE; + + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_SANCTIFY_MARTIAL_GREATSWORD || + FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE; + + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW; + case BASE_ITEM_FALCHION: return FEAT_SANCTIFY_MARTIAL_FALCHION; + case BASE_ITEM_GOAD: return FEAT_SANCTIFY_MARTIAL_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_SANCTIFY_MARTIAL_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_SANCTIFY_MARTIAL_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_SANCTIFY_MARTIAL_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_SANCTIFY_MARTIAL_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_SANCTIFY_MARTIAL_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_SANCTIFY_MARTIAL_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_SANCTIFY_MARTIAL_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_SANCTIFY_MARTIAL_SAI; + case BASE_ITEM_SAP: return FEAT_SANCTIFY_MARTIAL_SAP; + } + + return FALSE; +} + +int Sanctify_Feat(int iTypeWeap) +{ + switch(iTypeWeap) + { + case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BASTARDSWORD); + case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BATTLEAXE); + case BASE_ITEM_CLUB: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_CLUB); + case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DAGGER); + case BASE_ITEM_DART: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DART); + case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DIREMACE); + case BASE_ITEM_DOUBLEAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DOUBLEAXE); + case BASE_ITEM_DWARVENWARAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DWAXE); + case BASE_ITEM_GREATAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GREATAXE); + case BASE_ITEM_GREATSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GREATSWORD); + case BASE_ITEM_HALBERD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HALBERD); + case BASE_ITEM_HANDAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HANDAXE); + case BASE_ITEM_HEAVYCROSSBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW); + case BASE_ITEM_HEAVYFLAIL: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL); + case BASE_ITEM_KAMA: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KAMA); + case BASE_ITEM_KATANA: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KATANA); + case BASE_ITEM_KUKRI: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KUKRI); + case BASE_ITEM_LIGHTCROSSBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW); + case BASE_ITEM_LIGHTFLAIL: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL); + case BASE_ITEM_LIGHTHAMMER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER); + case BASE_ITEM_LIGHTMACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_MACE); + case BASE_ITEM_LONGBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LONGBOW); + case BASE_ITEM_LONGSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LONGSWORD); + case BASE_ITEM_MORNINGSTAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_MORNINGSTAR); + case BASE_ITEM_QUARTERSTAFF: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF); + case BASE_ITEM_RAPIER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_RAPIER); + case BASE_ITEM_SCIMITAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SCIMITAR); + case BASE_ITEM_SCYTHE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SCYTHE); + case BASE_ITEM_SHORTBOW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHORTBOW); + case BASE_ITEM_SHORTSPEAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SPEAR); + case BASE_ITEM_SHORTSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHORTSWORD); + case BASE_ITEM_SHURIKEN: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHURIKEN); + case BASE_ITEM_SLING: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SLING); + case BASE_ITEM_SICKLE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SICKLE); + case BASE_ITEM_TWOBLADEDSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_TWOBLADED); + case BASE_ITEM_WARHAMMER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_WARHAMMER); + case BASE_ITEM_WHIP: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_WHIP); + case BASE_ITEM_TRIDENT: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_TRIDENT); + + //:: New items + case BASE_ITEM_ELVEN_LIGHTBLADE: return (GetHasFeat(FEAT_SANCTIFY_MARTIAL_SHORTSWORD) || + GetHasFeat(FEAT_SANCTIFY_MARTIAL_RAPIER) || + GetHasFeat(FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE)); + + case BASE_ITEM_ELVEN_THINBLADE: return (GetHasFeat(FEAT_SANCTIFY_MARTIAL_LONGSWORD) || + GetHasFeat(FEAT_SANCTIFY_MARTIAL_RAPIER) || + GetHasFeat(FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE)); + + case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GREATSWORD || + GetHasFeat(FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE)); + + case BASE_ITEM_DOUBLE_SCIMITAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR); + case BASE_ITEM_EAGLE_CLAW: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW); + case BASE_ITEM_FALCHION: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_FALCHION); + case BASE_ITEM_GOAD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_GOAD); + case BASE_ITEM_HEAVY_MACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HEAVY_MACE); + case BASE_ITEM_HEAVY_PICK: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_HEAVY_PICK); + case BASE_ITEM_KATAR: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_KATAR); + case BASE_ITEM_LIGHT_LANCE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHT_LANCE); + case BASE_ITEM_LIGHT_PICK: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_LIGHT_PICK); + case BASE_ITEM_MAUL: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_MAUL); + case BASE_ITEM_NUNCHAKU: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_NUNCHAKU); + case BASE_ITEM_SAI: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SAI); + case BASE_ITEM_SAP: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_SAP); + } + + return FALSE; +} + +int DamageConv(int iMonsDmg) +{ + + switch(iMonsDmg) + { + case IP_CONST_MONSTERDAMAGE_1d4: return 1; + case IP_CONST_MONSTERDAMAGE_1d6: return 2; + case IP_CONST_MONSTERDAMAGE_1d8: return 3; + case IP_CONST_MONSTERDAMAGE_1d10: return 4; + case IP_CONST_MONSTERDAMAGE_1d12: return 5; + case IP_CONST_MONSTERDAMAGE_1d20: return 6; + + case IP_CONST_MONSTERDAMAGE_2d4: return 10; + case IP_CONST_MONSTERDAMAGE_2d6: return 11; + case IP_CONST_MONSTERDAMAGE_2d8: return 12; + case IP_CONST_MONSTERDAMAGE_2d10: return 13; + case IP_CONST_MONSTERDAMAGE_2d12: return 14; + case IP_CONST_MONSTERDAMAGE_2d20: return 15; + + case IP_CONST_MONSTERDAMAGE_3d4: return 20; + case IP_CONST_MONSTERDAMAGE_3d6: return 21; + case IP_CONST_MONSTERDAMAGE_3d8: return 22; + case IP_CONST_MONSTERDAMAGE_3d10: return 23; + case IP_CONST_MONSTERDAMAGE_3d12: return 24; + case IP_CONST_MONSTERDAMAGE_3d20: return 25; + + + } + + + return 0; +} + +int ConvMonsterDmg(int iMonsDmg) +{ + + switch(iMonsDmg) + { + case 1: return IP_CONST_MONSTERDAMAGE_1d4; + case 2: return IP_CONST_MONSTERDAMAGE_1d6; + case 3: return IP_CONST_MONSTERDAMAGE_1d8; + case 4: return IP_CONST_MONSTERDAMAGE_1d10; + case 5: return IP_CONST_MONSTERDAMAGE_1d12; + case 6: return IP_CONST_MONSTERDAMAGE_1d20; + case 10: return IP_CONST_MONSTERDAMAGE_2d4; + case 11: return IP_CONST_MONSTERDAMAGE_2d6; + case 12: return IP_CONST_MONSTERDAMAGE_2d8; + case 13: return IP_CONST_MONSTERDAMAGE_2d10; + case 14: return IP_CONST_MONSTERDAMAGE_2d12; + case 15: return IP_CONST_MONSTERDAMAGE_2d20; + case 20: return IP_CONST_MONSTERDAMAGE_3d4; + case 21: return IP_CONST_MONSTERDAMAGE_3d6; + case 22: return IP_CONST_MONSTERDAMAGE_3d8; + case 23: return IP_CONST_MONSTERDAMAGE_3d10; + case 24: return IP_CONST_MONSTERDAMAGE_3d12; + case 25: return IP_CONST_MONSTERDAMAGE_3d20; + + } + + return 0; +} + +int MonsterDamage(object oItem) +{ + int iBonus; + int iTemp; + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_MONSTER_DAMAGE) + { + iTemp = GetItemPropertyCostTableValue(ip); + iBonus = iTemp > iBonus ? iTemp : iBonus; + } + ip = GetNextItemProperty(oItem); + } + + return iBonus; +} + +int FeatIniDmg(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_BONUS_FEAT) + { + if(GetItemPropertySubType(ip) == IP_CONST_FEAT_WeapFocCreature) + return 1; + } + ip = GetNextItemProperty(oItem); + } + return 0; +} + +void AddIniDmg(object oPC) +{ + + int bUnarmedDmg = GetHasFeat(FEAT_INCREASE_DAMAGE1, oPC) + + GetHasFeat(FEAT_INCREASE_DAMAGE2, oPC); + + if(!bUnarmedDmg) + return; + + object oCweapB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oCweapL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oCweapR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + + int iDmg; + int iConv; + int iStr = GetAbilityModifier(ABILITY_STRENGTH, oPC); + int iWis = GetAbilityModifier(ABILITY_WISDOM, oPC); + iWis = iWis > iStr ? iWis : 0; + + + /*if(GetHasFeat(FEAT_INTUITIVE_ATTACK, oPC)) + { + SetCompositeBonusT(oCweapB,"",iWis,ITEM_PROPERTY_ATTACK_BONUS); + SetCompositeBonusT(oCweapL,"",iWis,ITEM_PROPERTY_ATTACK_BONUS); + SetCompositeBonusT(oCweapR,"",iWis,ITEM_PROPERTY_ATTACK_BONUS); + } + if (GetHasFeat(FEAT_RAVAGEGOLDENICE, oPC)) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE,2),oCweapB,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE,2),oCweapL,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE,2),oCweapR,9999.0); + }*/ + + + if ( oCweapB != OBJECT_INVALID && !FeatIniDmg(oCweapB)) + { + iDmg = MonsterDamage(oCweapB); + iConv = DamageConv(iDmg) + bUnarmedDmg; + iConv = (iConv > 6 && iConv < 10) ? 6 : iConv; + iConv = (iConv > 15 && iConv < 20) ? 15 : iConv; + iConv = (iConv > 25) ? 25 : iConv; + iConv = ConvMonsterDmg(iConv); + TotalAndRemoveProperty(oCweapB,ITEM_PROPERTY_MONSTER_DAMAGE,-1); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iConv),oCweapB); + //AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oCweapB); + IPSafeAddItemProperty(oCweapB, PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + if ( oCweapL != OBJECT_INVALID && !FeatIniDmg(oCweapL)) + { + iDmg = MonsterDamage(oCweapL); + iConv = DamageConv(iDmg) + bUnarmedDmg; + iConv = (iConv > 6 && iConv < 10) ? 6 : iConv; + iConv = (iConv > 15 && iConv < 20) ? 15 : iConv; + iConv = (iConv > 25) ? 25 : iConv; + iConv = ConvMonsterDmg(iConv); + TotalAndRemoveProperty(oCweapL,ITEM_PROPERTY_MONSTER_DAMAGE,-1); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iConv),oCweapL); + //AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oCweapL); + IPSafeAddItemProperty(oCweapL, PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + if ( oCweapR != OBJECT_INVALID && !FeatIniDmg(oCweapR)) + { + iDmg = MonsterDamage(oCweapR); + iConv = DamageConv(iDmg) + bUnarmedDmg; + iConv = (iConv > 6 && iConv < 10) ? 6 : iConv; + iConv = (iConv > 15 && iConv < 20) ? 15 : iConv; + iConv = (iConv > 25) ? 25 : iConv; + iConv = ConvMonsterDmg(iConv); + TotalAndRemoveProperty(oCweapR,ITEM_PROPERTY_MONSTER_DAMAGE,-1); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iConv),oCweapR); + //AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oCweapR); + IPSafeAddItemProperty(oCweapR, PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } +} + +void AddCriti(object oPC,object oSkin,int ip_feat_crit,int nFeat) +{ + // Do not add multiple instances of the same bonus feat iprop, it lags the game + AddSkinFeat(nFeat, ip_feat_crit, oSkin, oPC); +} + +void ImpCrit(object oPC,object oSkin) +{ + if(GetHasFeat(FEAT_WEAPON_FOCUS_BASTARD_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_BASTARD_SWORD, FEAT_IMPROVED_CRITICAL_BASTARD_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_BATTLE_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_BATTLE_AXE, FEAT_IMPROVED_CRITICAL_BATTLE_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_CLUB, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_CLUB, FEAT_IMPROVED_CRITICAL_CLUB); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DAGGER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DAGGER, FEAT_IMPROVED_CRITICAL_DAGGER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DART, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DART, FEAT_IMPROVED_CRITICAL_DART); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DIRE_MACE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DIRE_MACE, FEAT_IMPROVED_CRITICAL_DIRE_MACE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DOUBLE_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DOUBLE_AXE, FEAT_IMPROVED_CRITICAL_DOUBLE_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_DWAXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_DWAXE, FEAT_IMPROVED_CRITICAL_DWAXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_GREAT_AXE, FEAT_IMPROVED_CRITICAL_GREAT_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_GREAT_SWORD, FEAT_IMPROVED_CRITICAL_GREAT_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HALBERD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HALBERD, FEAT_IMPROVED_CRITICAL_HALBERD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HAND_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HAND_AXE, FEAT_IMPROVED_CRITICAL_HAND_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW, FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_FLAIL, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL, FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL); + if(GetHasFeat(FEAT_WEAPON_FOCUS_KAMA, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_KAMA, FEAT_IMPROVED_CRITICAL_KAMA); + if(GetHasFeat(FEAT_WEAPON_FOCUS_KATANA, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_KATANA, FEAT_IMPROVED_CRITICAL_KATANA); + if(GetHasFeat(FEAT_WEAPON_FOCUS_KUKRI, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_KUKRI, FEAT_IMPROVED_CRITICAL_KUKRI); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW, FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_FLAIL, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL, FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_HAMMER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER, FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_MACE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_MACE, FEAT_IMPROVED_CRITICAL_LIGHT_MACE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONG_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LONG_SWORD, FEAT_IMPROVED_CRITICAL_LONG_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONGBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_LONGBOW, FEAT_IMPROVED_CRITICAL_LONGBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_MORNING_STAR, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_MORNING_STAR, FEAT_IMPROVED_CRITICAL_MORNING_STAR); + if(GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_RAPIER, FEAT_IMPROVED_CRITICAL_RAPIER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SCIMITAR, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SCIMITAR, FEAT_IMPROVED_CRITICAL_SCIMITAR); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SCYTHE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SCYTHE, FEAT_IMPROVED_CRITICAL_SCYTHE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORT_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SHORT_SWORD, FEAT_IMPROVED_CRITICAL_SHORT_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORTBOW, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SHORTBOW, FEAT_IMPROVED_CRITICAL_SHORTBOW); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHURIKEN, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SHURIKEN, FEAT_IMPROVED_CRITICAL_SHURIKEN); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SICKLE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SICKLE, FEAT_IMPROVED_CRITICAL_SICKLE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SLING, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SLING, FEAT_IMPROVED_CRITICAL_SLING); + if(GetHasFeat(FEAT_WEAPON_FOCUS_SPEAR, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_SPEAR, FEAT_IMPROVED_CRITICAL_SPEAR); + if(GetHasFeat(FEAT_WEAPON_FOCUS_STAFF, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_STAFF, FEAT_IMPROVED_CRITICAL_STAFF); + if(GetHasFeat(FEAT_WEAPON_FOCUS_THROWING_AXE, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_THROWING_AXE, FEAT_IMPROVED_CRITICAL_THROWING_AXE); + if(GetHasFeat(FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD, FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD); + if(GetHasFeat(FEAT_WEAPON_FOCUS_WAR_HAMMER, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_WAR_HAMMER, FEAT_IMPROVED_CRITICAL_WAR_HAMMER); + if(GetHasFeat(FEAT_WEAPON_FOCUS_WHIP, oPC)) AddCriti(oPC, oSkin, IP_CONST_FEAT_IMPROVED_CRITICAL_WHIP, FEAT_IMPROVED_CRITICAL_WHIP); + +} + +////////////////End Soul Inc////////////////// + +////////////////Begin Martial Strike////////////////// + +void MartialStrike() +{ + object oItem; + object oPC = OBJECT_SELF; + + int iEquip=GetLocalInt(oPC,"ONEQUIP"); + int iType; + + if (iEquip==2) + { + + if (!GetHasFeat(FEAT_HOLY_MARTIAL_STRIKE)) return; + + oItem=GetItemLastEquipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_HOLY),oItem,9999.0); + SetLocalInt(oItem,"MartialStrik",1); + } + else if (iEquip==1) + { + oItem=GetItemLastUnequipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if ( GetLocalInt(oItem,"MartialStrik")) + { + RemoveSpecificProperty(oItem,ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP,IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGEBONUS_2d6, 1,"",IP_CONST_DAMAGETYPE_DIVINE,DURATION_TYPE_TEMPORARY); + RemoveSpecificProperty(oItem,ITEM_PROPERTY_VISUALEFFECT,ITEM_VISUAL_HOLY,-1,1,"",-1,DURATION_TYPE_TEMPORARY); + DeleteLocalInt(oItem,"MartialStrik"); + } + + } + else + { + + if (!GetHasFeat(FEAT_HOLY_MARTIAL_STRIKE)) return; + + oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if (!GetLocalInt(oItem,"MartialStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_HOLY),oItem,9999.0); + SetLocalInt(oItem,"MartialStrik",1); + } + oItem=GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + iType= GetBaseItemType(oItem); + if ( !GetLocalInt(oItem,"MartialStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_HOLY),oItem,9999.0); + SetLocalInt(oItem,"MartialStrik",1); + } + } + + +} + + +void UnholyStrike() +{ + object oItem; + object oPC = OBJECT_SELF; + + int iEquip=GetLocalInt(oPC,"ONEQUIP"); + int iType; + + if (iEquip==2) + { + + if (!GetHasFeat(FEAT_UNHOLY_STRIKE)) return; + + oItem=GetItemLastEquipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_EVIL),oItem,9999.0); + SetLocalInt(oItem,"UnholyStrik",1); + } + else if (iEquip==1) + { + oItem=GetItemLastUnequipped(); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if ( GetLocalInt(oItem,"UnholyStrik")) + { + RemoveSpecificProperty(oItem,ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP,IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGEBONUS_2d6, 1,"",IP_CONST_DAMAGETYPE_DIVINE,DURATION_TYPE_TEMPORARY); + RemoveSpecificProperty(oItem,ITEM_PROPERTY_VISUALEFFECT,ITEM_VISUAL_EVIL,-1,1,"",-1,DURATION_TYPE_TEMPORARY); + DeleteLocalInt(oItem,"UnholyStrik"); + } + + } + else + { + + if (!GetHasFeat(FEAT_UNHOLY_STRIKE)) return; + + oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); + iType= GetBaseItemType(oItem); + + switch (iType) + { + case BASE_ITEM_BOLT: + case BASE_ITEM_BULLET: + case BASE_ITEM_ARROW: + iType=GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + break; + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_LONGBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_ARROWS); + break; + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_HEAVYCROSSBOW: + oItem=GetItemInSlot(INVENTORY_SLOT_BOLTS); + break; + case BASE_ITEM_SLING: + oItem=GetItemInSlot(INVENTORY_SLOT_BULLETS); + break; + } + + if (!GetLocalInt(oItem,"UnholyStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_EVIL),oItem,9999.0); + SetLocalInt(oItem,"UnholyStrik",1); + } + oItem=GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + iType= GetBaseItemType(oItem); + if ( !GetLocalInt(oItem,"UnholyStrik")) + { + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD,IP_CONST_DAMAGETYPE_DIVINE,IP_CONST_DAMAGEBONUS_2d6),oItem,9999.0); + AddItemProperty(DURATION_TYPE_TEMPORARY,ItemPropertyVisualEffect(ITEM_VISUAL_EVIL),oItem,9999.0); + SetLocalInt(oItem,"UnholyStrik",1); + } + } + + +} + +////////////////End Martial Strike////////////////// + +////////////////Begin Soldier of Light Spells////////////////// +/* As far as I can tell, not used at all - Ornedan +void spellsCureMod(int nCasterLvl ,int nDamage, int nMaxExtraDamage, int nMaximized, int vfx_impactHurt, int vfx_impactHeal, int nSpellID) +{ + //Declare major variables + object oTarget = PRCGetSpellTargetObject(); + int nHeal; + int nMetaMagic = PRCGetMetaMagicFeat(); + effect eVis = EffectVisualEffect(vfx_impactHurt); + effect eVis2 = EffectVisualEffect(vfx_impactHeal); + effect eHeal, eDam; + + int nExtraDamage = nCasterLvl; // * figure out the bonus damage + if (nExtraDamage > nMaxExtraDamage) + { + nExtraDamage = nMaxExtraDamage; + } + // * if low or normal difficulty is treated as MAXIMIZED + if(GetIsPC(oTarget) && GetGameDifficulty() < GAME_DIFFICULTY_CORE_RULES) + { + nDamage = nMaximized + nExtraDamage; + } + else + { + nDamage = nDamage + nExtraDamage; + } + + + //Make metamagic checks + int iBlastFaith = BlastInfidelOrFaithHeal(OBJECT_SELF, oTarget, DAMAGE_TYPE_POSITIVE, TRUE); + if (nMetaMagic & METAMAGIC_MAXIMIZE || iBlastFaith) + { + nDamage = nMaximized + nExtraDamage; + // * if low or normal difficulty then MAXMIZED is doubled. + if(GetIsPC(OBJECT_SELF) && GetGameDifficulty() < GAME_DIFFICULTY_CORE_RULES) + { + nDamage = nDamage + nExtraDamage; + } + } + if (nMetaMagic & METAMAGIC_EMPOWER || GetHasFeat(FEAT_HEALING_DOMAIN_POWER)) + { + nDamage = nDamage + (nDamage/2); + } + + + if (MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) + { + //Figure out the amount of damage to heal + nHeal = nDamage; + //Set the heal effect + eHeal = EffectHeal(nHeal); + //Apply heal effect and VFX impact + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis2, oTarget); + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellID, FALSE)); + + + } + //Check that the target is undead + else + { + int nTouch = PRCDoMeleeTouchAttack(oTarget);; + if (nTouch > 0) + { + if(!GetIsReactionTypeFriendly(oTarget)) + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpellID)); + if (!PRCDoResistSpell(OBJECT_SELF, oTarget,nCasterLvl+add_spl_pen(OBJECT_SELF))) + { + eDam = EffectDamage(nDamage,DAMAGE_TYPE_NEGATIVE); + //Apply the VFX impact and effects + DelayCommand(1.0, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + } + } + } + } +} +*/ +////////////////End Soldier of Light Spells////////////////// + +////////////////Begin Master Harper Instruments////////////////// + +void ActiveModeCIMM(object oTarget) +{ + if(!GetLocalInt(oTarget,"use_CIMM") ) + { + string sScript = GetModuleOverrideSpellscript(); + if (sScript != "mh_spell_at_inst") + { + SetLocalString(OBJECT_SELF,"temp_spell_at_inst",sScript); + SetLocalString(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT", "mh_spell_at_inst"); + } + SetLocalInt(OBJECT_SELF,"nb_spell_at_inst",GetLocalInt(OBJECT_SELF,"nb_spell_at_inst")+1); + FloatingTextStrRefOnCreature(16825240,oTarget); + SetLocalInt(oTarget,"use_CIMM",TRUE); + } +} + +void UnactiveModeCIMM(object oTarget) +{ + if(GetLocalInt(oTarget,"use_CIMM") ) + { + string sScript = GetModuleOverrideSpellscript(); + SetLocalInt(OBJECT_SELF,"nb_spell_at_inst",GetLocalInt(OBJECT_SELF,"nb_spell_at_inst")-1); + if (sScript == "mh_spell_at_inst" && GetLocalInt(OBJECT_SELF,"nb_spell_at_inst") == 0) + { + SetLocalString(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT", GetLocalString(OBJECT_SELF,"temp_spell_at_inst")); + GetLocalString(OBJECT_SELF,"temp_spell_at_inst"); + SetLocalString(OBJECT_SELF,"temp_spell_at_inst",""); + } + FloatingTextStrRefOnCreature(16825241,oTarget); + SetLocalInt(oTarget,"use_CIMM",FALSE); + } +} + +////////////////End Master Harper Instruments////////////////// + +////////////////Begin Minstrel of the Edge////////////////// + +// Goes a bit further than RemoveSpellEffects -- makes sure to remove ALL effects +// made by the Singer+Song. +void RemoveSongEffects(int iSong, object oCaster, object oTarget) +{ + effect eCheck = GetFirstEffect(oTarget); + while (GetIsEffectValid(eCheck)) + { + if (GetEffectCreator(eCheck) == oCaster && GetEffectSpellId(eCheck) == iSong) + RemoveEffect(oTarget, eCheck); + eCheck = GetNextEffect(oTarget); + } +} + +// Stores a Song recipient to the PC as a local variable, and creates a list by using +// an index variable. +void StoreSongRecipient(object oRecipient, object oSinger, int iSongID, int iDuration = 0) +{ + int iSlot = GetLocalInt(oSinger, "SONG_SLOT"); + int iIndex = GetLocalInt(oSinger, "SONG_INDEX_" + IntToString(iSlot)) + 1; + string sIndex = "SONG_INDEX_" + IntToString(iSlot); + string sRecip = "SONG_RECIPIENT_" + IntToString(iIndex) + "_" + IntToString(iSlot); + string sSong = "SONG_IN_USE_" + IntToString(iSlot); + + // Store the recipient into the current used slot + SetLocalObject(oSinger, sRecip, oRecipient); + + // Store the song information + SetLocalInt(oSinger, sSong, iSongID); + + // Store the index of creatures we're on + SetLocalInt(oSinger, sIndex, iIndex); +} + +// Removes all effects given by the previous song from all creatures who recieved it. +// Now allows for two "slots", which means you can perform two songs at a time. +void RemoveOldSongEffects(object oSinger, int iSongID) +{ + object oCreature; + int iSlotNow = GetLocalInt(oSinger, "SONG_SLOT"); + int iSlot; + int iNumRecip; + int iSongInUse; + int iIndex; + string sIndex; + string sRecip; + string sSong; + + if (GetHasFeat(FEAT_MINSTREL_GREATER_MINSTREL_SONG, oSinger)) + { + // If you use the same song twice in a row you + // should deal with the same slot again... + if (GetLocalInt(oSinger, "SONG_IN_USE_" + IntToString(iSlotNow)) == iSongID) + iSlot = iSlotNow; + // Otherwise, we should toggle between slot "1" and slot "0" + else + iSlot = (iSlotNow == 1) ? 0 : 1; + } + else + { + iSlot = 0; + } + + // Save the toggle we're on for later. + SetLocalInt(oSinger, "SONG_SLOT", iSlot); + + // Find the proper variable names based on slot + sIndex = "SONG_INDEX_" + IntToString(iSlot); + sSong = "SONG_IN_USE_" + IntToString(iSlot); + + // Store the local variables into script variables + iNumRecip = GetLocalInt(oSinger, sIndex); + iSongInUse = GetLocalInt(oSinger, sSong); + + // Reset the local variables + SetLocalInt(oSinger, sIndex, 0); + SetLocalInt(oSinger, sSong, 0); + + // Removes any effects from the caster first + RemoveSongEffects(iSongInUse, oSinger, oSinger); + + // Removes any effects from the recipients + for (iIndex = 1 ; iIndex <= iNumRecip ; iIndex++) + { + sRecip = "SONG_RECIPIENT_" + IntToString(iIndex) + "_" + IntToString(iSlot); + oCreature = GetLocalObject(oSinger, sRecip); + + RemoveSongEffects(iSongInUse, oSinger, oCreature); + } +} + + +////////////////End Minstrel of the Edge////////////////// + +////////////////Begin Arcane Duelist////////////////// + +void FlurryEffects(object oPC) +{ + effect Effect1 = EffectModifyAttacks(1); + effect Effect2 = EffectAttackDecrease(2, ATTACK_BONUS_MISC); + + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, Effect1, oPC, RoundsToSeconds(10)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, Effect2, oPC, RoundsToSeconds(10)); + +} + +void CheckCombatDexAttack(object oPC) +{ +//object oPC = GetLocalObject(OBJECT_SELF, "PC_IN_COMBAT_WITH_DEXATTACK_ON"); +int iCombat = GetIsInCombat(oPC); +object oWeapon = GetLocalObject(oPC, "CHOSEN_WEAPON"); + + if(iCombat == TRUE && GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) == oWeapon) + { + DelayCommand(6.0, CheckCombatDexAttack(oPC)); + } + else + { + FloatingTextStringOnCreature("Dexterous Attack Mode Deactivated", oPC, FALSE); + effect eEffects = GetFirstEffect(oPC); + while (GetIsEffectValid(eEffects)) + { + + if (GetEffectType(eEffects) == EFFECT_TYPE_ATTACK_INCREASE && GetEffectSpellId(eEffects) == 1761) // dextrous attack + { + RemoveEffect(oPC, eEffects); + } + + eEffects = GetNextEffect(oPC); + } + DeleteLocalObject(OBJECT_SELF, "PC_IN_COMBAT_WITH_DEXATTACK_ON"); + } +} + +void SPMakeAttack(object oTarget, object oImage) +{ + int iDead = GetIsDead(oTarget); + + if(iDead == FALSE) + { + PrintString("TARGET AINT DEAD"); + DelayCommand(6.0, SPMakeAttack(oTarget, oImage)); + AssignCommand(oImage, ActionAttack(oTarget, FALSE)); + } + if(iDead == TRUE) + { + PrintString("TARGET BE DEAD AS A DOORNAIL"); + DestroyObject(oImage, 0.0); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), GetLocation(oImage), 0.0); + } + +} + +////////////////End Arcane Duelist////////////////// + +////////////////Begin Corpsecrafter////////////// + +void CorpseCrafter(object oPC, object oSummon) +{ + // Hijacking this function because it's already in the right places + if (GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC) >= 8) + { + if (DEBUG) DoDebug("Corpsecrafter: Dread Necro"); + int nHD = GetHitDice(oSummon); + effect eHP = EffectTemporaryHitpoints(nHD * 2); + effect eStr = EffectAbilityIncrease(ABILITY_STRENGTH, 4); + effect eDex = EffectAbilityIncrease(ABILITY_DEXTERITY, 4); + eHP = SupernaturalEffect(eHP); + eStr = SupernaturalEffect(EffectLinkEffects(eStr, eDex)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eHP, oSummon); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eStr, oSummon); + } + if (GetHasFeat(FEAT_CORPSECRAFTER, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Corpsecrafter"); + int nHD = GetHitDice(oSummon); + effect eHP = EffectTemporaryHitpoints(nHD * 2); + effect eStr = EffectAbilityIncrease(ABILITY_STRENGTH, 4); + eHP = SupernaturalEffect(eHP); + eStr = SupernaturalEffect(eStr); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eHP, oSummon); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eStr, oSummon); + } + if (GetHasFeat(FEAT_BOLSTER_RESISTANCE, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Bolster Resistance"); + effect eTurn = EffectTurnResistanceIncrease(4); + eTurn = SupernaturalEffect(eTurn); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eTurn, oSummon); + } + if (GetHasFeat(FEAT_DEADLY_CHILL, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Deadly Chill"); + effect eChill = EffectDamageIncrease(DAMAGE_BONUS_1d6, DAMAGE_TYPE_COLD); + eChill = SupernaturalEffect(eChill); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChill, oSummon); + } + if (GetHasFeat(FEAT_HARDENED_FLESH, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Hardened Flesh"); + effect eAC = EffectACIncrease(2); + eAC = SupernaturalEffect(eAC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAC, oSummon); + } + if (GetHasFeat(FEAT_NIMBLE_BONES, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Nimble Bones"); + object oSkin = GetPCSkin(oPC); + itemproperty iInit = PRCItemPropertyBonusFeat(IP_CONST_FEAT_IMPROVED_INIT); + //AddItemProperty(DURATION_TYPE_PERMANENT, iInit, oSkin); + IPSafeAddItemProperty(oSkin, iInit, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + // Speed boost, average speed is 30 feet, so a 10 foot boost is a 33% boost + effect eSpeed = EffectMovementSpeedIncrease(33); + eSpeed = SupernaturalEffect(eSpeed); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSpeed, oSummon); + } + if (GetHasFeat(FEAT_DESTRUCTION_RETRIBUTION, oPC)) + { + if (DEBUG) DoDebug("Corpsecrafter: Destruction Retribution"); + SetLocalInt(oSummon, "DestructionRetribution", TRUE); + } +} + +////////////////Begin Ninja////////////// + +void Ninja_DecrementKi (object oPC, int iExcept = -1) +{ + if (iExcept != FEAT_KI_POWER) + DecrementRemainingFeatUses(oPC, FEAT_KI_POWER); + if (iExcept != FEAT_GHOST_STEP) + DecrementRemainingFeatUses(oPC, FEAT_GHOST_STEP); + if (iExcept != FEAT_GHOST_STRIKE) + DecrementRemainingFeatUses(oPC, FEAT_GHOST_STRIKE); + if (iExcept != FEAT_GHOST_WALK) + DecrementRemainingFeatUses(oPC, FEAT_GHOST_WALK); + if (iExcept != FEAT_KI_DODGE) + DecrementRemainingFeatUses(oPC, FEAT_KI_DODGE); + // for testing only + SetLocalInt(oPC, "prc_ninja_ki", GetLocalInt(oPC, "prc_ninja_ki") - 1); + ExecuteScript("prc_ninjca", oPC); +} + +int Ninja_AbilitiesEnabled (object oPC) +{ + object oLefthand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + + if (GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC)) > 0 || + GetBaseItemType(oLefthand) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oLefthand) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oLefthand) == BASE_ITEM_TOWERSHIELD) + return FALSE; + // all Ki powers will not function when encumbered + if (GetIsEncumbered(oPC)) + return FALSE; + return TRUE; +} + +////////////////End Ninja////////////// + +////////////////Begin Virtuoso////////////// + +//Decrements the daily uses of Virtuoso Performance by the +// correct amount, returns FALSE if there are insufficient +// uses remaining to use the current feat +int VirtuosoPerformanceDecrement(object oPC, int nSpellID) +{ + int nDecrement = 0; + int nDifference = 1122; //hack, difference in number between feat and spell 2da lines + switch(nSpellID) + { + case SPELL_VIRTUOSO_SUSTAINING_SONG: + case SPELL_VIRTUOSO_CALUMNY: + case SPELL_VIRTUOSO_GREATER_CALUMNY: nDecrement = 1; break; + + case SPELL_VIRTUOSO_MINDBENDING_MELODY: + case SPELL_VIRTUOSO_MAGICAL_MELODY: + case SPELL_VIRTUOSO_REVEALING_MELODY: nDecrement = 2; break; + + case SPELL_VIRTUOSO_SHARP_NOTE: + case SPELL_VIRTUOSO_JARRING_SONG: + case SPELL_VIRTUOSO_SONG_OF_FURY: nDecrement = 3; break; + } + if(!nDecrement) return FALSE; //sanity check + int nUses = GetPersistantLocalInt(oPC, "Virtuoso_Performance_Uses"); + if(nUses >= nDecrement) + { + SetPersistantLocalInt(oPC, "Virtuoso_Performance_Uses", nUses - nDecrement); + int nFeat, nDec; + for(nFeat = FEAT_VIRTUOSO_SUSTAINING_SONG; nFeat <= FEAT_VIRTUOSO_PERFORMANCE; nFeat++) + { + nDec = nDecrement; + if(nFeat == (nSpellID + nDifference)) + nDec--; //already decremented once by being used + for(; nDec > 0; nDec--) + DecrementRemainingFeatUses(oPC, nFeat); + } + return TRUE; + } + else + { //refund feat use :P + IncrementRemainingFeatUses(oPC, nSpellID + nDifference); + return FALSE; + } +} + +////////////////End Virtuoso////////////// + + +///////////////Archmage & Heirophant SLAs /////////// + +void DoArchmageHeirophantSLA(object oPC, object oTarget, location lTarget, int nSLAID) +{ + int nSLAFeatID = -1; //feat ID of the SLA in use + int nSLASpellID = -1;//spell ID of the SLA in use NOT THE SPELL BEING CAST + //get the SLAFeatID + int SLA_ID; + switch(SLA_ID) + { + case 1: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_1; break; + case 2: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_2; break; + case 3: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_3; break; + case 4: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_4; break; + case 5: nSLAFeatID = FEAT_SPELL_LIKE_ABILITY_5; break; + } + //get the spellID of the spell your trying to cast + //+1 offset for unassigned + int nSpellID = GetPersistantLocalInt(oPC, "PRC_SLA_SpellID_"+IntToString(nSLAID))-1; + //test if already stored + if(nSpellID == -1) + { + //not stored + FloatingTextStringOnCreature("This SLA has not been stored yet\nThe next spell you cast will be assigned to this SLA", oPC); + SetLocalInt(oPC, "PRC_SLA_Store", nSLAID); + DelayCommand(18.0, + DeleteLocalInt(oPC, "PRC_SLA_Store")); + return; + } + else + { + //stored, recast it + int nSpellClass = GetPersistantLocalInt(oPC, "PRC_SLA_Class_"+IntToString(nSLAID)); + int nMetamagic = GetPersistantLocalInt(oPC, "PRC_SLA_Meta_"+IntToString(nSLAID)); + int nSpellLevel = PRCGetSpellLevelForClass(nSpellID, nSpellClass); + int nBaseDC = 10 + nSpellLevel + GetDCAbilityModForClass(nSpellClass, oPC); + //since this is targetted using a generic feat, + //make sure were within range and target is valid for this spell + //get current distance + /*string sRange = Get2DACache("spells", "Range", nSpellID); + float fDist; + if(GetIsObjectValid(oTarget)) + fDist = GetDistanceToObject(oTarget); + else + fDist = GetDistanceBetweenLocations(GetLocation(oPC), lTarget); + //check distance is allowed + if(fDist < 0.0 + || (sRange == "T" && fDist > 2.25) + || (sRange == "S" && fDist > 8.0 ) + || (sRange == "M" && fDist > 20.0 ) + || (sRange == "L" && fDist > 40.0 ) + ) + { + //out of range + FloatingTextStringOnCreature("You are out of range", oPC); + //replace the useage + IncrementRemainingFeatUses(oPC, nSLAFeatID); + //end the script + return; + }*/ + //check object type + int nTargetType = HexToInt(Get2DACache("spells", "TargetType", nSpellID)); + /* + # 0x01 = 1 = Self + # 0x02 = 2 = Creature + # 0x04 = 4 = Area/Ground + # 0x08 = 8 = Items + # 0x10 = 16 = Doors + # 0x20 = 32 = Placeables + */ + int nCaster = nTargetType & 1; + int nCreature = nTargetType & 2; + int nLocation = nTargetType & 4; + int nItem = nTargetType & 8; + int nDoor = nTargetType & 16; + int nPlaceable = nTargetType & 32; + int nTargetValid = TRUE; + //test targetting self + if(oTarget == OBJECT_SELF) + { + if(!nCaster) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target yourself", oPC); + } + } + //test targetting others + else if(GetIsObjectValid(oTarget)) + { + switch(GetObjectType(oTarget)) + { + case OBJECT_TYPE_CREATURE: + if(!nCreature) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target creatures", oPC); + } + break; + case OBJECT_TYPE_ITEM: + if(!nItem) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target items", oPC); + } + break; + case OBJECT_TYPE_DOOR: + if(!nDoor) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target doors", oPC); + } + break; + case OBJECT_TYPE_PLACEABLE: + if(!nPlaceable) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target placeables", oPC); + } + break; + } + } + //test if can target a location + else if(GetIsObjectValid(GetAreaFromLocation(lTarget))) + { + if(!nLocation) + { + nTargetValid = FALSE; + FloatingTextStringOnCreature("You cannot target locations", oPC); + } + } + //target was not valid, abort + if(!nTargetValid) + { + //replace the useage + IncrementRemainingFeatUses(oPC, nSLAFeatID); + //end the script + return; + } + //actually cast it at this point + //note that these are instant-spells, so we have to add the animation part too + /*if(GetIsObjectValid(oTarget)) + ActionCastFakeSpellAtObject(nSpellID, oTarget); + else + ActionCastFakeSpellAtLocation(nSpellID, lTarget);*/ + ActionDoCommand(ActionCastSpell(nSpellID, 0, nBaseDC, 0, nMetamagic, nSpellClass, 0, 0, OBJECT_INVALID, FALSE)); + } +} + +/////////////// End Archmage & Heirophant SLAs /////////// + +////////////////////////Alienist////////////////////////// +int GetPhobia(object oPC) +{ + int nPhobia = GetPersistantLocalInt(oPC, "Alienist_Phobia"); + if(nPhobia < 1) + { + nPhobia = Random(16) + 1; + SetPersistantLocalInt(oPC, "Alienist_Phobia", nPhobia); + } + return nPhobia; +} + +int GetPhobiaRace(int nPhobia) +{ + switch(nPhobia) + { + case 1: return RACIAL_TYPE_ABERRATION; + case 2: return RACIAL_TYPE_ANIMAL; + case 3: return RACIAL_TYPE_BEAST; + case 4: return RACIAL_TYPE_CONSTRUCT; + case 5: return RACIAL_TYPE_DRAGON; + case 6: return RACIAL_TYPE_HUMANOID_GOBLINOID; + case 7: return RACIAL_TYPE_HUMANOID_MONSTROUS; + case 8: return RACIAL_TYPE_HUMANOID_ORC; + case 9: return RACIAL_TYPE_HUMANOID_REPTILIAN; + case 10: return RACIAL_TYPE_ELEMENTAL; + case 11: return RACIAL_TYPE_FEY; + case 12: return RACIAL_TYPE_GIANT; + case 13: return RACIAL_TYPE_MAGICAL_BEAST; + case 14: return RACIAL_TYPE_SHAPECHANGER; + case 15: return RACIAL_TYPE_UNDEAD; + case 16: return RACIAL_TYPE_VERMIN; + case 17: return RACIAL_TYPE_OOZE; + case 18: return RACIAL_TYPE_PLANT; + } + return -1;//error +} + +int GetPhobiaFeat(int nPhobia) +{ + switch(nPhobia) + { + case 1: return IP_CONST_PHOBIA_ABERRATION; + case 2: return IP_CONST_PHOBIA_ANIMAL; + case 3: return IP_CONST_PHOBIA_BEAST; + case 4: return IP_CONST_PHOBIA_CONSTRUCT; + case 5: return IP_CONST_PHOBIA_DRAGON; + case 6: return IP_CONST_PHOBIA_GOBLINOID; + case 7: return IP_CONST_PHOBIA_MONSTROUS; + case 8: return IP_CONST_PHOBIA_ORC; + case 9: return IP_CONST_PHOBIA_REPTILIAN; + case 10: return IP_CONST_PHOBIA_ELEMENTAL; + case 11: return IP_CONST_PHOBIA_FEY; + case 12: return IP_CONST_PHOBIA_GIANT; + case 13: return IP_CONST_PHOBIA_MAGICAL_BEAST; + case 14: return IP_CONST_PHOBIA_SHAPECHANGER; + case 15: return IP_CONST_PHOBIA_UNDEAD; + case 16: return IP_CONST_PHOBIA_VERMIN; + } + return -1;//error +} + +/////////////////////DragonSong Lyrist//////////////////////// + +void RemoveOldSongs(object oPC) +{ + if(GetHasSpellEffect(SPELL_DSL_SONG_STRENGTH, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_STRENGTH); + if(GetHasSpellEffect(SPELL_DSL_SONG_COMPULSION, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_COMPULSION); + if(GetHasSpellEffect(SPELL_DSL_SONG_SPEED, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_SPEED); + if(GetHasSpellEffect(SPELL_DSL_SONG_FEAR, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_FEAR); + if(GetHasSpellEffect(SPELL_DSL_SONG_HEALING, oPC)) PRCRemoveEffectsFromSpell(oPC, SPELL_DSL_SONG_HEALING); +} + + +// Eldritch Theurge class requires arcane spellcasting and eldritch blast. +// If a character is an Eldritch Theruge we know that she must have levels in Warlock +// since in NWN character can have max 3 classes. We also know that Eldritch Theurge +// is at positon 3 (unless player is cheating). +// So we just need to check the third class. +int GetETArcaneClass(object oPC) +{ + + int nClass = GetPrimaryArcaneClass(oPC); + + /* int nClass = GetClassByPosition(1, oPC); + if(nClass == CLASS_TYPE_WARLOCK) + nClass = GetClassByPosition(2, oPC); */ + return nClass; +} + diff --git a/src/include/prc_inc_combat.nss b/src/include/prc_inc_combat.nss new file mode 100644 index 0000000..f5a2411 --- /dev/null +++ b/src/include/prc_inc_combat.nss @@ -0,0 +1,9254 @@ +//:://///////////////////////////////////////////// +//:: Combat Simulation System +//::////////////////////////////////////////////// +//:: Created By: Oni5115 +//:: Created On: July 16, 2004 +//:: Modified by motu99 On: April 7, 2007 +//::////////////////////////////////////////////// +//:: Code based on Aaon Graywolf's inc_combat.nss +//:: and Soul Taker's additions in inc_combat2.nss +//::////////////////////////////////////////////// +//:: Adds a great deal of additional functionality +//:: to the older combat system to make combat simulation +//:: more accurate and much easier to use. +//::////////////////////////////////////////////// +//:: Current Features: +//::////////////////////////////////////////////// +//:: Easy to use function for performing an entire attack round. PerformAttackRound +//:: Easy to use function for performing a single attack. PerformAttack +//:: Proper calculation of attack bonus including any bonus effects on weapon or spells. +//:: Proper calculation of weapon damage including any bonus effects or spell effects. +//:: Proper calculation of bonus, main, and off-hand attacks. +//:: Support for off-hand attacks from double (sided) weapons +//:: Proper calculation of enemy AC. +//:: Proper application of sneak attacks. +//:: Proper application of touch attacks. +//:: All On Hit: Cast Spell abilities working +//:: All On Hit: Unique Power abilities working +//:: All On Hit: properties working (daze, stun, vorpal, poison, disease, etc.) +//:: All known weapon/spell damage bonuses are counted +//:: +//::////////////////////////////////////////////// +//:: Support for the following feats: +//::////////////////////////////////////////////// +//:: Weapon Focus, Epic Weapon Focus, Epic Prowess, +//:: Improved Critical, Overwhelming Critical, Devastating Critical +//:: Weapon Specialization, Epic Weapon Specialization, +//:: Weapon Finesse, Inuitive Attack, Zen Archery, +//:: +//:: Expertise, Improved Expertise, +//:: Cleave, Great Cleave, Circle Kick, +//:: Power Attack, Improved Power Attack, Supreme Power Attack, +//:: Power Shot, Improved Power Shot, Supreme Power Shot, +//:: +//:: Rapid Shot, Rapid Reload, Extra Shot, Flurry of Blows, +//:: Martial Flurry, Furious Assult, One Strike Two Cuts, +//:: +//:: Epic Dodge, Self Concealment I-V, Blind Fight, Crippling Strike +//:: +//:: All Arcane Archer and Weapon Master Feats. +//:: Favored Enemy, Bane of Enemies, +//:: Battle Training, Divine Might, Bard Song, Thundering Rage +//:: large vs small creature size +//:: +//:: Note: If you notice any feats missing let Oni5115 know +//:: They might be accounted for, but not added to the list; +//:: or they might not be implemented yet. +//:: +//::////////////////////////////////////////////// +//:: Current Limitations: +//::////////////////////////////////////////////// +//:: +//:: Coup De Grace does not take into account bonus damage [Except from touch attack spells] +//:: Calculation of Enemy AC does not take into account bonus vs. racial type or alignment type. +//:: Weapon Range is not taken into account in melee combat (whenever we are within 10 feet, we will apply the damage) +//:: a few "hardwired" spells on weapon and armor of the type IP_CONST_ONHIT_ +//:: are not yet implemented (but we now know how to do them, so hopefully in a later release) +//:: +//::////////////////////////////////////////////// +//:: Tested: +//::////////////////////////////////////////////// +//:: Weapon Information and Damage +//:: Elemental Information and Damage +//:: Sneak Attack Functions +//:: On Hit: Cast Spell +//:: On Hit: Unique Power +//:: On Hit: properties +//:: Perform Attack Round +//:: Perform Attack +//:: Dark Fire and Flame Weapon +//:: Cleave, Great Cleave +//::////////////////////////////////////////////// +//:: Things to Test: +//::////////////////////////////////////////////// +//:: On Hit: Slay Race/Alignment Group/Alignment +//:: Wounding, Disease, Poison, +//:: +//:: Bug Fix for Sanctuary/invis/stealth not being removed +//:: Coup De Grace +//:: Circle Kick +//:: Unarmed Damage Calculation (motu99: partially tested) +//:: +//:: Blinding Speed (should count as haste effect) +//::////////////////////////////////////////////// +//:: Known Bugs: +//::////////////////////////////////////////////// +//:: +//:: GetIsFlanked does not work reliably +//:: AB calculation (mostly done at beginning of round) ignores situational adjustments (such as when attackers come within melee range of a ranged attacker) +//:: Damage calculation (partly done at beginning or round) ignores situational adjustments (for instance, favored enemy bonus is not adjusted when defender changes) +//:: +//::////////////////////////////////////////////// + +// various modes for the prc-combat routines (these are bit-flags) + +// should be used when PerformAttack or PerformAttackRound are run from a heartbeat +// and the aurora engine runs in parallel to prc scripted combat (such as for more than 2 +// offhand attacks or natural weapon attacks). Ensures that when we are not physically attacking +// any more, the scripted combat is aborted +const int PRC_COMBATMODE_HB = 1; + +// allows a switch of target (mostly used when scripted combat and aurora combat run in parallel) +const int PRC_COMBATMODE_ALLOW_TARGETSWITCH = 2; + +// will not abort a scripted melee attack (or attack round), when the target is out of range and cannot be reached with a five foot step +// not applicable for scripted ranged attacks (these are always executed, because so far the range of ranged weapons is not well implemented) +const int PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE = 4; + +// if set, will issue an ActionAttack Command on the last attack of a scripted attack round +// if used in PerformAttack(). will issue an ActionAttack command after the (single) attack +const int PRC_COMBATMODE_ACTIONATTACK_ON_LAST = 8; + +// will bypass damage reduction, if on +const int PRC_COMBATMODE_BYPASS_DR = 16; // not yet implemented + +// constant ints for touch attacks +// using these helps determing which type of touch attack to perform. +// using the spell versions if your spell does not use a weapon. +const int TOUCH_ATTACK_MELEE = 1; +const int TOUCH_ATTACK_RANGED = 2; +const int TOUCH_ATTACK_MELEE_SPELL = 3; +const int TOUCH_ATTACK_RANGED_SPELL = 4; + +// Distances +const float MELEE_RANGE_FEET = 10.; +const float MELEE_RANGE_METERS = 3.05; +const float RANGE_15_FEET_IN_METERS = 4.92; +const float RANGE_5_FEET_IN_METERS = 1.64; + +// maximum distance (in meters) where we actually rush to do an attack +const float fMaxAttackDistance = 20.; + +// constants that determine at what class or skill levels specific classes get specific feats or abilities +const int WEAPON_MASTER_LEVEL_KI_CRITICAL = 7; +const int WEAPON_MASTER_LEVEL_INCREASED_MULTIPLIER = 5; +const int WEAPON_MASTER_LEVEL_SUPERIOR_WEAPON_FOCUS = 5; +const int WEAPON_MASTER_LEVEL_EPIC_SUPERIOR_WEAPON_FOCUS = 13; + +const int RANGER_LEVEL_DUAL_WIELD = 1; +const int RANGER_LEVEL_ITWF = 9; + +const int TEMPEST_LEVEL_STWF = 10; +const int TEMPEST_LEVEL_GTWF = 5; +const int TEMPEST_LEVEL_ITWF = 1; +const int TEMPEST_LEVEL_ABS_AMBIDEX = 8; + +const int BARD_LEVEL_FOR_BARD_SONG_AB_2 = 8; +const int BARD_PERFORM_SKILL_FOR_BARD_SONG_AB_2 = 15; +const int BARD_LEVEL_FOR_BARD_SONG_DAM_2 = 3; +const int BARD_PERFORM_SKILL_FOR_BARD_SONG_DAM_2 = 9; +const int BARD_LEVEL_FOR_BARD_SONG_DAM_3 = 14; +const int BARD_PERFORM_SKILL_FOR_BARD_SONG_DAM_3 = 21; + +//consts for weapon sizes (should be equal to creature sizes) +const int WEAPON_SIZE_SMALL = CREATURE_SIZE_SMALL; + +// spell id constants + +// motu99: this is the spell number for the Create magic tatoo spell that increases AB by 2; +// not sure if it is a bug; in prc_spell_const the create_magic_tatoo spell has a number of 3166 +// fluffyamoeba - it's line 3166 in spells.2da atm, so getting rid of this +// const int SPELL_CREATE_MAGIC_TATOO_2 = 3167; + +// motu99: these spell numbers appeared directly in the code +// don't know why. Put them here with a "_2" extension, so we can check what the real constants are +// const int SPELL_BARD_SONG_2 = 411; +// const int SPELL_BARD_CURSE_SONG_2 = 644; +const int SPELL_HELLINFERNO_2 = 762; +// const int SPELL_DIVINE_WRATH_2 = 622; SPELLABILITY_DC_DIVINE_WRATH in nwscript.nss +//const int SPELL_CLERIC_WAR_DOMAIN_POWER_2 = 380; not used, also in nwscript.nss +const int SPELL_EPIC_DEADEYE_2 = 4013; + +const int DEBUG_BATTLE_ARDOR = FALSE; +const int DEBUG_BATTLE_CUNNING = FALSE; +const int DEBUG_BATTLE_FLANKING = FALSE; + +//::///////////////////////////////////////////////////////////////// +//:: Simple utility functions (# attacks, BAB) - might want to copy those inline +//::///////////////////////////////////////////////////////////////// + +// calculates the BAB that a pure fighter with the Hit Dice (characer level) would have +int GetFighterBAB(int iHD); + +// calculates the BAB that the character had at level 20 +// assumes that we are above level 20! +int GetLevel20BAB(int iBAB, int iHD); + +// calculates the # attacks, for normal weapons (non-monk); no maximum if called with high iBAB +int GetAttacks(int iBAB, int iHD); + +// calculates the (bioware) number of attacks for an unarmed monk (maximum: 6 attacks) +// this is the Bioware implementation, which includes BAB from other classes and caps at 6 +int GetMonkAttacks(int iBAB, int iHD); + +// calculates the number of unarmed attacks for an unarmed class (usually monk) with iMonkLevel unarmed levels +// maximum: 5 attacks; iMonkLevels should include levels from all "unarmed" PrC classes, such as brawler etc. +// use GetUBABLevel to calculate the total number of "unarmed" class levels +int GetPnPMonkAttacks(int iMonkLevel); + + +//::////////////////////////////////////////////// +//:: Weapon Information Functions +//::////////////////////////////////////////////// + +// Returns DAMAGE_TYPE_* const of weapon +int GetDamageTypeByWeaponType(int iWeaponType); +int GetWeaponDamageType(object oWeap); + +// returns TRUE if weapon is ranged +// checks for bow, crossbow, sling, dart, throwing axe, shuriken, but also ammo: arrow, bullet, bolt +int GetIsRangedWeaponType(int iWeaponType); + +// returns TRUE if weapon is a throwing weapon +// checks for dart, throwing axe, shuriken +int GetIsThrowingWeaponType(int iWeaponType); + +// returns true if weapon is two-handed +// does not include double weapons +int GetIsTwoHandedMeleeWeaponType(int iWeaponType); +int GetIsTwoHandedMeleeWeapon(object oWeap); + +// returns true if it is a creature weapon, one of the following base types: +// CBLUDGWEAPON, CPIERCWEAPON, CSLASHWEAPON +int GetIsCreatureWeaponType(int iWeaponType); +int GetIsCreatureWeapon(object oWeap); + +// returns true, if unarmed (base_item_invalid), glove or creature weapon +int GetIsNaturalWeaponType(int iWeaponType); +int GetIsNaturalWeapon(object oWeap); + +// returns true if the weapon is a simple weapon +// returns 1 if simple melee weapon +// returns 2 if ranged simple weapon +int GetIsSimpleWeaponType(int iWeaponType); +int GetIsSimpleWeapon(object oWeap); + +// returns true, if unarmed (base_item_invalid) or kama +// does not check for gloves or bracers +int GetIsMonkWeaponTypeOrUnarmed(int iWeaponType); +int GetIsMonkWeaponOrUnarmed(object oWeap); + +// returns true, if not unarmed (base_item_invalid), not shield, and not torch +// implicitly assumes, that iWeaponType is the base item type of the object held in the left hand. +// This can only be a weapon, a shield, or a torch +int GetIsOffhandWeaponType(int iWeaponType); +int GetIsOffhandWeapon(object oWeapL); + +// returns true, if it is a double sided weapon +int GetIsDoubleSidedWeaponType(int iWeaponType); +int GetIsDoubleSidedWeapon(object oWeap); + +// similar to GetFeatByWeaponType(), but should be much faster, +// because it only loops once over the weapon types +// and returns all feats relevant for the weapon in a struct +struct WeaponFeat GetAllFeatsOfWeaponType(int iWeaponType); + +// Returns the low end of oWeap's critical threat range +// Accounts for Keen and Improved Critical bonuses +int GetWeaponCriticalRange(object oPC, object oWeap); + +// returns the critical multiplier of the weapons base type +int GetCriticalMultiplierOfWeaponType(int iWeaponType); + +// Returns the players critical hit damage multiplier +// takes into account weapon master's increased critical feat +int GetWeaponCritcalMultiplier(object oPC, object oWeap); + +// returns the inventory slot (constant) in which to look for the ammunition +int GetAmmunitionInventorySlotFromWeaponType(int iWeaponType); + +// Return the proper ammunition based on the weapon +object GetAmmunitionFromWeaponType(int iWeaponType, object oAttacker); +object GetAmmunitionFromWeapon(object oWeapon, object oAttacker); + + +//::////////////////////////////////////////////// +//:: Combat Information Functions +//::////////////////////////////////////////////// + +// Returns true if melee attacker within 15 feet +// motu99: This actually was (and is) 10 feet +int GetMeleeAttackers15ft(object oPC = OBJECT_SELF); + +// estimates how much the range between oAttacker and oDefender is reduced due to the size of both creatures +// will add a meter for every size integer above CREATURE_SIZE_MEDIUM +float GetSizeAdjustment(object oDefender, object oAttacker); + +// Returns true if melee attacker is in range to attack target (e.g. 10 feet) +// if bSizeAdjustment is True, it will consider the creatures sizes +int GetIsInMeleeRange(object oDefender, object oAttacker, int bSizeAdjustment = TRUE); + +// returns the sum of all class levels that give an unarmed base attack bonus (UBAB) +int GetUBABLevel(object oPC); + +// if a creature weapon is equipped in the right slot (INVENTORY_SLOT_CWEAPON_R,) +// it randomly selects one of the (up to three) creature weapons in the right/left/back +// creature weapon slots with a chance right/left/back of 5:5:2 +// if no creature weapon is equipped, returns gloves (or base_item_invalid, of no gloves) +object GetUnarmedWeapon(object oPC); + +// Returns true if player has nothing in the right hand +int GetIsUnarmed(object oPC); + +// returns true if we have something in the left creature weapon slot, or if we have monk levels +int GetIsUnarmedFighter(object oPC); + +// Returns true if player is a monk and has a monk weapon equipped (or is unarmed) +int GetHasMonkWeaponEquipped(object oPC); + +// returns true, if we have a cross bow equipped and do not posses the rapid reload feat +int GetHasCrossBowEquippedWithoutRapidReload(object oPC); + +// Returns number of "normal" attacks per round for the main hand +// returns the correct number of unarmed attacks, if oPC has monk levels and has a monk weapon equipped or is unarmed +// returns only one attack, if wielding a cross-bow without the rapid reload feat (unless bIgnoreCrossBow = TRUE) +// does not include any "bonus" attacks from Haste, combat modes (flurry of blows), class specifics (One Strike Two Cuts) etc. +// However, the number of "normal" attacks might be increased due to special (attack) boni to BAB from spells (such as Tenser's or Divine Power) +// in order to calculate the attack # with such boni, call the function with a non-zero value of iBABBonus (negative values are possible) +// the minimum # attacks returned is always 1; the maximum number returned is 5 (for armed and unarmed combat) +// if the PRC_BIOWARE_MONK_ATTACKS switch is on, the maximum number returned is 6 (only for monk attacks - unarmed or kama) +int GetMainHandAttacks(object oPC, int iBABBonus = 0, int bIgnoreCrossBow = FALSE); + +// Returns number of offhand attacks per round for off-hand +// needs number of mainhand attacks in order to ensure, that offhand attacks are always less or equal mainhand attacks +// also needs number of mainhand attacks, if the oPC has the perfect two weapon fighting (PTWF) feat +// the function will calculate the number of mainhandattacks (without bonus attacks) on its own, if passed iMainHandAttacks = 0 +// otherwise it will assume that the (non-zero) number given to iMainHandAttacks is the correct number of mainhand attacks +// if you want the number of main hand attacks to include bonus attacks, calc the Mainhandattacks yourself and add whatever bonus attacks you like +int GetOffHandAttacks(object oPC, int iMainHandAttacks = 0); + +// Returns a specific alignment property. +// Returns a line number from iprp_alignment.2da +// Used to determine the attack and damage bonuses vs. alignments +int GetItemPropAlignment(int iGoodEvil,int iLawChaos); + +//::////////////////////////////////////////////// +//:: Other utility functions +//::////////////////////////////////////////////// + +int GetAttackModVersusDefender(object oDefender, object oAttacker, object oWeapon, int iTouchAttackType = FALSE); + +int GetDiceMaxRoll(int iDamageConst); + +// all of the ten ip dice constants are supposed to lie between 6 and 15 +int GetIsDiceConstant(int iDice); + +// returns the highest critical bonus damage constant on the weapon +// note that this is the IP_DAMAGE_CONSTANT, not the damage itself +// only compares the damage constants, so does not differentiate between dice and constant damage (and assumes, that damage constants are ordered appropriately) +int GetMassiveCriticalBonusDamageConstantOfWeapon(object oWeapon); + +// Find nearest valid living enemy creature, that is not oOldDefender, within the specified range (measured in meters) of oAttacker +// if there is no valid living target within the specified range, return the first target (valid or unvalid) found out of range +// the range fDistance is measured in meters; default distance is 3.05 meters (= 10 feet = melee range) +object FindNearestNewEnemyWithinRange(object oAttacker, object oOldDefender, float fDistance = MELEE_RANGE_METERS); + +// finds the nearest valid enemy that is not oOldDefender +// (should only return living enemies, but bioware functions used in the code was bugged, so it might return a valid, but dead enemy) +object FindNearestNewEnemy(object oAttacker, object oOldDefender); + +// finds the nearest valid enemy +object FindNearestEnemy(object oAttacker); + +// clears all actions (but not the combat state) and moves to the location or oTarget +void ClearAllActionsAndMoveToObject(object oTarget, float fRange = 2.0); + +// checksall equipped items of the oPC for the Haste property +int GetHasHasteItemProperty(object oPC); + +// calculates bonus attacks and their associated penalties due to spell effects on oAttacker, such as Haste, One Strike Two Cuts, etc. +// does not include bonus attacks and penalties from combat modes, such as flurry of blows +struct BonusAttacks GetBonusAttacks(object oAttacker); + +// equips the first ammunition it finds in the inventory that works with the (ranged) weapon in the right hand +// returns the equipped new ammunition +object EquipAmmunition(object oPC); + +//Gets the total damage reduction of a creature for a given weapon +//if any new damage reduction spells/powers/etc are added, add to this function +struct DamageReducers GetTotalReduction(object oPC, object oTarget, object oWeapon); + +//::////////////////////////////////////////////// +//:: Debug Functions +//::////////////////////////////////////////////// + +// returns the name of the ACTION_* constant +string GetActionName(int iAction); + +// experimental, not yet used +int GetIsPhysicalCombatAction(int iAction); + +// used in AttackLoopLogic in order to have prc combat and aurora combat running smoothly +// checks whether the anticipated target (oDefender) selected by prc combat must be changed +// due to actions taken by the aurora engine (or the player character) +// returns the most suitable target for the next prc attack +object CheckForChangeOfTarget(object oAttacker, object oDefender, int bAllowSwitchOfTarget = FALSE); + +// returns name of the ITEM_PROPERTY_* constant +// removed as unused +// string GetItemPropertyName(int iItemType); + +// returns name of the EFFECT_TYPE_* const +// removed as unused +//string GetEffectTypeName(int iEffectType); + +// returns name of the DAMAGE_BONUS_* constant +// removed as unused +// string GetDamageBonusConstantName(int iDamageType); + +// returns name of the IP_CONST_DAMAGEBONUS_* constant +// removed as unused +// string GetIPDamageBonusConstantName(int iDamageType); +// moved to inc_debug and renamed +// string DebugStringEffect(effect eEffect); +// replaced with DebugProp2Str() +// string DebugStringItemProperty(itemproperty ip); + + +//::////////////////////////////////////////////// +//:: Attack Bonus Functions +//::////////////////////////////////////////////// + +// Returns the magical attack bonus modifier on the attacker +// checks for all the spells and determines the proper bonuses/penalties +int GetMagicalAttackBonus(object oAttacker, object oTarget); + +// Returns Weapon Attack bonus or penalty +// inlcuding race and alignment checks +int GetWeaponAttackBonusItemProperty(object oWeap, object oDefender); + +// Returns the proper AC for the defender vs. this attacker. +// takes into account being denied dex bonus to ac, etc. +int GetDefenderAC(object oDefender, object oAttacker, int bIsTouchAttack = FALSE); + +// Returns the Attack Bonus for oAttacker attacking oDefender +// iOffhand = 0 means attack is from main hand (default) +// iOffhand = 1 for an off-hand attack +int GetAttackBonus(object oDefender, object oAttacker, object oWeap, int iOffhand = 0, int iTouchAttackType = FALSE); + +// Returns 0 on miss, 1 on hit, and 2 on Critical hit +// Works for both Ranged and Melee Attacks +// iOffhand 0 = main; 1 = off-hand +// iAttackBonus 0 = calculate it; Anything else and it will use that value and will not calculate it. +// This is mainly for when you call PerformAttackRound to do multiple attacks, +// so that it does not have to recalculate all the bonuses for every attack made. +// int iMod = 0; iMod just modifies the attack roll. +// If you are coding an attack that reduces the attack roll, put the number in the iMod slot. +// bShowFeedback tells the script to show the script feedback +// fDelay is the amount of time to delay the display of feedback +int GetAttackRoll(object oDefender, object oAttacker, object oWeapon, int iOffhand = 0, int iAttackBonus = 0, int iMod = 0, int bShowFeedback = TRUE, float fDelay = 0.0, int iTouchAttackType = FALSE); + +//::////////////////////////////////////////////// +//:: Damage Bonus Functions +//::////////////////////////////////////////////// + +// returns sum of levels of all classes that have favored enemies +int GetFavoredEnemyLevel(object oAttacker); + +// returns the feat constant for the favored enemy feat versus a specific racial type +int GetFavoredEnemyFeat(int iRacialType); + +// Returns Favored Enemy Bonus Damage +// int GetFavoredEnemeyDamageBonus(object oDefender, object oAttacker); // old functions, replaced by the new one: +int GetFavoredEnemyDamageBonus(object oDefender, object oAttacker); + +// Get Mighty Weapon Bonus +int GetMightyWeaponBonus(object oWeap); + +// Returns the Enhancement Bonus of oWeapon +// Can also return - enhancements (penalties) +int GetWeaponEnhancement(object oWeapon, object oDefender, object oAttacker); + +// Used to return the Enhancement Bonus for monks +// called by GetAttackDamage to make sure monks +// get proper damage power for cutting through DR +// Note: Calls GetWeaponEnhancement as well, so when determining Damage Power +// Just use GetMonkEnhancement instead. +int GetMonkEnhancement(object oWeapon, object oDefender, object oAttacker); + +// Returns the DAMAGE_POWER_* of a weapon +// For monks send gloves instead of weapon +// function makes use of above enhancement functions +// to determine Enhancement vs. Alignment and everything +int GetDamagePowerConstant(object oWeapon, object oDefender, object oAttacker); + +// Returns Enhancement bonus on Ammunition +// oWeap = Weapon used by Attacker +int GetAmmunitionEnhancement(object oWeapon, object oDefender, object oAttacker); + +// Returns an integer amount of damage from a constant +// iDamageConst = DAMAGE_BONUS_* or IP_CONST_DAMAGEBONUS_* +int GetDamageByConstant(int iDamageConst, int iItemProp); + +// Utility function used by GetWeaponBonusDamage to store the damage constants +// Prevents same code from being written multiple times for various damage properties +struct BonusDamage GetItemPropertyDamageConstant(int iDamageType, int iDice, struct BonusDamage weapBonusDam); + +// Returns a struct filled with IP damage constants for the given weapon. +// Used to add elemental damage to combat system. +// Can also get information from Weapon Ammunition if used in place of oWeapon +struct BonusDamage GetWeaponBonusDamage(object oWeapon, object oTarget); + +// gets the Dice parameters (dice side, number of dice to roll) from a monster weapon +struct Dice GetWeaponMonsterDamage(object oWeapon); + +// Stores bonus damage from spells into the struct +struct BonusDamage GetMagicalBonusDamage(object oAttacker, object oTarget); + +// Returns damage caused by each attack that should remain constant the whole round +// Mainly things from feat, strength bonus, etc. +// iOffhand = 0 means attack is from main hand (default) +// iOffhand = 1 for an off-hand attack +int GetWeaponDamagePerRound(object oDefender, object oAttacker, object oWeap, int iOffhand = 0); + +// Returns Damage dealt by weapon +// Works for both Ranged and Melee Attacks +// Defaults typically calculate everything for you, but cacheing the data and reusing it +// can make things run faster so I left them as optional parameters. +// sWeaponBonusDamage = result of a call to GetWeaponBonusDamage +// sSpellBonusDamage = result of a call to GetMagicalBonusDamage +// iOffhand: 0 = main; 1 = off-hand +// iDamage: 0 = calculate the GetWeaponDamagePerRound; Anything else and it will use that value +// and will not calculate it. This is mainly for when you call PerformAttackRound +// to do multiple attacks, so that it does not have to recalculate all the bonuses +// for every attack made. +// bIsCritical = FALSE is not a critical; true is a critical hit. (Function checks for crit immunity). +// iNumDice: 0 = calculate it; Anything else is the number of dice rolled +// iNumSides: 0 = calculate it; Anything else is the sides of the dice rolled +// iCriticalMultiplier: 0 = calculate it; Anything else is the damage multiplier on a critical hit +effect GetAttackDamage(object oDefender, object oAttacker, object oWeapon, struct BonusDamage sWeaponBonusDamage, struct BonusDamage sSpellBonusDamage, int iOffhand = 0, int iDamage = 0, int bIsCritical = FALSE, int iNumDice = 0, int iNumSides = 0, int iCriticalMultiplier = 0); + +//::////////////////////////////////////////////// +//:: Attack Logic Functions +//::////////////////////////////////////////////// + +// utility function to apply any onhit-property (found on an item) to oTarget +// assumes that the Item Property Type of ip is ITEM_PROPERTY_ON_HIT_PROPERTIES +// cycles through all the known subtypes in order to properly perform the action +struct OnHitSpell DoOnHitProperties(itemproperty ip, object oTarget); + +// same as DoOnHitProperties, only cycles through on Monster hit subtypes +// e.g. the item property type must be ITEM_PROPERTY_ON_MONSTER_HIT +void DoOnMonsterHit(itemproperty ip, object oTarget); + +// Called by ApplyOnHitAbilities +// properly applies on hit abilties with a X% chance of firing that last Y rounds. +// in other words... on hit stun, daze, sleep, blindness, etc. +// iDurationVal - iprp_onhitdur.2da entry number +// eAbility - the proper effect this ability will apply to the target (will be applied temporarily) +// eVis - the visual effect to apply (will be applied instantly) +void ApplyOnHitDurationAbiltiies(object oTarget, int iDurationVal, effect eAbility, effect eVis); + +// applies any On Hit abilities like spells, vampiric regen, poison, vorpal, stun, etc. +// if you sent a WEAPON +// oTarget = one being hit | oItemWielder = one attacking +// if you send ARMOR +// oTarget = one attacking | oItemWielder = one being hit +void ApplyOnHitAbilities(object oTarget, object oItemWielder, object oItem); + +// Due to the lack of a proper sleep function in order to delay attacks properly +// I needed to make a separate function to control the logic of each attack. +// AttackLoopMain calls this function, which in turn uses a delay and calls AttackLoopMain. +// This allowed a proper way to delay the function. +void AttackLoopLogic(object oDefender, object oAttacker, + int iBonusAttacks, int iMainAttacks, int iOffHandAttacks, int iMod, + struct AttackLoopVars sAttackVars, struct BonusDamage sMainWeaponDamage, + struct BonusDamage sOffHandWeaponDamage, struct BonusDamage sSpellBonusDamage, + int iOffhand, int bIsCleaveAttack); + +// Function used by PerformAttackRound to control the flow of logic +// this function calls AttackLoopLogic which then calls this function again +// making them recursive until the AttackLoopMain stops calling AttackLoopLogic +void AttackLoopMain(object oDefender, object oAttacker, + int iBonusAttacks, int iMainAttacks, int iOffHandAttacks, int iMod, + struct AttackLoopVars sAttackVars, struct BonusDamage sMainWeaponDamage, + struct BonusDamage sOffHandWeaponDamage, struct BonusDamage sSpellBonusDamage); + +/** + * Performs a full attack round and can add in bonus damage damage/effects. + * Will perform all attacks and accounts for weapontype, haste, twf, tempest twf, etc. + * If the first attack hits, a local int called "PRCCombat_StruckByAttack" will be TRUE + * on the target for 1 second. + * + * @param oDefender The object being attacked. + * @param oAttacker The object doing the attacks. + * + * @param eSpecialEffect Any special Vfx or other effects the attack should use IF successful. + * @param eDuration specifies the duration of the applied effect(s) + * 0.0 = DURATION_TYPE_INSTANT, effect lasts 0.0 seconds. + * >0.0 = DURATION_TYPE_TEMPORARY, effect lasts the amount of time put in here. + * <0.0 = DURATION_TYPE_PERMAMENT!!!!! Effect lasts until dispelled. + + * @param iAttackBonusMod Is the attack modifier - Will effect all attacks if bEffectAllAttacks is on. + * @param iDamageModifier Should be either a DAMAGE_BONUS_* constant or an integer amount of damage. + * Give an integer if the attack effects ONLY the first attack! + * @param iDamageType DAMAGE_TYPE_* constant. + * + * @param bEffectAllAttacks If FALSE will only effect first attack, otherwise effects all attacks. + * + * @param sMessageSuccess Message to display on a successful hit. (i.e. "*Sneak Attack Hit*") + * @param sMessageFailure Message to display on a failure to hit. (i.e. "*Sneak Attack Miss*") + * + * @param bApplyTouchToAll Applies a touch attack to all attacks - FALSE if only first attack is a touch attack. + * @param iTouchAttackType TOUCH_ATTACK_* const - melee, ranged, spell melee, spell ranged + * + * @param bInstantAttack If TRUE, all attacks are performed at the same time, instead of over a round. + * Default: FALSE + * @param bCombatModeFlags various bit-wise flags that control the scripted combat + * see PRC_COMBATMODE_* constants for more info + */ +void PerformAttackRound(object oDefender, object oAttacker, + effect eSpecialEffect, float eDuration = 0.0, int iAttackBonusMod = 0, + int iDamageModifier = 0, int iDamageType = 0, int bEffectAllAttacks = FALSE, + string sMessageSuccess = "", string sMessageFailure = "", + int bApplyTouchToAll = FALSE, int iTouchAttackType = FALSE, + int bInstantAttack = FALSE, int bCombatModeFlags = 0); + +/** +// Performs a single attack and can add in bonus damage damage/effects +// If the first attack hits, a local int called "PRCCombat_StruckByAttack" will be TRUE +// on the target for 1 second. +// [Note that even though you only perform a single attack, there could be more than 1 attack, +// if the attacker has a feat like cleave or circle kick. The local int is NOT set on the bonus attacks! +// +// eSpecialEffect - any special Vfx or other effects the attack should use IF successful. +// eDuration - specifies the duration of the applied effect(s) +// 0.0 = DURATION_TYPE_INSTANT, effect lasts 0.0 seconds. +// >0.0 = DURATION_TYPE_TEMPORARY, effect lasts the amount of time put in here. +// <0.0 = DURATION_TYPE_PERMAMENT!!!!! Effect lasts until dispelled. +// iAttackBonusMod is the attack modifier +// iDamageModifier - is always an integer in PerformAttack() +// [Note that iDamageModifier might be a DAMAGE_BONUS_* const in PerformAttackRound] +// iDamageType = DAMAGE_TYPE_* +// sMessageSuccess - message to display on a successful hit. (i.e. "*Sneak Attack Hit*") +// sMessageFailure - message to display on a failure to hit. (i.e. "*Sneak Attack Miss*") +// iTouchAttackType - TOUCH_ATTACK_* const - melee, ranged, spell melee, spell ranged +// oRightHandOverride - item to use as if in the right hand +// oLeftHandOverride - item to use as if in the left hand +// nHandednessOverride - if TRUE, count as offhand attack +// bCombatModeFlags - various bit-wise flags that control the scripted combat +// see PRC_COMBATMODE_* constants for more info +*/ +void PerformAttack(object oDefender, object oAttacker, + effect eSpecialEffect, float eDuration = 0.0, int iAttackBonusMod = 0, + int iDamageModifier = 0, int iDamageType = 0, + string sMessageSuccess = "", string sMessageFailure = "", + int iTouchAttackType = FALSE, + object oRightHandOverride = OBJECT_INVALID, object oLeftHandOverride = OBJECT_INVALID, + int nHandednessOverride = 0, int bCombatModeFlags = 0); // motu99: changed default of nHandednessOverride to FALSE (was -1) + +//::///////////////////////////////////////// +//:: Structs +//::///////////////////////////////////////// + +// stores all Feat constants that apply to a particular weapon base type +struct WeaponFeat +{ + int Focus; + int Specialization; + int EpicFocus; + int EpicSpecialization; + int ImprovedCritical; + int OverwhelmingCritical; + int DevastatingCritical; + int WeaponOfChoice; + int SanctifyMartialStrike; + int VileMartialStrike; +}; + +struct WeaponFeat InitWeaponFeat(int value) +{ + struct WeaponFeat sFeat; + sFeat.Focus = value; + sFeat.Specialization = value; + sFeat.EpicFocus = value; + sFeat.EpicSpecialization = value; + sFeat.ImprovedCritical = value; + sFeat.OverwhelmingCritical = value; + sFeat.DevastatingCritical = value; + sFeat.WeaponOfChoice = value; + sFeat.SanctifyMartialStrike = value; + sFeat.VileMartialStrike = value; + return sFeat; +} + +struct BonusDamage +{ + // dice_* vars are for the damage bonus IP that are NdX dice elemental damage + int dice_Acid, dice_Cold, dice_Fire, dice_Elec, dice_Son; + int dice_Div, dice_Neg, dice_Pos; + int dice_Mag; + int dice_Slash, dice_Pier, dice_Blud; + + // dam_* vars are for +/- X damage bonuses + int dam_Acid, dam_Cold, dam_Fire, dam_Elec, dam_Son; + int dam_Div, dam_Neg, dam_Pos; + int dam_Mag; + int dam_Slash, dam_Pier, dam_Blud; +}; + +struct AttackLoopVars +{ + // most of these variables don't change during the attack loop logic, they are just + // recursively passed back to the function. + + // the delay of the functions recursion and the duration of the eSpecialEffect + float fDelay, eDuration; + + // does the special effect apply to all attacks? is it a ranged weapon? + int bEffectAllAttacks, bIsRangedWeapon; + + // Ammo if it is a ranged weapon, and both main and off-hand weapons + object oAmmo, oWeaponR, oWeaponL; + + // all the main hand weapon data + int iMainNumDice, iMainNumSides, iMainCritMult, iMainAttackBonus, iMainWeaponDamageRound; + + // all the off hand weapon data + int iOffHandNumDice, iOffHandNumSides, iOffHandCritMult, iOffHandAttackBonus, iOffHandWeaponDamageRound; + + // special effect applied on first attack, or all attacks + effect eSpecialEffect; + //when the new PRC effect system is in place, this will be a reference to a local effect on the module + //that exists temporarilly and will be destroyed at the end + //string sEffectLocalName; + + // the damage modifier and damage type for extra damage from special attacks + int iDamageModifier, iDamageType; + + // string displayed on a successful hit, or a miss + string sMessageSuccess, sMessageFailure; + + // ints for internal logic (they can change during the attack or attack round) + int iCleaveAttacks; // number of cleave attacks performed in the round; does not count cleaves within cleaves + int iCircleKick; // number of circle kicks per round (only one allowed) + int iAttackNumber; // number of attacks already performed in the round; does not count double or triple cleaves + int bUseMonkAttackMod; // if true, we use the monk attack modifier (-3 instead of -5 for additional attacks per round) + int bApplyTouchToAll; // duplicates the parameter of PerformAttackRound() for use in Attack logic functions + int iTouchAttackType; // duplicates the parameter of PerformAttack() and PerformAttackRound() for use in Attack logic functions + int bFiveFootStep; // determines whether we already did a five foot step in the full combat round + int bMode; // flag wit bitwise settings that determines various combat mode settings, such as offhand attack checks etc. +}; + +// used to calculate the BonusAttacks (due to spell effects such as Haste, One Strike Two Cuts or similar) +// and the attack penalty associated with the bonus attacks +struct BonusAttacks +{ + int iNumber; + int iPenalty; +}; + +// used to store sides and number of dice to be rolled +struct Dice +{ + int iNum; + int iSides; +}; + +// stores SpellID and DC of an onhit spell +struct OnHitSpell +{ + int iSpellID; + int iDC; +}; + +//internal struct to store damage reduction +struct DamReduction +{ + int nRedLevel; + int nRedAmount; +}; + +//struct of damage reduction/resistance. +//First value is for static reduction like DR 5/- +//second value is for persent immunities like 25% Slashing Immunity +struct DamageReducers +{ + int nStaticReductions; + int nPercentReductions; +}; + +// "quasi global" Vars needed to pass information between AttackLoopMain and AttackLoopLogic or other utitily functions used by these two + +/* +// motu99: these vars work similar to locals attached to an object. They have global scope, but in contrast to the so called locals they have limited lifetime. +// GLOBAL SCOPE: the variables are defined outside of any function, thus they are visible by all functions (and are initialized when main() starts up) +// They are compiled into the code of any main() function created by the system, if that main() calls a prc_inc_combat function that uses these variables (such as PerformAttack) +// LIMITED LIFETIME: These global scope vars only "live" as long as the main() function started within the context of an object (usally an event or an action assigned to a PC or NPC) has not terminated +// Whenever the main() function is called anew, all quasi-global variables are initialized to their default values +// WARNING: no changes from the last call to the main() function are remembered! +// USE: These variables are used to pass information between functions that execute within a single main(). They are much faster to access than the so called locals attached to an object +// LIMITATIONS: They do not persist over a DelayCommand() or AssignCommand() call. Whenever you call a function via DelayCommand or AssignCommand +// the system creates a new (!) main() function in order to actually execute the function. So any function called via DelayCommand or AssignCommand will have +// all of its quasi-global variables initialzed to their default values! The values of these variables at the time when the DelayCommand() or AssignCommand() was executed, +// are *not remembered* and not transferred to the function called within the DelayCommand() or AssignCommand(). +// Therefore when AttackLoopLogic() calls AttackLoopMain() in a DelayCommand(), in order to do any left over attacks in the round after a proper delay, +// all these variables are initialized to their default values. If one wants to remember the values of these variables, one has to pass them to the function directly +// (either as individual parameters, or as a collection of parameters in a struct) +*/ + +/* +// motu99: moved these "quasi globals" to the struct AttackLoopVars, so that they are remembered from one DelayCommand to the next +int iCleaveAttacks = 0; +int iCircleKick = 0; +*/ +// motu99: Still using these quasi-globals to communicate between functions *within one* single attack +int bFirstAttack; +int bUseMonkAttackMod; + +int bIsVorpalWeaponEquiped = FALSE; +int iVorpalSaveDC = 0; + +// #include "prc_x2_itemprop" +//#include "prc_inc_racial" // includes prc_feat_const, prc_class_const +// #include "prc_inc_function" +// #include "prc_inc_sneak" +#include "prc_inc_unarmed" // includes prc_spell_const, inc_utility +// #include "prc_inc_util" +//#include "inc_utility" +// #include "inc_abil_damage" +// #include "inc_epicspelldef" +#include "prc_inc_onhit" +#include "prc_misc_const" +#include "prc_inc_fork" + +//::///////////////////////////////////////////////////////////////////////////// +//:: Utility functions (BAB, # Attacks) - mostly used inline, but good to have them here to copy +//::///////////////////////////////////////////////////////////////////////////// + +//returns a struct describing the applicable damage reductions with the given weapon and target +struct DamReduction OvercomeDR(object oTarget) +{ + struct DamReduction nBestDamageReduction; + int nBestDamageResistance = 0; + int nApplicableReduction; + struct DamReduction nCurrentReduction; + nCurrentReduction.nRedLevel = DAMAGE_POWER_NORMAL; + nCurrentReduction.nRedAmount = 0; + nBestDamageReduction = nCurrentReduction; + + //loop through spell/power effects first + effect eLoop=GetFirstEffect(oTarget); + + while (GetIsEffectValid(eLoop)) + { + int nSpellID = GetEffectSpellId(eLoop); + + //Stoneskin + if( nSpellID == 172 + || nSpellID == 342 + || nSpellID == SPELL_URDINNIR_STONESKIN) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 10; + } + //GreaterStoneskin + if( nSpellID == 74) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 20; + } + //Premonition + if( nSpellID == 134) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 30; + } + //Ghostly Visage + if( nSpellID == 351 + || nSpellID == 605 + || nSpellID == 120) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_ONE; + nCurrentReduction.nRedAmount = 5; + } + //Ethereal Visage + if( nSpellID == 121) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_THREE; + nCurrentReduction.nRedAmount = 20; + } + //Shadow Shield and Shadow Evade(best case) + if( nSpellID == 160 + || nSpellID == 477 + || nSpellID == SPELL_SHADOWSHIELD) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_THREE; + nCurrentReduction.nRedAmount = 10; + } + //Iron Body + if( nSpellID == POWER_IRONBODY) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 15; + } + //Shadow Body + if( nSpellID == POWER_SHADOWBODY) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_ONE; + nCurrentReduction.nRedAmount = 10; + } + + //if it applies and prevents more damage, replace + if(nCurrentReduction.nRedLevel > nApplicableReduction + && nCurrentReduction.nRedAmount > nBestDamageReduction.nRedAmount) + nBestDamageReduction = nCurrentReduction; + + + eLoop=GetNextEffect(oTarget); + } + + //now loop through items + int nSlot; + object oItem; + int nSubType; + + for (nSlot=0; nSlot nApplicableReduction*/) + { + //if (DEBUG) DoDebug("Item Property is Damage Reduction"); + int nReduce = GetItemPropertyCostTableValue(ipLoop) * 5; + if (nReduce > nBestDamageReduction.nRedAmount) + nBestDamageReduction.nRedAmount = nReduce; + } + + //Next itemproperty on the list... + ipLoop=GetNextItemProperty(oItem); + } + }//end validity check + }//end for + //if(DEBUG) DoDebug("Best Reduction: " + IntToString(nBestDamageReduction.nRedAmount)); + + return nBestDamageReduction; +} + +// calculates the BAB that a pure fighter with the Hit Dice (characer level) would have +int GetFighterBAB(int iHD) +{ + return ((iHD > 20) ? (20 + (iHD -19)/2) : iHD); +} + +// assumes that we are above level 20! +// otherwise function call makes no sense, because we don't yet have a level 20 BAB +// and the BAB is used "as is" in order to calculate the number of main hand attacks +int GetLevel20BAB(int iBAB, int iHD) +{ + // subtract half of the character levels beyond 20 + return (iBAB - (iHD-19)/2); +} + +// calculates the # attacks, for normal weapons (non-monk) +int GetAttacks(int iBAB, int iHD) +{ + return ((iHD > 20) ? ((iBAB -(iHD-17)/2)/5 +1) : ((iBAB-1)/ 5+1)); +} + +// calculates the number of attacks for an unarmed monk +// this is the Bioware implementation, which includes BAB from other classes and caps at 6 +int GetMonkAttacks(int iBAB, int iHD) +{ + return ((iHD > 20) ? PRCMin(6,((iBAB - (iHD-17)/2)/3 +1)) : PRCMin(6,((iBAB-1) / 3 +1))); +} + +int GetPnPMonkAttacks(int iMonkLevel) +{ + if(iMonkLevel < 6) return 1; + else if (iMonkLevel < 10) return 2; + else if (iMonkLevel < 14) return 3; + else if (iMonkLevel < 18) return 4; + else return 5; +} + + +//::////////////////////////////////////////////// +//:: Weapon Information Functions +//::////////////////////////////////////////////// + + +// @TODO: include CEP stuff in the weapon information functions +// maybe we should use the 2das instead of "hardwiring" everything +// [don't know how slow cached calls to look up 2das are] +// we could also use a switch to decide whether to use "hardwired" fast calls, or "safer" calls to 2das +// x - since cache functions were already used in some spellscripts I've edited this function to use them as well + +int GetDamageTypeByWeaponType(int iWeaponType) +{ + switch(StringToInt(Get2DACache("baseitems", "WeaponType", iWeaponType))) + { + case 1: return DAMAGE_TYPE_PIERCING; + case 2: return DAMAGE_TYPE_BLUDGEONING; + case 3: return DAMAGE_TYPE_SLASHING; + case 4: return 5; //DAMAGE_TYPE_PIERCING | DAMAGE_TYPE_SLASHING; + case 5: return 3; //DAMAGE_TYPE_PIERCING | DAMAGE_TYPE_BLUDGEONING; + } + return DAMAGE_TYPE_BLUDGEONING; // Every place expects a default, and most errors appear to be on unarmed. +} + +int GetWeaponDamageType(object oWeap) +{ + return GetDamageTypeByWeaponType(GetBaseItemType(oWeap)); +} + +int DamageTypeToIPConst(int nDamageType) +{ + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: return IP_CONST_DAMAGETYPE_ACID; + case DAMAGE_TYPE_BLUDGEONING: return IP_CONST_DAMAGETYPE_BLUDGEONING; + case DAMAGE_TYPE_COLD: return IP_CONST_DAMAGETYPE_COLD; + case DAMAGE_TYPE_DIVINE: return IP_CONST_DAMAGETYPE_DIVINE; + case DAMAGE_TYPE_ELECTRICAL: return IP_CONST_DAMAGETYPE_ELECTRICAL; + case DAMAGE_TYPE_FIRE: return IP_CONST_DAMAGETYPE_FIRE; + case DAMAGE_TYPE_MAGICAL: return IP_CONST_DAMAGETYPE_MAGICAL; + case DAMAGE_TYPE_NEGATIVE: return IP_CONST_DAMAGETYPE_NEGATIVE; + case DAMAGE_TYPE_PIERCING: return IP_CONST_DAMAGETYPE_PIERCING; + case DAMAGE_TYPE_POSITIVE: return IP_CONST_DAMAGETYPE_POSITIVE; + case DAMAGE_TYPE_SLASHING: return IP_CONST_DAMAGETYPE_SLASHING; + case DAMAGE_TYPE_SONIC: return IP_CONST_DAMAGETYPE_SONIC; + } + return IP_CONST_DAMAGETYPE_PHYSICAL; +} + +int GetIsRangedWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_LONGBOW: return TRUE; + case BASE_ITEM_SHORTBOW: return TRUE; + case BASE_ITEM_LIGHTCROSSBOW: return TRUE; + case BASE_ITEM_HEAVYCROSSBOW: return TRUE; + case BASE_ITEM_SLING: return TRUE; + + case BASE_ITEM_THROWINGAXE: return TRUE; + case BASE_ITEM_DART: return TRUE; + case BASE_ITEM_SHURIKEN: return TRUE; + + case BASE_ITEM_ARROW: return TRUE; + case BASE_ITEM_BOLT: return TRUE; + case BASE_ITEM_BULLET: return TRUE; + } + return FALSE; +} + +int GetIsThrowingWeaponType(int iWeaponType) +{ + return iWeaponType == BASE_ITEM_THROWINGAXE + || iWeaponType == BASE_ITEM_DART + || iWeaponType == BASE_ITEM_SHURIKEN; +} + +int GetIsTwoHandedMeleeWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_GREATAXE: return TRUE; + case BASE_ITEM_GREATSWORD: return TRUE; + case BASE_ITEM_HALBERD: return TRUE; + case BASE_ITEM_SHORTSPEAR: return TRUE; + case BASE_ITEM_HEAVYFLAIL: return TRUE; + case BASE_ITEM_SCYTHE: return TRUE; + case BASE_ITEM_QUARTERSTAFF: return TRUE; + case BASE_ITEM_ELVEN_COURTBLADE: return TRUE; + case BASE_ITEM_MAUL: return TRUE; + case BASE_ITEM_FALCHION: return TRUE; + } + return FALSE; +} + +int GetIsTwoHandedMeleeWeapon(object oWeap) +{ + return GetIsTwoHandedMeleeWeaponType(GetBaseItemType(oWeap)); +} + +int GetIsCreatureWeaponType(int iWeaponType) +{ +// any of the three creature weapon types that produce bludgeoning, piercing or slashing damage + return ( iWeaponType == BASE_ITEM_CBLUDGWEAPON + || iWeaponType == BASE_ITEM_CPIERCWEAPON + || iWeaponType == BASE_ITEM_CSLASHWEAPON + || iWeaponType == BASE_ITEM_CSLSHPRCWEAP + ); +} + +int GetIsCreatureWeapon(object oWeap) +{ + return GetIsCreatureWeaponType(GetBaseItemType(oWeap)); +} + +int GetIsNaturalWeaponType(int iWeaponType) +{ +// a natural weapon is either a creature weapon, or an unarmed weapon (BASE_ITEM_INVALID) or a glove (for unarmed monks) + + return ( iWeaponType == BASE_ITEM_INVALID + || iWeaponType == BASE_ITEM_GLOVES + || GetIsCreatureWeaponType(iWeaponType) + ); +} + +int GetIsNaturalWeapon(object oWeap) +{ + return GetIsNaturalWeaponType(GetBaseItemType(oWeap)); +} + +int GetIsSimpleWeaponType(int iWeaponType) +{ + switch (iWeaponType) + { + case BASE_ITEM_MORNINGSTAR: return 1; + case BASE_ITEM_QUARTERSTAFF: return 1; + case BASE_ITEM_SHORTSPEAR: return 1; + case BASE_ITEM_HEAVYCROSSBOW: return 1; + case BASE_ITEM_INVALID: return 1; + case BASE_ITEM_CBLUDGWEAPON: return 1; + case BASE_ITEM_CPIERCWEAPON: return 1; + case BASE_ITEM_CSLASHWEAPON: return 1; + case BASE_ITEM_CSLSHPRCWEAP: return 1; + case BASE_ITEM_GLOVES: return 1; + case BASE_ITEM_BRACER: return 1; + + case BASE_ITEM_CLUB: return 2; + case BASE_ITEM_DAGGER: return 2; + case BASE_ITEM_LIGHTMACE: return 2; + case BASE_ITEM_SICKLE: return 2; + case BASE_ITEM_SLING: return 2; + case BASE_ITEM_DART: return 2; + case BASE_ITEM_LIGHTCROSSBOW: return 2; + } + + return 0; +} + +int GetIsSimpleWeapon(object oWeap) +{ + return GetIsSimpleWeaponType(GetBaseItemType(oWeap)); +} + +int GetIsMonkWeaponTypeOrUnarmed(int iWeaponType) +// returns TRUE if nothing or a kama in the right hand +{ + return ( iWeaponType == BASE_ITEM_INVALID + || iWeaponType == BASE_ITEM_KAMA + || iWeaponType == BASE_ITEM_SAI + || iWeaponType == BASE_ITEM_TORCH + || iWeaponType == BASE_ITEM_SHURIKEN + || iWeaponType == BASE_ITEM_NUNCHAKU + ); +} + +int GetIsMonkWeaponOrUnarmed(object oWeap) +// returns TRUE if nothing or a kama in the right hand +{ + return GetIsMonkWeaponTypeOrUnarmed(GetBaseItemType(oWeap)); +} + +// this function will return true on *any item* that is not invalid, shield or torch +int GetIsOffhandWeaponType(int iWeaponType) +// implicitly assumes, that iWeaponType is the base item type of the object held in the left hand. +// This can only be a weapon, a shield, or a torch (or nothing) +// returns TRUE; unless there is nothing in the left hand or a shield or a torch +// also works if we pass a double-sided weapon to this function +// note that we do not check for ranged weapons or two-handers or whether the base item is a weapon at all. +{ + return ( iWeaponType != BASE_ITEM_INVALID + && iWeaponType != BASE_ITEM_LARGESHIELD + && iWeaponType != BASE_ITEM_SMALLSHIELD + && iWeaponType != BASE_ITEM_TOWERSHIELD + && iWeaponType != BASE_ITEM_TORCH + ); +} + +// returns TRUE; unless the oWeapL is an invalid object, a shield or a torch +int GetIsOffhandWeapon(object oWeapL) +// implicitly assumes, that oWeapL is the object held in the left hand. This can only be a weapon, a shield, or a torch +{ + return GetIsOffhandWeaponType(GetBaseItemType(oWeapL)); +} + +// returns TRUE for a double sided weapon type +int GetIsDoubleSidedWeaponType(int iWeaponType) +{ + return ( iWeaponType == BASE_ITEM_DIREMACE + || iWeaponType == BASE_ITEM_DOUBLEAXE + || iWeaponType == BASE_ITEM_TWOBLADEDSWORD + ); +} + +// returns TRUE for a double sided weapon +int GetIsDoubleSidedWeapon(object oWeap) +{ + return GetIsDoubleSidedWeaponType(GetBaseItemType(oWeap)); +} + +// similar to GetFeatByWeaponType(), but should be much faster, because it only loops once over the weapon types and returns all feats relevant for the weapon in a struct +struct WeaponFeat GetAllFeatsOfWeaponType(int iWeaponType) +{ + struct WeaponFeat sFeat = InitWeaponFeat(-1); + + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: { + sFeat.Focus = FEAT_WEAPON_FOCUS_UNARMED_STRIKE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_UNARMED; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_UNARMED; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_UNARMED_STRIKE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_UNARMED; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_UNARMED; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = -1; + sFeat.VileMartialStrike = -1; + break; + } + case BASE_ITEM_BASTARDSWORD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_BASTARD_SWORD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_BASTARDSWORD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_BASTARDSWORD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_BASTARD_SWORD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_BASTARDSWORD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_BASTARDSWORD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_BASTARDSWORD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_BASTARDSWORD; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_BASTARDSWORD; + break; + } + case BASE_ITEM_BATTLEAXE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_BATTLE_AXE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_BATTLEAXE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_BATTLEAXE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_BATTLE_AXE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_BATTLEAXE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_BATTLEAXE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_BATTLEAXE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_BATTLEAXE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_BATTLEAXE; + break; + } + case BASE_ITEM_CLUB: { + sFeat.Focus = FEAT_WEAPON_FOCUS_CLUB; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_CLUB; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_CLUB; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_CLUB; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_CLUB; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_CLUB; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_CLUB; + break; + } + case BASE_ITEM_DAGGER: { + sFeat.Focus = FEAT_WEAPON_FOCUS_DAGGER; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DAGGER; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_DAGGER; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_DAGGER; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_DAGGER; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_DAGGER; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_DAGGER; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_DAGGER; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_DAGGER; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_DAGGER; + break; + } + case BASE_ITEM_DART: { + sFeat.Focus = FEAT_WEAPON_FOCUS_DART; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DART; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_DART; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_DART; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_DART; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_DART; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_DART; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_DART; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_DART; + break; + } + case BASE_ITEM_DIREMACE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_DIRE_MACE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DIRE_MACE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_DIREMACE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_DIREMACE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_DIRE_MACE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_DIREMACE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_DIREMACE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_DIREMACE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_DIREMACE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_DIREMACE; + break; + } + case BASE_ITEM_DOUBLEAXE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_DOUBLE_AXE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_DOUBLEAXE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_DOUBLEAXE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_DOUBLE_AXE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_DOUBLEAXE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_DOUBLEAXE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_DOUBLEAXE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_DOUBLEAXE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_DOUBLEAXE; + break; + } + case BASE_ITEM_DWARVENWARAXE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_DWAXE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DWAXE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_DWAXE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_DWAXE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_DWAXE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_DWAXE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_DWAXE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_DWAXE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_DWAXE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_DWAXE; + break; + } + case BASE_ITEM_GREATAXE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_GREAT_AXE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_GREAT_AXE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_GREATAXE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_GREATAXE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_GREAT_AXE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_GREATAXE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_GREATAXE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_GREATAXE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_GREATAXE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_GREATAXE; + break; + } + case BASE_ITEM_GREATSWORD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_GREAT_SWORD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_GREATSWORD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_GREATSWORD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_GREAT_SWORD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_GREATSWORD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_GREATSWORD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_GREATSWORD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_GREATSWORD; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_GREATSWORD; + break; + } + case BASE_ITEM_HALBERD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_HALBERD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_HALBERD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_HALBERD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_HALBERD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_HALBERD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_HALBERD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_HALBERD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_HALBERD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_HALBERD; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_HALBERD; + break; + } + case BASE_ITEM_HANDAXE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_HAND_AXE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_HAND_AXE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_HANDAXE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_HANDAXE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_HAND_AXE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_HANDAXE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_HANDAXE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_HANDAXE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_HANDAXE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_HANDAXE; + break; + } + case BASE_ITEM_HEAVYCROSSBOW: { + sFeat.Focus = FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_HEAVYCROSSBOW; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVYCROSSBOW; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYCROSSBOW; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYCROSSBOW; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_HEAVYCROSSBOW; + break; + } + case BASE_ITEM_HEAVYFLAIL: { + sFeat.Focus = FEAT_WEAPON_FOCUS_HEAVY_FLAIL; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_HEAVYFLAIL; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVYFLAIL; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYFLAIL; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYFLAIL; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_HEAVYFLAIL; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_HEAVYFLAIL; + break; + } + case BASE_ITEM_KAMA: { + sFeat.Focus = FEAT_WEAPON_FOCUS_KAMA; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_KAMA; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_KAMA; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_KAMA; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_KAMA; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_KAMA; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_KAMA; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_KAMA; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_KAMA; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_KAMA; + break; + } + case BASE_ITEM_KATANA: { + sFeat.Focus = FEAT_WEAPON_FOCUS_KATANA; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_KATANA; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_KATANA; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_KATANA; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_KATANA; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_KATANA; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_KATANA; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_KATANA; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_KATANA; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_KATANA; + break; + } + case BASE_ITEM_KUKRI: { + sFeat.Focus = FEAT_WEAPON_FOCUS_KUKRI; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_KUKRI; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_KUKRI; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_KUKRI; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_KUKRI; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_KUKRI; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_KUKRI; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_KUKRI; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_KUKRI; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_KUKRI; + break; + } + case BASE_ITEM_LIGHTCROSSBOW: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LIGHTCROSSBOW; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTCROSSBOW; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTCROSSBOW; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTCROSSBOW; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LIGHTCROSSBOW; + break; + } + case BASE_ITEM_LIGHTFLAIL: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_FLAIL; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LIGHTFLAIL; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTFLAIL; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTFLAIL; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTFLAIL; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_LIGHTFLAIL; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LIGHTFLAIL; + break; + } + case BASE_ITEM_LIGHTHAMMER: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_HAMMER; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LIGHTHAMMER; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTHAMMER; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTHAMMER; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTHAMMER; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_LIGHTHAMMER; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LIGHTHAMMER; + break; + } + case BASE_ITEM_LIGHTMACE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_MACE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LIGHTMACE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTMACE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LIGHT_MACE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTMACE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTMACE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_LIGHTMACE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LIGHTMACE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LIGHTMACE; + break; + } + case BASE_ITEM_LONGBOW: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LONGBOW; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LONGBOW; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LONGBOW; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LONGBOW; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LONGBOW; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LONGBOW; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LONGBOW; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LONGBOW; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LONGBOW; + break; + } + case BASE_ITEM_LONGSWORD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LONG_SWORD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LONG_SWORD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LONGSWORD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LONGSWORD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LONG_SWORD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LONGSWORD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LONGSWORD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_LONGSWORD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LONGSWORD; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LONGSWORD; + break; + } + case BASE_ITEM_MORNINGSTAR: { + sFeat.Focus = FEAT_WEAPON_FOCUS_MORNING_STAR; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_MORNING_STAR; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_MORNINGSTAR; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_MORNINGSTAR; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_MORNING_STAR; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_MORNINGSTAR; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_MORNINGSTAR; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_MORNINGSTAR; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_MORNINGSTAR; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_MORNINGSTAR; + break; + } + case BASE_ITEM_QUARTERSTAFF: { + sFeat.Focus = FEAT_WEAPON_FOCUS_STAFF; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_STAFF; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_QUARTERSTAFF; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_QUARTERSTAFF; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_STAFF; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_QUARTERSTAFF; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_QUARTERSTAFF; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_QUARTERSTAFF; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_QUARTERSTAFF; + break; + } + case BASE_ITEM_RAPIER: { + sFeat.Focus = FEAT_WEAPON_FOCUS_RAPIER; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_RAPIER; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_RAPIER; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_RAPIER; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_RAPIER; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_RAPIER; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_RAPIER; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_RAPIER; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_RAPIER; + break; + } + case BASE_ITEM_SCIMITAR: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SCIMITAR; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SCIMITAR; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SCIMITAR; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SCIMITAR; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SCIMITAR; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SCIMITAR; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SCIMITAR; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_SCIMITAR; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SCIMITAR; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SCIMITAR; + break; + } + case BASE_ITEM_SCYTHE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SCYTHE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SCYTHE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SCYTHE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SCYTHE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SCYTHE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SCYTHE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SCYTHE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_SCYTHE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SCYTHE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SCYTHE; + break; + } + case BASE_ITEM_SHORTBOW: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SHORTBOW; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SHORTBOW; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SHORTBOW; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTBOW; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SHORTBOW; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTBOW; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SHORTBOW; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SHORTBOW; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SHORTBOW; + break; + } + case BASE_ITEM_SHORTSPEAR: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SPEAR; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SPEAR; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SHORTSPEAR; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSPEAR; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SPEAR; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSPEAR; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSPEAR; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_SHORTSPEAR; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SHORTSPEAR; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SHORTSPEAR; + break; + } + case BASE_ITEM_SHORTSWORD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SHORT_SWORD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SHORTSWORD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSWORD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SHORT_SWORD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSWORD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSWORD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_SHORTSWORD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SHORTSWORD; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SHORTSWORD; + break; + } + case BASE_ITEM_SHURIKEN: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SHURIKEN; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SHURIKEN; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SHURIKEN; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SHURIKEN; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SHURIKEN; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SHURIKEN; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SHURIKEN; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SHURIKEN; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SHURIKEN; + break; + } + case BASE_ITEM_SICKLE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SICKLE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SICKLE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SICKLE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SICKLE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SICKLE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SICKLE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SICKLE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_SICKLE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SICKLE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SICKLE; + break; + } + case BASE_ITEM_SLING: { + sFeat.Focus = FEAT_WEAPON_FOCUS_SLING; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_SLING; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_SLING; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_SLING; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_SLING; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_SLING; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_SLING; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_SLING; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_SLING; + break; + } + case BASE_ITEM_THROWINGAXE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_THROWING_AXE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_THROWING_AXE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_THROWINGAXE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_THROWINGAXE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_THROWING_AXE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_THROWINGAXE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_THROWINGAXE; + sFeat.WeaponOfChoice = -1; + sFeat.SanctifyMartialStrike = -1; + sFeat.VileMartialStrike = -1; + break; + } + case BASE_ITEM_TRIDENT: { + sFeat.Focus = FEAT_WEAPON_FOCUS_TRIDENT; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_TRIDENT; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_TRIDENT; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_TRIDENT; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_TRIDENT; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_TRIDENT; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_TRIDENT; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_TRIDENT; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_TRIDENT; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_TRIDENT; + break; + } + case BASE_ITEM_TWOBLADEDSWORD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_TWOBLADEDSWORD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_TWOBLADEDSWORD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_TWOBLADEDSWORD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_TWOBLADEDSWORD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_TWOBLADEDSWORD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_TWOBLADED; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_TWOBLADED; + break; + } + case BASE_ITEM_WARHAMMER: { + sFeat.Focus = FEAT_WEAPON_FOCUS_WAR_HAMMER; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_WARHAMMER; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_WARHAMMER; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_WAR_HAMMER; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_WARHAMMER; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_WARHAMMER; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_WARHAMMER; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_WARHAMMER; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_WARHAMMER; + break; + } + case BASE_ITEM_WHIP: { + sFeat.Focus = FEAT_WEAPON_FOCUS_WHIP; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_WHIP; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_WHIP; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_WHIP; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_WHIP; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_WHIP; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_WHIP; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_WHIP; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_WHIP; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_WHIP; + break; + } + case BASE_ITEM_DOUBLE_SCIMITAR: { + sFeat.Focus = FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_DBL_SCIMITAR; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_DBL_SCIMITAR; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_DBL_SCIMITAR; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_DBL_SCIMITAR; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_DBL_SCIMITAR; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_DBL_SCIMITAR; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_DBL_SCIMITAR; + break; + } + case BASE_ITEM_EAGLE_CLAW: { + sFeat.Focus = FEAT_WEAPON_FOCUS_EAGLE_CLAW; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_EAGLE_CLAW; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_EAGLE_CLAW; + break; + } + case BASE_ITEM_ELVEN_COURTBLADE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_COURTBLADE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_ELVEN_COURTBLADE; + break; + } + case BASE_ITEM_ELVEN_THINBLADE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_ELVEN_THINBLADE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_THINBLADE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_THINBLADE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_ELVEN_THINBLADE; + break; + } + case BASE_ITEM_ELVEN_LIGHTBLADE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_ELVEN_LIGHTBLADE; + break; + } + case BASE_ITEM_FALCHION: { + sFeat.Focus = FEAT_WEAPON_FOCUS_FALCHION; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_FALCHION; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_FALCHION; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_FALCHION; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_FALCHION; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_FALCHION; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_FALCHION; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_FALCHION; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_FALCHION; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_FALCHION; + break; + } + case BASE_ITEM_GOAD: { + sFeat.Focus = FEAT_WEAPON_FOCUS_GOAD; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_GOAD; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_GOAD; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_GOAD; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_GOAD; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_GOAD; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_GOAD; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_GOAD; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_GOAD; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_GOAD; + break; + } + case BASE_ITEM_HEAVY_MACE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_HEAVY_MACE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_HEAVY_MACE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_MACE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_HEAVY_MACE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_MACE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_MACE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_HEAVY_MACE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_HEAVY_MACE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_HEAVY_MACE; + break; + } + case BASE_ITEM_HEAVY_PICK: { + sFeat.Focus = FEAT_WEAPON_FOCUS_HEAVY_PICK; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_HEAVY_PICK; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_PICK; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_HEAVY_PICK; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_PICK; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_PICK; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_HEAVY_PICK; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_HEAVY_PICK; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_HEAVY_PICK; + break; + } + case BASE_ITEM_KATAR: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_LANCE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_KATAR; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_KATAR; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_KATAR; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_KATAR; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_KATAR; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_KATAR; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_KATAR; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_KATAR; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_KATAR; + break; + } + case BASE_ITEM_LIGHT_LANCE: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_LANCE; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LIGHT_LANCE; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LIGHT_LANCE; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_LANCE; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LIGHT_LANCE; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_LANCE; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_LANCE; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_LIGHT_LANCE; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LIGHT_LANCE; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LIGHT_LANCE; + break; + } + case BASE_ITEM_LIGHT_PICK: { + sFeat.Focus = FEAT_WEAPON_FOCUS_LIGHT_PICK; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_LIGHT_PICK; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_PICK; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_LIGHT_PICK; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_PICK; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_PICK; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_LIGHT_PICK; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_LIGHT_PICK; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_LIGHT_PICK; + break; + } + case BASE_ITEM_MAUL: { + sFeat.Focus = FEAT_WEAPON_FOCUS_MAUL; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_MAUL; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_MAUL; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_MAUL; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_MAUL; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_MAUL; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_MAUL; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_MAUL; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_MAUL; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_MAUL; + break; + } + // If the feat is blank when we get here, do a lookup series + if (!sFeat.Focus) + { + sFeat.Focus = StringToInt(Get2DACache("baseitems", "WeaponFocusFeat", iWeaponType)); + sFeat.Specialization = StringToInt(Get2DACache("baseitems", "WeaponSpecializationFeat", iWeaponType));; + sFeat.EpicFocus = StringToInt(Get2DACache("baseitems", "EpicWeaponFocusFeat", iWeaponType));; + sFeat.EpicSpecialization = StringToInt(Get2DACache("baseitems", "EpicWeaponSpecializationFeat", iWeaponType));; + sFeat.ImprovedCritical = StringToInt(Get2DACache("baseitems", "WeaponImprovedCriticalFeat", iWeaponType));; + sFeat.OverwhelmingCritical = StringToInt(Get2DACache("baseitems", "EpicWeaponOverwhelmingCriticalFeat", iWeaponType));; + sFeat.DevastatingCritical = StringToInt(Get2DACache("baseitems", "EpicWeaponDevastatingCriticalFeat", iWeaponType));; + sFeat.WeaponOfChoice = StringToInt(Get2DACache("baseitems", "WeaponOfChoiceFeat", iWeaponType));; + sFeat.SanctifyMartialStrike = StringToInt(Get2DACache("baseitems", "SanctifyMartialStrike", iWeaponType));; + sFeat.VileMartialStrike = StringToInt(Get2DACache("baseitems", "VileMartialStrike", iWeaponType));; + break; + } + } + return sFeat; +} + +// returns the proper ammunition inventory slot based on iWeaponType +// if not longbow, crossbow or sling, returns right hand slot +// right hand slot is the correct slot for darts, shuriken and throwing axes +// this does not check whether the weapon type actually is a ranged type +int GetAmmunitionInventorySlotFromWeaponType(int iWeaponType) +{ + switch (iWeaponType) + { + case BASE_ITEM_LONGBOW: return INVENTORY_SLOT_ARROWS; + case BASE_ITEM_SHORTBOW: return INVENTORY_SLOT_ARROWS; + case BASE_ITEM_HEAVYCROSSBOW: return INVENTORY_SLOT_BOLTS; + case BASE_ITEM_LIGHTCROSSBOW: return INVENTORY_SLOT_BOLTS; + case BASE_ITEM_SLING: return INVENTORY_SLOT_BULLETS; +// case BASE_ITEM_DART: return INVENTORY_SLOT_RIGHTHAND; +// case BASE_ITEM_SHURIKEN: return INVENTORY_SLOT_RIGHTHAND; +// case BASE_ITEM_THROWINGAXE: return INVENTORY_SLOT_RIGHTHAND; + } + return INVENTORY_SLOT_RIGHTHAND; +} + +// for a ranged weapon type it will return the ammunition (if there is any) +// for throwing weapons the ammunition is the weapon itself, so it returns whatever oAttacker holds in his right hand +// Note that this does not check whether iWeaponType is weapon. For all non-ranged weapons (and any other types) +// it will return whatever object oAttacker holds in his right hand +object GetAmmunitionFromWeaponType(int iWeaponType, object oAttacker) +{ + return GetItemInSlot(GetAmmunitionInventorySlotFromWeaponType(iWeaponType), oAttacker); +} + +// for a ranged weapon it will return the ammunition (if there is any) +// for throwing weapons the ammunition is the weapon itself, so it returns whatever oAttacker holds in his right hand +// Note that this does not check whether oWeapon is a weapon. For all non-ranged weapons (or any other object) +// it will return whatever object oAttacker holds in his right hand +object GetAmmunitionFromWeapon(object oWeapon, object oAttacker) +{ + return GetAmmunitionFromWeaponType(GetBaseItemType(oWeapon), oAttacker); +} + +int GetWeaponCriticalRange(object oPC, object oWeap) +// for a ranged weapon we should call this *after* we have ammo equipped, because it checks the ammo for keen +// if we (re)equip the ammo later, we don't get the right keen property +{ + //no weapon, touch attacks mainly + if(!GetIsObjectValid(oWeap)) + return 20; + + int iWeaponType = GetBaseItemType(oWeap); + + // threat range of base weapon + int nThreat = StringToInt(Get2DACache("baseitems", "CritThreat", iWeaponType)); + + // find out all feat constant for this particular weapon type + struct WeaponFeat sWeaponFeat = GetAllFeatsOfWeaponType(iWeaponType); + + int bImpCrit = GetHasFeat(sWeaponFeat.ImprovedCritical, oPC); + int bKeen; + + if (GetIsRangedWeaponType(iWeaponType)) // ranged weapon, ? + { // then replace oWeap with the ammution + oWeap = GetAmmunitionFromWeaponType(iWeaponType, oPC); + } + + // check weapon (or ammo) for keen property + bKeen = GetItemHasItemProperty(oWeap, ITEM_PROPERTY_KEEN); + + // motu99: original calculation was not correct, would multiply threat range with factor of 2 for every feat + // or do PnP rules do it differently than nwn? + // also weapon master gets Ki Critical Feat at level 7, which increases the threat range by 2 + int nThreatMultiplier = 1; + if(bKeen) nThreatMultiplier++; + if(bImpCrit) nThreatMultiplier++; + + nThreat *= nThreatMultiplier; + + // motu99: instead of checking the weapon master level, we might want to check for the KiCriticalFeat + // because the PC could have been granted this feat by other means (unlikely, because we still need a WeaponOfChoice for the feat to work) + int iWeaponMasterLevel = GetLevelByClass(CLASS_TYPE_WEAPON_MASTER, oPC); + if( iWeaponMasterLevel >= WEAPON_MASTER_LEVEL_KI_CRITICAL + && GetHasFeat(sWeaponFeat.WeaponOfChoice, oPC)) + nThreat += 2; + + return (21 - nThreat); +} + +// calculates the critical multiplier of the base weapon type +int GetCriticalMultiplierOfWeaponType(int iWeaponType) +{ + // no weapon, touch attacks mainly + if (iWeaponType == BASE_ITEM_INVALID || GetIsCreatureWeaponType(iWeaponType)) + return 2; + else + return StringToInt(Get2DACache("baseitems", "CritHitMult", iWeaponType)); +} + +// includes weapon master enhancements +int GetWeaponCritcalMultiplier(object oPC, object oWeap) +{ + int iWeaponType = GetBaseItemType(oWeap); + int iCriticalMultiplier = GetCriticalMultiplierOfWeaponType(iWeaponType); + + // check for weapon master Increased Multiplier feat, gained at level 7 + // motu99: actually, we do not check for the feat, but rather for the weapon master level + //(this is faster, but problematic if other classes than WM can have this feat) + int iWeaponMasterLevel = GetLevelByClass(CLASS_TYPE_WEAPON_MASTER, oPC); + if (iWeaponMasterLevel >= WEAPON_MASTER_LEVEL_INCREASED_MULTIPLIER + && GetHasFeat(GetWeaponOfChoiceFeatOfWeaponType(iWeaponType), oPC)) + iCriticalMultiplier += 1; + + return iCriticalMultiplier; +} + + +//::////////////////////////////////////////////// +//:: Combat Information Functions +//::////////////////////////////////////////////// + +int GetMeleeAttackers15ft(object oPC = OBJECT_SELF) +// motu99: this is (and was) actually 10 feet +{ + //if (DEBUG) DoDebug("Entering GetMeleeAttackers15ft"); + object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oPC,1,CREATURE_TYPE_IS_ALIVE,TRUE); + + if (oTarget == OBJECT_INVALID) + return FALSE; + + float fDistance = GetDistanceBetween(oPC,oTarget); + if (GetLocalInt(oPC, "DWBurningBrand")) + fDistance -= RANGE_5_FEET_IN_METERS; + if (GetLocalInt(oPC, "SDGiantsStance")) + fDistance -= RANGE_5_FEET_IN_METERS; + if (GetLocalInt(oPC, "IHDancingBladeForm")) + fDistance -= RANGE_5_FEET_IN_METERS; + + //if (DEBUG) DoDebug("Exiting GetMeleeAttackers15ft"); + return fDistance <= MELEE_RANGE_METERS; +} + +int GetIsInMeleeRange(object oDefender, object oAttacker, int bSizeAdjustment = TRUE) +{ + //if (DEBUG) DoDebug("Entering GetIsInMeleeRange"); + // Throw attack + if(GetLocalInt(oAttacker, "IHLightningThrow")) return TRUE; + + float fDistance = GetDistanceBetween(oDefender, oAttacker); + if (bSizeAdjustment) + fDistance -= GetSizeAdjustment(oDefender, oAttacker); + if (GetLocalInt(oAttacker, "DWBurningBrand")) + fDistance -= RANGE_5_FEET_IN_METERS; + if (GetLocalInt(oAttacker, "SDGiantsStance")) + fDistance -= RANGE_5_FEET_IN_METERS; + if (GetLocalInt(oAttacker, "IHDancingBladeForm")) + fDistance -= RANGE_5_FEET_IN_METERS; + + //Shadowblade Far Shadow + if(GetLocalInt(oAttacker, "PRC_SB_FARSHAD")) fDistance -= FeetToMeters(10.0); + + //Umbral Disciple Kiss of the Shadows + if(GetEssentiaInvested(oAttacker, MELD_UMBRAL_KISS)) fDistance -= FeetToMeters(5.0 * GetEssentiaInvested(oAttacker, MELD_UMBRAL_KISS)); + + /*// Charging needs a little futzing sometimes. + if (GetLocalInt(oAttacker, "PCIsCharging")) fDistance -= FeetToMeters(10.0); + + // So does Tome of Battle + if (GetLocalInt(oAttacker, "PCIsInitiating")) fDistance -= FeetToMeters(10.0); */ + + // This is an adjustment because things seem to have just a little too much error otherwise + fDistance -= RANGE_5_FEET_IN_METERS; + + //if (DEBUG) DoDebug("Exiting GetIsInMeleeRange"); + return fDistance <= MELEE_RANGE_METERS; +} + +object GetUnarmedWeapon(object oPC) +{ + // get the right creature weapon + object oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + + // if they have a claw weapon of some sort + if( GetIsObjectValid(oWeapon) ) + { + // should emulate how rare a critters special attack is + // by making them random with 5:5:2 ratio + // can be tweaked though + int iRandom = d12(); + if(iRandom <= 5) + return oWeapon; + else if(iRandom <= 10) + { + object oCritterL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC); + if (GetIsObjectValid(oCritterL)) + oWeapon = oCritterL; + } + else + { + object oCritterB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oPC); + if (GetIsObjectValid(oCritterB)) + oWeapon = oCritterB; + } + } + else // we don't have a (right) critter weapon + { + object oGlove = GetItemInSlot(INVENTORY_SLOT_ARMS, oPC); + // motu99: this will also return bracers (don't know if we want this; if not, check here whether base item type is glove) + if (GetIsObjectValid(oGlove)) + oWeapon = oGlove; + else + oWeapon = OBJECT_INVALID; + } + return oWeapon; +} + +// returns TRUE if nothing in the right hand +int GetIsUnarmed(object oPC) +{ + return GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) == OBJECT_INVALID; +} + +// returns true if we have something in the left creature weapon slot, or if we have monk levels +// [unarmed PrC classes with a creature weapon apparently have their creature weapon in the left creature slot] +int GetIsUnarmedFighter(object oPC) +{ + return GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC) != OBJECT_INVALID + || GetLevelByClass(CLASS_TYPE_MONK, oPC); +} + +// motu99: includes PrC classes with an unarmed base attack bonus progression in the calculation +// any new PrCs that add to UBAB should be included in this function +int GetUBABLevel(object oPC) +{ + int iMonkLevel = 0; + iMonkLevel += GetLevelByClass(CLASS_TYPE_MONK, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_DRUNKEN_MASTER, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_INITIATE_DRACONIC, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_RED_AVENGER, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_SHOU, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_SACREDFIST, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_HENSHIN_MYSTIC, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oPC); + iMonkLevel += GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oPC); + return iMonkLevel; +} + +// only returns true, if oPC has at least one monk level and if (s)he is unarmed or has a kama equipped +int GetHasMonkWeaponEquipped(object oPC) +{ + if (GetLevelByClass(CLASS_TYPE_MONK, oPC)) + { + return GetIsMonkWeaponOrUnarmed(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)); + } + return FALSE; +} + +// we use this in GetMainHandAttacks in order to find out, whether cross-bow special +// rules override the usual # of main hand attacks (Cross-bows without the rapid reload feat +// only get one attack per round, regardless of the # of "normal" mainhand attacks) +int GetHasCrossBowEquippedWithoutRapidReload(object oPC) +{ + object oWeapR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (oWeapR != OBJECT_INVALID) + { + int iWeaponType = GetBaseItemType(oWeapR); + if( (iWeaponType == BASE_ITEM_HEAVYCROSSBOW || iWeaponType == BASE_ITEM_LIGHTCROSSBOW) + && !GetHasFeat(FEAT_RAPID_RELOAD, oPC) ) + { + return TRUE; + } + } + return FALSE; +} + +// only does a full calculation if the local int "OverrideBaseAttackCount" is zero or does not exist, +// otherwise just returns the local int "OverrideBaseAttackCount" (unless we wield a crossbow, see below) +// note that GetMainHandAttacks() does not set the local int "OverrideBaseAttackCount", you have to do this yourself +// calculation only calculates "normal" main hand attacks; e.g. it does not include increased attacks from spells (Tensers, Divine Power) +// calculation does not include bonus attacks (from Haste, combat modes etc.) +// the number of "normal" attacks might be increased due to special attack boni to BAB from spells (such as Tenser's or Divine Power) +// in order to calculate the attack # with such boni, call the function with a non-zero value of iBABBonus (negative values are possible) +// the minimum # attacks returned is always 1; the maximum number returned is 5 (for armed and unarmed combat) +// if the PRC_BIOWARE_MONK_ATTACKS switch is on, the maximum number returned is 6 (only for monk attacks) +int GetMainHandAttacks(object oPC, int iBABBonus = 0, int bIgnoreCrossBow = FALSE) +{ + // crossbows special rules + // if they wield a crossbow and don't have rapid reload, then only 1 attack per round + if (!bIgnoreCrossBow && GetHasCrossBowEquippedWithoutRapidReload(oPC)) + return 1; + + int iBAB = GetLocalInt(oPC, "OverrideBaseAttackCount"); + if(iBAB) return iBAB; + + if (DEBUG) if(iBABBonus > 20 || iBABBonus < 0) DoDebug("WARNING: GetMainHandAttacks is called with an unusual BAB-bonus = "+IntToString(iBABBonus)); + + int iCharLevel = GetHitDice(oPC); + iBAB = GetBaseAttackBonus(oPC) + iBABBonus; + + bUseMonkAttackMod = FALSE; + int iNumAttacks = GetAttacks(iBAB, iCharLevel); + if (iNumAttacks > 5) iNumAttacks = 5; + + if(GetHasMonkWeaponEquipped(oPC)) + { + int iNumMonkAttacks; + + // motu99: moved up here, because we want monk progression whenever we have a kama equipped or are unarmed, + // it is for the PC to decide, whether he will use the unarmed progression. If unarmed combat gives him less attacks + // than armed combat, its his decision. + bUseMonkAttackMod = TRUE; + + if(GetPRCSwitch(PRC_BIOWARE_MONK_ATTACKS)) // motu99: added switch to reenable bioware's (strange) monk UBAB progression + { + iNumMonkAttacks = GetMonkAttacks(iBAB, iCharLevel); + } + else + { + //note this is the correct PnP monk 3.0 progression + //not biowares progression including other classes + // add in unarmed PrC's so that they stack for number of unarmed attacks + iNumMonkAttacks = GetPnPMonkAttacks(GetUBABLevel(oPC)); + } + + // only use number of attacks from unarmed (UBAB) progression, if the number + // of attacks from UBAB is higher than number of attacks from "normal" BAB + // motu99: Why do we try to "correct" the decision of the PC here? + if(iNumMonkAttacks > iNumAttacks) + { + iNumAttacks = iNumMonkAttacks; + } + } + if (GetLocalInt(oPC, "CombatMoveAttack")) iNumAttacks -= 1; + if (iNumAttacks <= 0) iNumAttacks = 1; + return iNumAttacks; +} + +// iMainHandAttacks (second parameter) can be given to speed up the calculation of main hand attacks +// or to override any value that would otherwise be calculated by the function GetMainHandAttacks() +// the number of main hand attacks is always calculated in this function, because we need it to ensure +// that the number of offhand attacks never exceed the number of main hand attacks +// if iMainHandAttacks == 0, GetOffHandAttacks() calculates the number of main hand attacks (without taking any bonus attacks into account) +// if iMainHandAttacks != 0, GetOffHandAttacks() just assumes that this is the number if main hand attacks (without checking) +// function only returns a non-zero value, if we are using an offhand or a double sided weapon +int GetOffHandAttacks(object oPC, int iMainHandAttacks = 0) +{ + object oWeapR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + + // no offhand attacks if unarmed + if (oWeapR == OBJECT_INVALID) + return 0; + + int iWeaponTypeR = GetBaseItemType(oWeapR); + int iWeaponTypeL; + int bHasDoubleSidedWeapon = FALSE; + + int iOffHandAttacks = 0; + + // motu99: added double sided weapons + if(GetIsDoubleSidedWeaponType(iWeaponTypeR)) + { +//DoDebug("GetOffHandAttacks: found double sided weapon"); + iWeaponTypeL = iWeaponTypeR; + bHasDoubleSidedWeapon = TRUE; + } + else + iWeaponTypeL = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC)); + + if(bHasDoubleSidedWeapon || GetIsOffhandWeaponType(iWeaponTypeL)) + { + // they are wielding two weapons (or double sided weapon) so at least 1 off-hand attack + iOffHandAttacks = 1; + + if (!iMainHandAttacks) + iMainHandAttacks = GetMainHandAttacks(oPC); // these are main hand attacks without bonus attacks + + int bHasPTWF = GetHasFeat(FEAT_PERFECT_TWO_WEAPON_FIGHTING, oPC); + + if(bHasPTWF) {iOffHandAttacks = iMainHandAttacks;} + else if(GetHasFeat(FEAT_SUPREME_TWO_WEAPON_FIGHTING, oPC) ) iOffHandAttacks = 4; + else if(GetHasFeat(FEAT_GREATER_TWO_WEAPON_FIGHTING, oPC) ) iOffHandAttacks = 3; + else if(GetHasFeat(FEAT_IMPROVED_TWO_WEAPON_FIGHTING, oPC) ) iOffHandAttacks = 2; + + // ranger who wears medium or heavy armor looses improved two weapon fighting (and any higher feats) + int iRangerLevel = GetLevelByClass(CLASS_TYPE_RANGER, oPC); + if (iRangerLevel) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC); + int iArmorType = GetArmorType(oArmor); + if(iArmorType == ARMOR_TYPE_MEDIUM || iArmorType == ARMOR_TYPE_HEAVY) + iOffHandAttacks =1; +/* + else if (iOffHandAttacks <=1 && iRangerLevel >= RANGER_LEVEL_ITWF) // code only needed, if ITWF feat of ranger does not show up in GetHasFeat() + iOffHandAttacks = 2; +*/ + } + + // a tempest using double sided weapons or wearing medium or heavy armor looses GTWF and STWF feats + int iTempestLevel = GetLevelByClass(CLASS_TYPE_TEMPEST, oPC); + if(iTempestLevel) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC); + int iArmorType = GetArmorType(oArmor); + if(bHasDoubleSidedWeapon || iArmorType == ARMOR_TYPE_MEDIUM || iArmorType == ARMOR_TYPE_HEAVY) + iOffHandAttacks = 1; +/* + else if (!bHasPTWF) // this code is only needed, if the STWF, GTWF and ITWF feats do not show up in GetHasFeat() + { + if(iTempestLevel >= TEMPEST_LEVEL_STWF) + iOffHandAttacks = 4; + else if (iTempestLevel >= TEMPEST_LEVEL_GTWF && iOffHandAttacks < 3) + iOffHandAttacks = 3; + else if (iTempestLevel >= TEMPEST_LEVEL_ITWF && iOffHandAttacks < 2) + iOffHandAttacks = 2; + } +*/ + } + + +// motu99: not sure if there a rule that offhand attacks must always be less or equal main hand attacks (+Bonus attacks)? +// motu99: commented out for testing (remove comments after testing!) +/* + if (!iMainHandAttacks) + iMainHandAttacks = GetMainHandAttacks(oPC); // these are main hand attacks without bonus attacks + + if(iOffHandAttacks > iMainHandAttacks) + { + iOffHandAttacks = iMainHandAttacks; + } +*/ + // prevents dual kama monk abuse + // motu99: added switch to reenable bioware behaviour + if(iWeaponTypeL == BASE_ITEM_KAMA && !GetPRCSwitch(PRC_BIOWARE_MONK_ATTACKS)) + iOffHandAttacks = 0; + + } + if (DEBUG) DoDebug("GetOffHandAttacks: iOffHandAttacks = " + IntToString(iOffHandAttacks)); + return iOffHandAttacks; +} + +// this returns a number between 0 and 8 signalling a specific alignment +int GetItemPropAlignment(int iGoodEvil,int iLawChaos) +{ + int Align; + + switch(iGoodEvil) + { + case ALIGNMENT_GOOD: + Align = 0; + break; + case ALIGNMENT_NEUTRAL: + Align = 1; + break; + case ALIGNMENT_EVIL: + Align = 2; + break; + } + switch(iLawChaos) + { + case ALIGNMENT_LAWFUL: + Align += 0; + break; + case ALIGNMENT_NEUTRAL: + Align += 3; + break; + case ALIGNMENT_CHAOTIC: + Align += 6; + break; + } + return Align; +} + +//::////////////////////////////////////////////// +//:: Attack Bonus Functions +//::////////////////////////////////////////////// + +/** @todo + * This needs fixing. Possible fixes: + * 1) Wait until Primo gets the effects code done + * 2) Add all missing stuff here (motu99: addedmost missing things) + * 3) Make all spells that apply AB bonus or penalty update a local variable that tracks the total of such effects. + * Have dispellation monitors to decrement the variable by same when the spell ends + */ + + +// motu99: functions used for debugging +// might move these to "inc_utility" or delete +// there are better functions (using 2da lookups) in inc_utility +// +// string GetIPDamageBonusConstantName(int iDamageType) +// { + // switch(iDamageType) + // { + // case IP_CONST_DAMAGEBONUS_1: return "IP_CONST_DAMAGEBONUS_1"; + // case IP_CONST_DAMAGEBONUS_2: return "IP_CONST_DAMAGEBONUS_2"; + // case IP_CONST_DAMAGEBONUS_3: return "IP_CONST_DAMAGEBONUS_3"; + // case IP_CONST_DAMAGEBONUS_4: return "IP_CONST_DAMAGEBONUS_4"; + // case IP_CONST_DAMAGEBONUS_5: return "IP_CONST_DAMAGEBONUS_5"; + // case IP_CONST_DAMAGEBONUS_6: return "IP_CONST_DAMAGEBONUS_6"; + // case IP_CONST_DAMAGEBONUS_7: return "IP_CONST_DAMAGEBONUS_7"; + // case IP_CONST_DAMAGEBONUS_8: return "IP_CONST_DAMAGEBONUS_8"; + // case IP_CONST_DAMAGEBONUS_9: return "IP_CONST_DAMAGEBONUS_9"; + // case IP_CONST_DAMAGEBONUS_10: return "IP_CONST_DAMAGEBONUS_10"; + // case IP_CONST_DAMAGEBONUS_11: return "IP_CONST_DAMAGEBONUS_11"; + // case IP_CONST_DAMAGEBONUS_12: return "IP_CONST_DAMAGEBONUS_12"; + // case IP_CONST_DAMAGEBONUS_13: return "IP_CONST_DAMAGEBONUS_13"; + // case IP_CONST_DAMAGEBONUS_14: return "IP_CONST_DAMAGEBONUS_14"; + // case IP_CONST_DAMAGEBONUS_15: return "IP_CONST_DAMAGEBONUS_15"; + // case IP_CONST_DAMAGEBONUS_16: return "IP_CONST_DAMAGEBONUS_16"; + // case IP_CONST_DAMAGEBONUS_17: return "IP_CONST_DAMAGEBONUS_17"; + // case IP_CONST_DAMAGEBONUS_18: return "IP_CONST_DAMAGEBONUS_18"; + // case IP_CONST_DAMAGEBONUS_19: return "IP_CONST_DAMAGEBONUS_19"; + // case IP_CONST_DAMAGEBONUS_20: return "IP_CONST_DAMAGEBONUS_20"; + // case IP_CONST_DAMAGEBONUS_1d4: return "IP_CONST_DAMAGEBONUS_1d4"; + // case IP_CONST_DAMAGEBONUS_1d6: return "IP_CONST_DAMAGEBONUS_1d6"; + // case IP_CONST_DAMAGEBONUS_1d8: return "IP_CONST_DAMAGEBONUS_1d8"; + // case IP_CONST_DAMAGEBONUS_1d10: return "IP_CONST_DAMAGEBONUS_1d10"; + // case IP_CONST_DAMAGEBONUS_1d12: return "IP_CONST_DAMAGEBONUS_1d12"; + // case IP_CONST_DAMAGEBONUS_2d10: return "IP_CONST_DAMAGEBONUS_2d10"; + // case IP_CONST_DAMAGEBONUS_2d12: return "IP_CONST_DAMAGEBONUS_2d12"; + // case IP_CONST_DAMAGEBONUS_2d4: return "IP_CONST_DAMAGEBONUS_2d4"; + // case IP_CONST_DAMAGEBONUS_2d6: return "IP_CONST_DAMAGEBONUS_2d6"; + // case IP_CONST_DAMAGEBONUS_2d8: return "IP_CONST_DAMAGEBONUS_2d8"; + // } + // return "unknown"; +// } + +// string GetDamageBonusConstantName(int iDamageType) +// { + // switch(iDamageType) + // { + // case DAMAGE_BONUS_1: return "DAMAGE_BONUS_1"; + // case DAMAGE_BONUS_2: return "DAMAGE_BONUS_2"; + // case DAMAGE_BONUS_3: return "DAMAGE_BONUS_3"; + // case DAMAGE_BONUS_4: return "DAMAGE_BONUS_4"; + // case DAMAGE_BONUS_5: return "DAMAGE_BONUS_5"; + // case DAMAGE_BONUS_6: return "DAMAGE_BONUS_6"; + // case DAMAGE_BONUS_7: return "DAMAGE_BONUS_7"; + // case DAMAGE_BONUS_8: return "DAMAGE_BONUS_8"; + // case DAMAGE_BONUS_9: return "DAMAGE_BONUS_9"; + // case DAMAGE_BONUS_10: return "DAMAGE_BONUS_10"; + // case DAMAGE_BONUS_11: return "DAMAGE_BONUS_11"; + // case DAMAGE_BONUS_12: return "DAMAGE_BONUS_12"; + // case DAMAGE_BONUS_13: return "DAMAGE_BONUS_13"; + // case DAMAGE_BONUS_14: return "DAMAGE_BONUS_14"; + // case DAMAGE_BONUS_15: return "DAMAGE_BONUS_15"; + // case DAMAGE_BONUS_16: return "DAMAGE_BONUS_16"; + // case DAMAGE_BONUS_17: return "DAMAGE_BONUS_17"; + // case DAMAGE_BONUS_18: return "DAMAGE_BONUS_18"; + // case DAMAGE_BONUS_19: return "DAMAGE_BONUS_19"; + // case DAMAGE_BONUS_20: return "DAMAGE_BONUS_20"; + // case DAMAGE_BONUS_1d10: return "DAMAGE_BONUS_1d10"; + // case DAMAGE_BONUS_1d12: return "DAMAGE_BONUS_1d12"; + // case DAMAGE_BONUS_1d4: return "DAMAGE_BONUS_1d4"; + // case DAMAGE_BONUS_1d6: return "DAMAGE_BONUS_1d6"; + // case DAMAGE_BONUS_1d8: return "DAMAGE_BONUS_1d8"; + // case DAMAGE_BONUS_2d10: return "DAMAGE_BONUS_2d10"; + // case DAMAGE_BONUS_2d12: return "DAMAGE_BONUS_2d12"; + // case DAMAGE_BONUS_2d4: return "DAMAGE_BONUS_2d4"; + // case DAMAGE_BONUS_2d6: return "DAMAGE_BONUS_2d6"; + // case DAMAGE_BONUS_2d8: return "DAMAGE_BONUS_2d8"; + // } + // return "unknown"; +// } + +// string GetEffectTypeName(int iEffectType) +// { + // switch(iEffectType) + // { + // case EFFECT_TYPE_ABILITY_DECREASE: return "EFFECT_TYPE_ABILITY_DECREASE"; + // case EFFECT_TYPE_ABILITY_INCREASE: return "EFFECT_TYPE_ABILITY_INCREASE"; + // case EFFECT_TYPE_AC_DECREASE: return "EFFECT_TYPE_AC_DECREASE"; + // case EFFECT_TYPE_AC_INCREASE: return "EFFECT_TYPE_AC_INCREASE"; + // case EFFECT_TYPE_ARCANE_SPELL_FAILURE: return "EFFECT_TYPE_ARCANE_SPELL_FAILURE"; + // case EFFECT_TYPE_AREA_OF_EFFECT: return "EFFECT_TYPE_AREA_OF_EFFECT"; + // case EFFECT_TYPE_ATTACK_DECREASE: return "EFFECT_TYPE_ATTACK_DECREASE"; + // case EFFECT_TYPE_ATTACK_INCREASE: return "EFFECT_TYPE_ATTACK_INCREASE"; + // case EFFECT_TYPE_BEAM: return "EFFECT_TYPE_BEAM"; + // case EFFECT_TYPE_BLINDNESS: return "EFFECT_TYPE_BLINDNESS"; + // case EFFECT_TYPE_CHARMED: return "EFFECT_TYPE_CHARMED"; + // case EFFECT_TYPE_CONCEALMENT: return "EFFECT_TYPE_CONCEALMENT"; + // case EFFECT_TYPE_CONFUSED: return "EFFECT_TYPE_CONFUSED"; + // case EFFECT_TYPE_CURSE: return "EFFECT_TYPE_CURSE"; + // case EFFECT_TYPE_CUTSCENE_PARALYZE: return "EFFECT_TYPE_CUTSCENE_PARALYZE"; + // case EFFECT_TYPE_CUTSCENEGHOST: return "EFFECT_TYPE_CUTSCENEGHOST"; + // case EFFECT_TYPE_CUTSCENEIMMOBILIZE: return "EFFECT_TYPE_CUTSCENEIMMOBILIZE"; + // case EFFECT_TYPE_DAMAGE_DECREASE: return "EFFECT_TYPE_DAMAGE_DECREASE"; + // case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE: return "EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE"; + // case EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE: return "EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE"; + // case EFFECT_TYPE_DAMAGE_INCREASE: return "EFFECT_TYPE_DAMAGE_INCREASE"; + // case EFFECT_TYPE_DAMAGE_REDUCTION: return "EFFECT_TYPE_DAMAGE_REDUCTION"; + // case EFFECT_TYPE_DAMAGE_RESISTANCE: return "EFFECT_TYPE_DAMAGE_RESISTANCE"; + // case EFFECT_TYPE_DARKNESS: return "EFFECT_TYPE_DARKNESS"; + // case EFFECT_TYPE_DAZED: return "EFFECT_TYPE_DAZED"; + // case EFFECT_TYPE_DEAF: return "EFFECT_TYPE_DEAF"; + // case EFFECT_TYPE_DISAPPEARAPPEAR: return "EFFECT_TYPE_DISAPPEARAPPEAR"; + // case EFFECT_TYPE_DISEASE: return "EFFECT_TYPE_DISEASE"; + // case EFFECT_TYPE_DISPELMAGICALL: return "EFFECT_TYPE_DISPELMAGICALL"; + // case EFFECT_TYPE_DISPELMAGICBEST: return "EFFECT_TYPE_DISPELMAGICBEST"; + // case EFFECT_TYPE_DOMINATED: return "EFFECT_TYPE_DOMINATED"; + // case EFFECT_TYPE_ELEMENTALSHIELD: return "EFFECT_TYPE_ELEMENTALSHIELD"; + // case EFFECT_TYPE_ENEMY_ATTACK_BONUS: return "EFFECT_TYPE_ENEMY_ATTACK_BONUS"; + // case EFFECT_TYPE_ENTANGLE: return "EFFECT_TYPE_ENTANGLE"; + // case EFFECT_TYPE_ETHEREAL: return "EFFECT_TYPE_ETHEREAL"; + // case EFFECT_TYPE_FRIGHTENED: return "EFFECT_TYPE_FRIGHTENED"; + // case EFFECT_TYPE_HASTE: return "EFFECT_TYPE_HASTE"; + // case EFFECT_TYPE_IMMUNITY: return "EFFECT_TYPE_IMMUNITY"; + // case EFFECT_TYPE_IMPROVEDINVISIBILITY: return "EFFECT_TYPE_IMPROVEDINVISIBILITY"; + // case EFFECT_TYPE_INVALIDEFFECT: return "EFFECT_TYPE_INVALIDEFFECT"; + // case EFFECT_TYPE_INVISIBILITY: return "EFFECT_TYPE_INVISIBILITY"; + // case EFFECT_TYPE_INVULNERABLE: return "EFFECT_TYPE_INVULNERABLE"; + // case EFFECT_TYPE_MISS_CHANCE: return "EFFECT_TYPE_MISS_CHANCE"; + // case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE: return "EFFECT_TYPE_MOVEMENT_SPEED_DECREASE"; + // case EFFECT_TYPE_MOVEMENT_SPEED_INCREASE: return "EFFECT_TYPE_MOVEMENT_SPEED_INCREASE"; + // case EFFECT_TYPE_NEGATIVELEVEL: return "EFFECT_TYPE_NEGATIVELEVEL"; + // case EFFECT_TYPE_PARALYZE: return "EFFECT_TYPE_PARALYZE"; + // case EFFECT_TYPE_PETRIFY: return "EFFECT_TYPE_PETRIFY"; + // case EFFECT_TYPE_POISON: return "EFFECT_TYPE_POISON"; + // case EFFECT_TYPE_POLYMORPH: return "EFFECT_TYPE_POLYMORPH"; + // case EFFECT_TYPE_REGENERATE: return "EFFECT_TYPE_REGENERATE"; + // case EFFECT_TYPE_RESURRECTION: return "EFFECT_TYPE_RESURRECTION"; + // case EFFECT_TYPE_SANCTUARY: return "EFFECT_TYPE_SANCTUARY"; + // case EFFECT_TYPE_SAVING_THROW_DECREASE : return "EFFECT_TYPE_SAVING_THROW_DECREASE "; + // case EFFECT_TYPE_SAVING_THROW_INCREASE: return "EFFECT_TYPE_SAVING_THROW_INCREASE"; + // case EFFECT_TYPE_SEEINVISIBLE: return "EFFECT_TYPE_SEEINVISIBLE"; + // case EFFECT_TYPE_SILENCE: return "EFFECT_TYPE_SILENCE"; + // case EFFECT_TYPE_SKILL_DECREASE: return "EFFECT_TYPE_SKILL_DECREASE"; + // case EFFECT_TYPE_SKILL_INCREASE: return "EFFECT_TYPE_SKILL_INCREASE"; + // case EFFECT_TYPE_SLEEP: return "EFFECT_TYPE_SLEEP"; + // case EFFECT_TYPE_SLOW: return "EFFECT_TYPE_SLOW"; + // case EFFECT_TYPE_SPELL_FAILURE: return "EFFECT_TYPE_SPELL_FAILURE"; + // case EFFECT_TYPE_SPELL_IMMUNITY: return "EFFECT_TYPE_SPELL_IMMUNITY"; + // case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE: return "EFFECT_TYPE_SPELL_RESISTANCE_DECREASE"; + // case EFFECT_TYPE_SPELL_RESISTANCE_INCREASE: return "EFFECT_TYPE_SPELL_RESISTANCE_INCREASE"; + // case EFFECT_TYPE_SPELLLEVELABSORPTION: return "EFFECT_TYPE_SPELLLEVELABSORPTION"; + // case EFFECT_TYPE_STUNNED: return "EFFECT_TYPE_STUNNED"; + // case EFFECT_TYPE_SWARM: return "EFFECT_TYPE_SWARM"; + // case EFFECT_TYPE_TEMPORARY_HITPOINTS: return "EFFECT_TYPE_TEMPORARY_HITPOINTS"; + // case EFFECT_TYPE_TIMESTOP: return "EFFECT_TYPE_TIMESTOP"; + // case EFFECT_TYPE_TRUESEEING: return "EFFECT_TYPE_TRUESEEING"; + // case EFFECT_TYPE_TURN_RESISTANCE_DECREASE: return "EFFECT_TYPE_TURN_RESISTANCE_DECREASE"; + // case EFFECT_TYPE_TURN_RESISTANCE_INCREASE: return "EFFECT_TYPE_TURN_RESISTANCE_INCREASE"; + // case EFFECT_TYPE_TURNED: return "EFFECT_TYPE_TURNED"; + // case EFFECT_TYPE_ULTRAVISION: return "EFFECT_TYPE_ULTRAVISION"; + // case EFFECT_TYPE_VISUALEFFECT: return "EFFECT_TYPE_VISUALEFFECT"; + // } + // return "unknown"; +// } + +// string GetItemPropertyName(int iItemType) +// { + // switch(iItemType) + // { + // case ITEM_PROPERTY_ABILITY_BONUS: return "ITEM_PROPERTY_ABILITY_BONUS"; + // case ITEM_PROPERTY_AC_BONUS: return "ITEM_PROPERTY_AC_BONUS"; + // case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP: return "ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP"; + // case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE: return "ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE"; + // case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP: return "ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP"; + // case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT: return "ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT"; + // case ITEM_PROPERTY_ARCANE_SPELL_FAILURE: return "ITEM_PROPERTY_ARCANE_SPELL_FAILURE"; + // case ITEM_PROPERTY_ATTACK_BONUS: return "ITEM_PROPERTY_ATTACK_BONUS"; + // case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: return "ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP"; + // case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: return "ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP"; +// // case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNEMENT: return "ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNEMENT"; + // case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: return "ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT"; + // case ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION: return ""; + // case ITEM_PROPERTY_BONUS_FEAT: return "ITEM_PROPERTY_BONUS_FEAT"; + // case ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N: return "ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N"; +// // case ITEM_PROPERTY_BOOMERANG: return "ITEM_PROPERTY_BOOMERANG"; + // case ITEM_PROPERTY_CAST_SPELL: return "ITEM_PROPERTY_CAST_SPELL"; + // case ITEM_PROPERTY_DAMAGE_BONUS: return "ITEM_PROPERTY_DAMAGE_BONUS"; + // case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: return "ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP"; + // case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: return "ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP"; + // case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT: return "ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT"; + // case ITEM_PROPERTY_DAMAGE_REDUCTION: return "ITEM_PROPERTY_DAMAGE_REDUCTION"; + // case ITEM_PROPERTY_DAMAGE_RESISTANCE: return "ITEM_PROPERTY_DAMAGE_RESISTANCE"; + // case ITEM_PROPERTY_DAMAGE_VULNERABILITY: return "ITEM_PROPERTY_DAMAGE_VULNERABILITY"; +// // case ITEM_PROPERTY_DANCING: return "ITEM_PROPERTY_DANCING"; + // case ITEM_PROPERTY_DARKVISION: return "ITEM_PROPERTY_DARKVISION"; + // case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: return "ITEM_PROPERTY_DECREASED_ABILITY_SCORE"; + // case ITEM_PROPERTY_DECREASED_AC: return "ITEM_PROPERTY_DECREASED_AC"; + // case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: return "ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER"; + // case ITEM_PROPERTY_DECREASED_DAMAGE: return "ITEM_PROPERTY_DECREASED_DAMAGE"; + // case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER: return "ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER"; + // case ITEM_PROPERTY_DECREASED_SAVING_THROWS: return "ITEM_PROPERTY_DECREASED_SAVING_THROWS"; + // case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: return "ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC"; + // case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER: return "ITEM_PROPERTY_DECREASED_SKILL_MODIFIER"; +// // case ITEM_PROPERTY_DOUBLE_STACK: return "ITEM_PROPERTY_DOUBLE_STACK"; +// // case ITEM_PROPERTY_ENHANCED_CONTAINER_BONUS_SLOTS: return "ITEM_PROPERTY_ENHANCED_CONTAINER_BONUS_SLOTS"; + // case ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT: return "ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT"; + // case ITEM_PROPERTY_ENHANCEMENT_BONUS: return "ITEM_PROPERTY_ENHANCEMENT_BONUS"; + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: return "ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP"; + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: return "ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP"; + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: return "ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT"; +// // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNMENT: return "ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNMENT"; + // case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: return "ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE"; + // case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE: return "ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE"; + // case ITEM_PROPERTY_FREEDOM_OF_MOVEMENT: return "ITEM_PROPERTY_FREEDOM_OF_MOVEMENT"; + // case ITEM_PROPERTY_HASTE: return "ITEM_PROPERTY_HASTE"; + // case ITEM_PROPERTY_HEALERS_KIT: return "ITEM_PROPERTY_HEALERS_KIT"; + // case ITEM_PROPERTY_HOLY_AVENGER: return "ITEM_PROPERTY_HOLY_AVENGER"; + // case ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE: return "ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE"; + // case ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS: return "ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS"; +// // case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SCHOOL: return "ITEM_PROPERTY_IMMUNITY_SPECIFIC_SCHOOL"; + // case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL: return "ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL"; + // case ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL: return "ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL"; + // case ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL: return "ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL"; + // case ITEM_PROPERTY_IMPROVED_EVASION: return "ITEM_PROPERTY_IMPROVED_EVASION"; + // case ITEM_PROPERTY_KEEN: return "ITEM_PROPERTY_KEEN"; + // case ITEM_PROPERTY_LIGHT: return "ITEM_PROPERTY_LIGHT"; + // case ITEM_PROPERTY_MASSIVE_CRITICALS: return "ITEM_PROPERTY_MASSIVE_CRITICALS"; + // case ITEM_PROPERTY_MIGHTY: return "ITEM_PROPERTY_MIGHTY"; + // case ITEM_PROPERTY_MIND_BLANK: return "ITEM_PROPERTY_MIND_BLANK"; + // case ITEM_PROPERTY_MONSTER_DAMAGE: return "ITEM_PROPERTY_MONSTER_DAMAGE"; + // case ITEM_PROPERTY_NO_DAMAGE: return "ITEM_PROPERTY_NO_DAMAGE"; + // case ITEM_PROPERTY_ON_HIT_PROPERTIES: return "ITEM_PROPERTY_ON_HIT_PROPERTIES"; + // case ITEM_PROPERTY_ON_MONSTER_HIT: return "ITEM_PROPERTY_ON_MONSTER_HIT"; + // case ITEM_PROPERTY_ONHITCASTSPELL: return "ITEM_PROPERTY_ONHITCASTSPELL"; + // case ITEM_PROPERTY_POISON: return "ITEM_PROPERTY_POISON"; + // case ITEM_PROPERTY_REGENERATION: return "ITEM_PROPERTY_REGENERATION"; + // case ITEM_PROPERTY_REGENERATION_VAMPIRIC: return "ITEM_PROPERTY_REGENERATION_VAMPIRIC"; + // case ITEM_PROPERTY_SAVING_THROW_BONUS: return "ITEM_PROPERTY_SAVING_THROW_BONUS"; + // case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: return "ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC"; + // case ITEM_PROPERTY_SKILL_BONUS: return "ITEM_PROPERTY_SKILL_BONUS"; + // case ITEM_PROPERTY_SPECIAL_WALK: return "ITEM_PROPERTY_SPECIAL_WALK"; + // case ITEM_PROPERTY_SPELL_RESISTANCE: return "ITEM_PROPERTY_SPELL_RESISTANCE"; + // case ITEM_PROPERTY_THIEVES_TOOLS: return "ITEM_PROPERTY_THIEVES_TOOLS"; + // case ITEM_PROPERTY_TRAP: return "ITEM_PROPERTY_TRAP"; + // case ITEM_PROPERTY_TRUE_SEEING: return "ITEM_PROPERTY_TRUE_SEEING"; + // case ITEM_PROPERTY_TURN_RESISTANCE: return "ITEM_PROPERTY_TURN_RESISTANCE"; + // case ITEM_PROPERTY_UNLIMITED_AMMUNITION: return "ITEM_PROPERTY_UNLIMITED_AMMUNITION"; + // case ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP: return "ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP"; + // case ITEM_PROPERTY_USE_LIMITATION_CLASS: return "ITEM_PROPERTY_USE_LIMITATION_CLASS"; + // case ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE: return "ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE"; + // case ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT: return "ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT"; + // case ITEM_PROPERTY_USE_LIMITATION_TILESET: return "ITEM_PROPERTY_USE_LIMITATION_TILESET"; + // case ITEM_PROPERTY_VISUALEFFECT: return "ITEM_PROPERTY_VISUALEFFECT"; +// // case ITEM_PROPERTY_VORPAL: return "ITEM_PROPERTY_VORPAL"; + // case ITEM_PROPERTY_WEIGHT_INCREASE: return "ITEM_PROPERTY_WEIGHT_INCREASE"; + // case ITEM_PROPERTY_WOUNDING: return "ITEM_PROPERTY_WOUNDING"; + // } + // return "unknown"; +// } + +// returns a string with basic information about an effect found on a PC +// string DebugStringEffect(effect eEffect) +// { + // string sString = ""; +// + // int nType = GetEffectType(eEffect); + // sString += "Effect; Type = " + IntToString(nType) + " (" + GetEffectTypeName(nType) + ")"; +// + // int nSpell = GetEffectSpellId(eEffect); + // sString += ", SpellID: " + IntToString(nSpell); +// + // int nSubType = GetEffectSubType(eEffect); + // sString += ", Subtype: " + IntToString(nSubType); +// + // int nDurationType = GetEffectDurationType(eEffect); + // sString += ", Duration: " + IntToString(nDurationType); +// + // object oCreator = GetEffectCreator(eEffect); + // sString += ", Creator: " + GetName(oCreator); +// + // return sString; +// } + +// returns a string with basic information about an item property (found on an item) +// we could also use DebugIProp2Str(itemproperty ip) from "inc_utility" +// or ItemPropertyToString(itemproperty ip) from "inc_utility" (which uses 2da and tlk lookups) + +// replaced calls with DebugIProp2Str() in inc_debug +// string DebugStringItemProperty(itemproperty ip) +// { +// // return DebugIProp2Str(ip); +// // return ItemPropertyToString(ip); + // int iType = GetItemPropertyType(ip); + // int iValue = GetItemPropertyCostTableValue(ip); + // int iSubType = GetItemPropertySubType(ip); + // int iParam1 = GetItemPropertyParam1Value(ip); + // return "IP, Type = " + IntToString(iType) + " (" + GetItemPropertyName(iType) + "), Cost = " + IntToString(iValue) + ", Subtype = " + IntToString(iSubType) + ", Param1 = " + IntToString(iParam1); +// } + +// motu99: modified this code so that it now works with PRC 3.1d +int GetMagicalAttackBonus(object oAttacker, object oTarget) +{ +// motu99: added createMagicTatoo, Heroism, GreaterHeroism, MantleOfEgregiousMight, Deadeye Sense +// @TODO: research if all AB increasing spells are covered; find a way to get AB-increase directly from effect +//ebonfowl: this function now pulls attack increasing effects automatically + + int iMagicBonus = 0; + int nType = 0; + int iEffectSubtype = 0; + int nSpell = 0; + + object oCaster; + + effect eEffect = GetFirstEffect(oAttacker); + + while(GetIsEffectValid(eEffect)) + { + nType = GetEffectType(eEffect); + iEffectSubtype = GetEffectInteger(eEffect, 1); + nSpell = GetEffectSpellId(eEffect); +// DoDebug("GetMagicalAttackBonus: found "+ DebugStringEffect(eEffect)); + int iBonus = 0; + + int iRace = MyPRCGetRacialType(oTarget); + + int iEffectRace = GetEffectInteger(eEffect, 2); + + int iGoodEvil = GetAlignmentGoodEvil(oTarget); + int iLawChaos = GetAlignmentLawChaos(oTarget); + + int iEffectLawChaos = GetEffectInteger(eEffect, 3); + int iEffectGoodEvil = GetEffectInteger(eEffect, 4); + + if(nType == EFFECT_TYPE_ATTACK_INCREASE && iEffectSubtype == 0) + { +// motu99: If the aurora engine can determine the attack increase due to all of these spells +// we should also be able to get the attack increase directly +// this would reduce the effort going through all spells (and not catching any new spells, unless we meticously update the code) + + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based attack increase and load into struct + iBonus = GetEffectInteger(eEffect, 0); + iMagicBonus += iBonus; + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based attack increase and load into struct + iBonus = GetEffectInteger(eEffect, 0); + iMagicBonus += iBonus; + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic attack increase and load into struct + iBonus = GetEffectInteger(eEffect, 0); + iMagicBonus += iBonus; + } + +/* switch(nSpell) + { +// case 2732: // Tempest absolute ambidex (calculated independent from spell effect) +// iMagicBonus += 2; +// break; + + case SPELL_AID: + iMagicBonus += 1; + break; + + case SPELL_BLESS: + iMagicBonus += 1; + break; + + case SPELL_PRAYER: + iMagicBonus += 1; + break; + + case SPELL_WAR_CRY: + iMagicBonus += 2; + break; + + case SPELL_BATTLETIDE: + iMagicBonus += 2; + break; + + case SPELL_TRUE_STRIKE: + iMagicBonus += 20; + break; + + case SPELL_EPIC_DEADEYE_2: + iMagicBonus += 20; + break; + + case SPELL_DIVINE_PROTECTION: + iMagicBonus += 1; + break; + + case SPELL_CREATE_MAGIC_TATTOO: // motu99: don't know if this ever finds anything - fluffyamoeba: does now + iMagicBonus += 2; + break; + + case SPELL_HEROISM: + iMagicBonus += 2; + break; + + case SPELL_GREATER_HEROISM: + iMagicBonus += 4; + break; + + case SPELL_MANTLE_OF_EGREG_MIGHT: + iMagicBonus += 4; + break; + + case SPELL_RECITATION: + iMagicBonus += 2; + break; + + case SPELL_DIVINE_FAVOR: { + iMagicBonus++; // at least one point increase + +// motu99: normally divine favor can only be cast on self, but what with runes? +// so we should find out the caster of the spell (same as with bard song, see below) + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + iBonus = GetLevelByTypeDivine(oCaster); + iBonus /= 3; + if(iBonus > 4) iBonus = 4; // we already have one increase, so can only be four more + + iMagicBonus += iBonus; + break; + } + case SPELL_DIVINE_POWER: + iBonus = GetFighterBAB(GetHitDice(oAttacker)) - GetBaseAttackBonus(oAttacker); + iMagicBonus += iBonus; + break; + + // // Cleric War Domain Power + // case SPELL_CLERIC_WAR_DOMAIN_POWER_2: +// // here we implicitly assume, that the war domain power SLA can only be cast on oneself + // iBonus = GetLevelByTypeDivine(oAttacker); // GetLevelByClass(CLASS_TYPE_CLERIC, oAttacker); // motu99: changed to divine caster levels + // iBonus /= 5; + // iBonus++; +// + // iMagicBonus += iBonus; + // break; + + // SPELL_DIVINE_WRATH + case SPELLABILITY_DC_DIVINE_WRATH: {// motu99: didn't check this piece of code + iBonus = GetLevelByClass(CLASS_TYPE_DIVINECHAMPION, oAttacker); + iBonus /= 5; + iBonus -= 1; + if(iBonus < 0) iBonus = 0; + else iBonus *= 2; + + iBonus += 3; + iMagicBonus += iBonus; + break; + } + case SPELL_TENSERS_TRANSFORMATION: { + // find out caster level (should be stored in local int on oAttacker) + iBonus = GetLocalInt(oAttacker, "CasterLvl_TensersTrans"); + + // if there is no local int, we have to find out the caster level (is not very accurate) + if (!iBonus) + { + // Tenser's could have been cast on us by someone else (rune or scroll), so find out caster + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + iBonus = GetLevelByTypeArcane(oCaster); + } + + iBonus /= 2; + iMagicBonus += iBonus; + break; + } + // Bard's Song + case SPELL_BARD_SONG: { + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + iMagicBonus++; // at least one point increase + if(GetIsObjectValid(oCaster)) + { + if( GetLevelByClass(CLASS_TYPE_BARD, oCaster) >= BARD_LEVEL_FOR_BARD_SONG_AB_2 + && GetSkillRank(SKILL_PERFORM, oCaster) >= BARD_PERFORM_SKILL_FOR_BARD_SONG_AB_2) + iMagicBonus++; + } + break; + } + }*/ + } + + else if(nType == EFFECT_TYPE_ATTACK_DECREASE && iEffectSubtype == 0) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based attack decrease and load into struct + iBonus = GetEffectInteger(eEffect, 0); + iMagicBonus -= iBonus; + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based attack decrease and load into struct + iBonus = GetEffectInteger(eEffect, 0); + iMagicBonus -= iBonus; + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic attack decrease and load into struct + iBonus = GetEffectInteger(eEffect, 0); + iMagicBonus -= iBonus; + } + + /* + switch(nSpell) + { + case SPELL_BANE: + iMagicBonus -= 1; + break; + + case SPELL_PRAYER: + iMagicBonus -= 1; + break; + + case SPELL_FLARE: + iMagicBonus -= 1; + break; + + case SPELL_GHOUL_TOUCH: + iMagicBonus -= 2; + break; + + case SPELL_DOOM: + iMagicBonus -= 2; + break; + + case SPELL_SCARE: + iMagicBonus -= 2; + break; + + case SPELL_RECITATION: + iMagicBonus -= 2; + break; + + case SPELL_BATTLETIDE: + iMagicBonus -= 2; + break; + + case SPELL_CURSE_OF_PETTY_FAILING: + iMagicBonus -= 2; + break; + + case SPELL_LEGIONS_CURSE_OF_PETTY_FAILING: + iMagicBonus -= 2; + break; + + case SPELL_BESTOW_CURSE: + iMagicBonus -= 4; + break; + + // SPELL_HELLINFERNO + case SPELL_HELLINFERNO_2: + iMagicBonus -= 4; + break; + + case SPELL_BIGBYS_INTERPOSING_HAND: + iMagicBonus -= 10; + break; + + // Bard's Curse Song + case SPELL_BARD_CURSE_SONG: { + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + iMagicBonus--; + + if(GetIsObjectValid(oCaster)) + { + if( GetLevelByClass(CLASS_TYPE_BARD, oCaster) >= BARD_LEVEL_FOR_BARD_SONG_AB_2 + && GetSkillRank(SKILL_PERFORM, oCaster) >= BARD_PERFORM_SKILL_FOR_BARD_SONG_AB_2) + iMagicBonus--; + } + break; + } + // Power Shot + case SPELL_PA_POWERSHOT: + iMagicBonus -= 5; + break; + + case SPELL_PA_IMP_POWERSHOT: + iMagicBonus -= 10; + break; + + case SPELL_PA_SUP_POWERSHOT: + iMagicBonus -= 15; + break; + }*/ + } + + eEffect = GetNextEffect(oAttacker); + } + return iMagicBonus; +} + +// this function only calculates pure attack boni on the weapon, enhancement boni are calculated elsewhere +// it takes boni or penalties due to alignment into account +// it calculates the maximum of all boni and subtracts the maximum of all penalties +int GetWeaponAttackBonusItemProperty(object oWeap, object oDefender) +{ + int iBonus = 0; + int iPenalty = 0; + int iTemp; + + int iRace = MyPRCGetRacialType(oDefender); + + int iGoodEvil = GetAlignmentGoodEvil(oDefender); + int iLawChaos = GetAlignmentLawChaos(oDefender); + int iAlignSpecific = GetItemPropAlignment(iGoodEvil, iLawChaos); + int iAlignGroup; + + itemproperty ip = GetFirstItemProperty(oWeap); + while(GetIsItemPropertyValid(ip)) + { + iTemp = 0; + int iIpType=GetItemPropertyType(ip); + switch(iIpType) + { + case ITEM_PROPERTY_ATTACK_BONUS: + iTemp = GetItemPropertyCostTableValue(ip); + break; + + case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: + iTemp - GetItemPropertyCostTableValue(ip); + break; + + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + iAlignGroup = GetItemPropertySubType(ip); + + if (iAlignGroup == ALIGNMENT_NEUTRAL) + { + if (iAlignGroup == iLawChaos) iTemp = GetItemPropertyCostTableValue(ip); + } + else if (iAlignGroup == iGoodEvil || iAlignGroup == iLawChaos || iAlignGroup == IP_CONST_ALIGNMENTGROUP_ALL) + { + iTemp = GetItemPropertyCostTableValue(ip); + } + break; + + case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: + if(GetItemPropertySubType(ip) == iRace ) + { + iTemp = GetItemPropertyCostTableValue(ip); + } + break; + + case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: + if(GetItemPropertySubType(ip) == iAlignSpecific ) + { + iTemp = GetItemPropertyCostTableValue(ip); + } + break; + } + + if (iTemp > iBonus) + iBonus = iTemp; + else if(iTemp < iPenalty) + iPenalty = iTemp; + + ip = GetNextItemProperty(oWeap); + } + iBonus -= iPenalty; + return iBonus; +} + +int GetDefenderAC(object oDefender, object oAttacker, int bIsTouchAttack = FALSE) +{ + int iAC = GetAC(oDefender); + int iDexMod = GetAbilityModifier(ABILITY_DEXTERITY, oDefender); + int bIsHelpless = GetIsHelpless(oDefender); + int bGetIsDeniedDexBonus = GetIsDeniedDexBonusToAC(oDefender, oAttacker); + int bIsStunned = PRCGetHasEffect(EFFECT_TYPE_STUNNED, oDefender); + + // helpless enemies have an effective dexterity of 0 (for -5 ac) + if(bIsHelpless) + { + iAC -= 5; + } + //if (DEBUG) DoDebug("GetDefenderAC: End Section #1"); + // remove the dexterity modifier to AC, based on armor limits + if(bGetIsDeniedDexBonus || bIsHelpless ) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oDefender); + int iArmorType = GetItemACBase(oArmor); + int iDexMax = 100; + + // remove any bonus AC from boots (it's Dodge AC) + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_BOOTS, oDefender) ); + + // remove bonus AC from having tumble skill. + // this is only for ranks, not items/feats/etc + int iTumble = GetSkill(oDefender, SKILL_TUMBLE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE); + iTumble -= iDexMod; + iTumble /= 5; + iAC -= iTumble; + + // change the max dex mod based on armor value + if(iArmorType == 8) iDexMax = 1; + else if(iArmorType == 7) iDexMax = 1; + else if(iArmorType == 5) iDexMax = 2; + else if(iArmorType == 4) iDexMax = 4; + else if(iArmorType == 3) iDexMax = 4; + else if(iArmorType == 2) iDexMax = 6; + else if(iArmorType == 1) iDexMax = 8; + + // if their dex mod exceeds the max for their current armor + if(iDexMod > iDexMax) iDexMod = iDexMax; + //if (DEBUG) DoDebug("GetDefenderAC: End Section #2"); + // remove any dex bonus to AC + iAC -= iDexMod; + + // remove any bonuses applied to PrC Skins + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_CARMOUR, oDefender) ); + + // if the skin AC bonus was racial "natural" AC, add it back in. + // but only if it is not a touch attack + if(!bIsTouchAttack) + { +// motu99: This calculation is quite costly; has to loop through all feats of OBJECT_SELF several times (up to 22 times) +// if performance is an issue, better make one single loop, checking for the highest of the feats and return that +//[feats seem to be item properties on the creature / PC, so could we loop through all item properties on the characters? not sure] + if ( GetHasFeat(FEAT_NATARM_19) ) iAC += 19; + else if( GetHasFeat(FEAT_NATARM_18) ) iAC += 18; + else if( GetHasFeat(FEAT_NATARM_17) ) iAC += 17; + else if( GetHasFeat(FEAT_NATARM_16) ) iAC += 16; + else if( GetHasFeat(FEAT_NATARM_15) ) iAC += 15; + else if( GetHasFeat(FEAT_NATARM_14) ) iAC += 14; + else if( GetHasFeat(FEAT_NATARM_13) ) iAC += 13; + else if( GetHasFeat(FEAT_NATARM_12) ) iAC += 12; + else if( GetHasFeat(FEAT_NATARM_11) ) iAC += 11; + else if( GetHasFeat(FEAT_NATARM_10) ) iAC += 10; + else if( GetHasFeat(FEAT_NATARM_9) ) iAC += 9; + else if( GetHasFeat(FEAT_NATARM_8) ) iAC += 8; + else if( GetHasFeat(FEAT_NATARM_7) ) iAC += 7; + else if( GetHasFeat(FEAT_NATARM_6) ) iAC += 6; + else if( GetHasFeat(FEAT_NATARM_5) ) iAC += 5; + else if( GetHasFeat(FEAT_NATARM_4) ) iAC += 4; + else if( GetHasFeat(FEAT_NATARM_3) ) iAC += 3; + else if( GetHasFeat(FEAT_NATARM_2) ) iAC += 2; + else if( GetHasFeat(FEAT_NATARM_1) ) iAC += 1; + //if (DEBUG) DoDebug("GetDefenderAC: End Section #3"); + } + } + + // if helpless or stunned, can't use shield + if(bIsHelpless || bIsStunned) + { + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oDefender) ); + } + + // AC rules are different for a touch attack + // no shield, armor, or natural armor bonuses apply. + if(bIsTouchAttack) + { + //if (DEBUG) DoDebug("GetDefenderAC: End Section #4"); + // Temporary storage, needed for Elude Touch + int nNormalAC = iAC; + + // remove Armor AC + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_CHEST, oDefender) ); + + // remove natural armor AC + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_NECK, oDefender) ); + + // remove shield bonus - only if it has not been removed already + if(!bIsHelpless && !bIsStunned) + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oDefender) ); + + // Remove AC from skin - only if it has not been removed already + if(!(bGetIsDeniedDexBonus || bIsHelpless)) + iAC -= GetItemACValue( GetItemInSlot(INVENTORY_SLOT_CARMOUR, oDefender) ); + + // Wilders get to add cha bonus to touch attacks only, but cannot exceed normal AC that way + if(GetHasFeat(FEAT_WILDER_ELUDE_TOUCH, oDefender)) + iAC = PRCMin(iAC + GetAbilityModifier(ABILITY_CHARISMA, oDefender), nNormalAC); + } + //if (DEBUG) DoDebug("GetDefenderAC: End Section #5"); + return iAC; +} + + +// motu99: restructured code for better efficiency / readability March 16, 2007 +// note that the attack boni calculated here also depend on the defender +// (for instance if weapon has an attack/enhancement bonus vs. specific alignments) + +// the only source of defender specific AB in this function is the attacker's weapon +// if efficiency becomes an issue, it might be better to remember the defender (or its race/alignment), +// so that AB and other stuff will only be recalculated when the defender changes. + +// at the current time, GetAttackBonus is calculated at the beginning of the round, but not for every attack. +// therefore, if the defender changes during the round, weapon boni versus specific enemy races or alignments are not properly accounted for +// so we eventually might want to move that part out of GetAttackBonus. +// also if we switch weapons during a combat round, this will not be not noticed until the beginning of the next round, when GetAttackBonus is called anew + +// the following AB-sources are taken into account: (* = depends on Defender) +// Base Attack Bonus - iBAB +// Ability modifier due to Str/Dex/Wis - iAbilityBonus [motu99: could change during a round, if enemy does ability decreasing attacks] +// Bonus from Feats - iFeatBonus (Weapon Focus, Specialization, Prowess, Good aim) [motu99: will change during the round, if we equip other weapons, or if defenders comes into melee range for a ranged attack (with/without Point Blank Shot)] +// Two Weapon Fighting penalties - iTWFPenalty (is subtracted) [motu99: will change during the round, if we equip other weapons] +// Bonus / Penalties from combat modes - iCombatModeBonus (Flurry of Blows is treated elsewhere) [motu99: the PC can change the combat mode during a round] +// Magical Boni from Spells on Attacker - iMagicBonus [motu99: might not last through the round, or could be dispelled] +// * Boni on Weapon - iWeaponAttackBonus, iWeaponEnhancement [motu99: will change during the round, if defender's race or alignment changes] +int GetAttackBonus(object oDefender, object oAttacker, object oWeap, int iOffhand = 0, int iTouchAttackType = FALSE) +{ +// note that oWeap could be gloves (for an unarmed monk) or a creature weapon +// passing gloves or ammunition to this function may not work well with the override feature in PerformAttack() +// [in order to calculate dual wielding penalties GetAttackBonus does not look up the weapon override variables, it looks directly into the inventory slots] +// possible solution: pass right and left weapons to this function! (but make sure, that GetAttackBonus is not used in other prc sources,unless you want to modify these sources!!) + + struct WeaponFeat sWeaponFeat; // holds all the feats relevant for the weapon (or rather weapon base type) + int iAttackBonus = 0; + int iCappedAttackBonus = 0; //ebonfowl: bonuses are added to this value and truncated at the global attack limit if they are of the variety that are capped in normal combat. Can be disabled via the switch PRC_CAPPED_ATTACK_BONUS. + int iCappedRightBonus = 0; //ebonfowl: these are EffectAttackIncrease bonuses applied specifically to the right hand which add to the capped limit + int iCappedRightPen = 0; //ebonfowl: same as above but penalties + int iCappedLeftBonus = 0; //ebonfowl: these are EffectAttackIncrease bonuses applied specifically to the left hand which add to the capped limit + int iCappedLeftPen = 0; //ebonfowl: same as above but penalties + int iAbilityBonus = 0; // boni from abilities (Str, Dex, Wis) - also depends on feats (Finesse, Intuitive Attack) and type of attack (touch, melee, ranged) + int iFeatBonus = 0; // boni from feats (mostly weapon specific: weapon focus, specialization, prowess, weapon of choice) + int iMagicBonus = 0; // boni from AB increasing spells or spell effects on attacker + int iCombatModeBonus = 0; // boni / penalties from Combat Mode + int iTWFPenalty = 0; // penalty from two weapon fighting + int iBAB = GetBaseAttackBonus(oAttacker); + // DoDebug("entering GetAttackBonus() for Weapon " + GetName(oWeap) + ", Attacker: " + GetName(oAttacker) + ", Defender: " + GetName(oDefender)); + int iWeaponAttackBonus = GetWeaponAttackBonusItemProperty(oWeap, oDefender); + int iWeaponType = GetBaseItemType(oWeap); + int iCombatMode = GetLastAttackMode(oAttacker); + int iStr = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); + int iDex = GetAbilityModifier(ABILITY_DEXTERITY, oAttacker); + int iWis = GetAbilityModifier(ABILITY_WISDOM, oAttacker); // needed for ZenArchery and intuitive attack + int bIsRangedWeapon = GetIsRangedWeaponType(iWeaponType); // = GetWeaponRanged(oWeap); + + // uses GetMonkEnhancement in case a glove/creature weapon is passed as oWeapon + int iWeaponEnhancement = GetMonkEnhancement(oWeap, oDefender, oAttacker); + + if (GetLocalInt(oAttacker, "BABOverride")) iBAB = GetLocalInt(oAttacker, "BABOverride"); + + // weapon specific feats + sWeaponFeat = GetAllFeatsOfWeaponType(iWeaponType); + + int bFocus = 0; + int bEpicFocus = 0; + int bIsRangedTouchAttack = iTouchAttackType == TOUCH_ATTACK_RANGED || iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL; + + if(bIsRangedTouchAttack) + { + // Weapon Focus(Ray) applies to touch attacks(motu99: ranged only?) + bFocus = GetHasFeat(FEAT_WEAPON_FOCUS_RAY, oAttacker); + if (bFocus) // no need to look for epic focus, if we don't have focus + bEpicFocus = GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_RAY, oAttacker); + } + else + { // no touch attack, normal weapon focus feats + bFocus = GetHasFeat(sWeaponFeat.Focus, oAttacker); + if (bFocus) // no need to look for epic focus, if we don't have focus + bEpicFocus = GetHasFeat(sWeaponFeat.EpicFocus, oAttacker); + } + + int bEpicProwess = GetHasFeat(FEAT_EPIC_PROWESS, oAttacker); + + // attack bonus from feats + if(bFocus) iFeatBonus += 1; + if(bEpicFocus) iFeatBonus += 2; + if(bEpicProwess) iFeatBonus += 1; + + +// if(bWeaponOfChoice) iFeatBonus += (GetLevelByClass(CLASS_TYPE_WEAPON_MASTER, oAttacker) / 5); +// motu99: original calculation was not correct: Gimoire says that we get AB +1 at level 5, another +1 at level 13 and +1 every third level thereafter +// lets hope Grimoire was right, otherwise I'll be off on a trip to Canossa + int iWeaponMasterLevel = GetLevelByClass(CLASS_TYPE_WEAPON_MASTER, oAttacker); + + // only look for weapon of choice and related AB increase, if we have enough weapon master levels for the superior weapon focus feat + if(iWeaponMasterLevel >= WEAPON_MASTER_LEVEL_SUPERIOR_WEAPON_FOCUS) + { + int bWeaponOfChoice = GetHasFeat(sWeaponFeat.WeaponOfChoice, oAttacker); + + if (bWeaponOfChoice) + { + iFeatBonus++; + if (iWeaponMasterLevel >= WEAPON_MASTER_LEVEL_EPIC_SUPERIOR_WEAPON_FOCUS) + iFeatBonus += (iWeaponMasterLevel-WEAPON_MASTER_LEVEL_EPIC_SUPERIOR_WEAPON_FOCUS)/3 +1; + } + } + + + if(!bIsRangedWeapon && !bIsRangedTouchAttack) // first do calculations for "normal" melee weapons (includes unarmed, even torches) + { + //this is the attack bonus from ability for melee combat only + //ranged combat goes further down + + // Melee Specific Rules + int bIsFinessableWeapon = FALSE; + int iCreatureSize = PRCGetCreatureSize(oAttacker); + int iWeaponSize = StringToInt(Get2DACache("baseitems", "WeaponSize", iWeaponType)); + + if(iWeaponType == BASE_ITEM_KATANA) + { // if we wield a katana, see if we have the katana finesse feat; if yes we have a finessable weapon + bIsFinessableWeapon = GetHasFeat(FEAT_KATANA_FINESSE, oAttacker); + } + else if(GetTag(oWeap) == "prc_eldrtch_glv") + { + bIsFinessableWeapon = GetHasFeat(FEAT_WEAPON_FINESSE, oAttacker); + } + else if(GetIsNaturalWeapon(oWeap)) + { // all "natural" weapons are finessable (the critters have them from their birth - if they cannot use DEX for their own limbs ...) + bIsFinessableWeapon = GetHasFeat(FEAT_WEAPON_FINESSE, oAttacker); + } + // motu99: added a switch in order to have sensible rules for small creatures, don't know it its PnP, but assume so + else if(GetPRCSwitch(PRC_SMALL_CREATURE_FINESSE) && iWeaponSize < iCreatureSize) + { // with the switch on, weapon is only finessable if its size is smaller than the creature size + bIsFinessableWeapon = GetHasFeat(FEAT_WEAPON_FINESSE, oAttacker); + } + // switch is off, so normal bioware rules: finessable weapons are rapiers and small or tiny weapons (size <= 2) + else if(iWeaponType == BASE_ITEM_RAPIER || iWeaponSize <= WEAPON_SIZE_SMALL) + { + bIsFinessableWeapon = GetHasFeat(FEAT_WEAPON_FINESSE, oAttacker); + } + + // now increase attack bonus from stats for melee + // str normally unless exceptional circumstances + // if(iStr > bTempBonus) // motu99: that is strange, this code prehibits any attack penalties due to low strength for melee weapons; removed this check + iAbilityBonus = iStr; + + // if we have a finessable weapon, we take Dex whenever it is higher than Str + if(bIsFinessableWeapon) + { + if(iDex > iAbilityBonus) iAbilityBonus = iDex; + } + + // Two Weapon Fighting Penalties + // NwN only allows melee weapons to be dual wielded + // motu99: this calculation partly ignores the weapon overrides we might have passed to PerformAttack() + + int iOffhandWeaponType; + int bIsDoubleSidedWeapon = FALSE; + + // motu99: added check for double sided weapons + if(GetIsDoubleSidedWeaponType(iWeaponType)) + { + bIsDoubleSidedWeapon = TRUE; + // iOffhandWeaponType = iWeaponType; // motu99 don't need this + } + else + { // if it is an offhand attack, we assume the weapon given to us in oWeap is the offhand weapon + if (iOffhand) + { + iOffhandWeaponType = iWeaponType; + } + else // otherwise we must look in the left hand slot (this ignores any overrides from PerformAttack) + { + iOffhandWeaponType = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oAttacker)); + } + } + + //ebonfowl: now checking for attack bonuses and penalties that are hand-specific, only the highest applies in each case + + if(iOffhand) //ebonfowl: is it an off-hand attack? + { + effect eEffect = GetFirstEffect(oAttacker); + int nType, iEffectSubtype; + + while(GetIsEffectValid(eEffect)) + { + nType = GetEffectType(eEffect); + iEffectSubtype = GetEffectInteger(eEffect, 1); + + int iBonus = 0; + + int iRace = MyPRCGetRacialType(oDefender); + + int iEffectRace = GetEffectInteger(eEffect, 2); + + int iGoodEvil = GetAlignmentGoodEvil(oDefender); + int iLawChaos = GetAlignmentLawChaos(oDefender); + + int iEffectLawChaos = GetEffectInteger(eEffect, 3); + int iEffectGoodEvil = GetEffectInteger(eEffect, 4); + + if(nType == EFFECT_TYPE_ATTACK_INCREASE && iEffectSubtype == 2) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedLeftBonus) iCappedLeftBonus = iBonus; + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedLeftBonus) iCappedLeftBonus = iBonus; + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedLeftBonus) iCappedLeftBonus = iBonus; + } + + } + else if(nType == EFFECT_TYPE_ATTACK_DECREASE && iEffectSubtype == 2) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedLeftPen) iCappedLeftPen = iBonus; + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedLeftPen) iCappedLeftPen = iBonus; + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedLeftPen) iCappedLeftPen = iBonus; + } + + } + + eEffect = GetNextEffect(oAttacker); + } + + iCappedLeftBonus -= iCappedLeftPen; + } + else //ebonfowl: anything else will be a main-hand attack + { + effect eEffect = GetFirstEffect(oAttacker); + int nType, iEffectSubtype; + + while(GetIsEffectValid(eEffect)) + { + nType = GetEffectType(eEffect); + iEffectSubtype = GetEffectInteger(eEffect, 1); + + int iBonus = 0; + + int iRace = MyPRCGetRacialType(oDefender); + + int iEffectRace = GetEffectInteger(eEffect, 2); + + int iGoodEvil = GetAlignmentGoodEvil(oDefender); + int iLawChaos = GetAlignmentLawChaos(oDefender); + + int iEffectLawChaos = GetEffectInteger(eEffect, 3); + int iEffectGoodEvil = GetEffectInteger(eEffect, 4); + + if(nType == EFFECT_TYPE_ATTACK_INCREASE && iEffectSubtype == 1) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedRightBonus) iCappedRightBonus = iBonus; + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedRightBonus) iCappedRightBonus = iBonus; + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedRightBonus) iCappedRightBonus = iBonus; + } + + } + else if(nType == EFFECT_TYPE_ATTACK_DECREASE && iEffectSubtype == 1) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedRightPen) iCappedRightPen = iBonus; + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedRightPen) iCappedRightPen = iBonus; + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic attack increase and set the bonus if the highest one on that hand + iBonus = GetEffectInteger(eEffect, 0); + if(iBonus > iCappedRightPen) iCappedRightPen = iBonus; + } + + } + + eEffect = GetNextEffect(oAttacker); + } + + iCappedRightBonus -= iCappedRightPen; + } + + if(bIsDoubleSidedWeapon || GetIsOffhandWeaponType(iOffhandWeaponType)) + { + int bOffHandLight; + + if(bIsDoubleSidedWeapon) + bOffHandLight = TRUE; + else + { // find out offhand weapon size, to see if it is light + int iOffhandWeaponSize; + if (iOffhandWeaponType == iWeaponType) + iOffhandWeaponSize = iWeaponSize; + else + iOffhandWeaponSize = StringToInt(Get2DACache("baseitems", "WeaponSize", iOffhandWeaponType)); + + // is the size appropriate for a light weapon? + bOffHandLight = (iOffhandWeaponSize < iCreatureSize); // motu99: added creature size + } + + int bHasTWF = FALSE; + int bHasAmbidex = FALSE; + + // since there is no way to determine the value of AB effects + // applied to a PC, I had to add Absolute Ambidexterity here + + int bHasAbsoluteAmbidex = FALSE; + + if(GetHasFeat(FEAT_AMBIDEXTERITY, oAttacker) ) bHasAmbidex = TRUE; // motu99: Ambidex and TWF were mixed up, changed that + if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oAttacker) ) bHasTWF = TRUE; + if(GetHasFeat(FEAT_ABSOLUTE_AMBIDEX, oAttacker) ) bHasAbsoluteAmbidex = TRUE; + + if(GetLevelByClass(CLASS_TYPE_RANGER, oAttacker) >= RANGER_LEVEL_DUAL_WIELD) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oAttacker); + int iArmorType = GetArmorType(oArmor); + if(iArmorType != ARMOR_TYPE_MEDIUM && iArmorType != ARMOR_TYPE_HEAVY) + { + bHasTWF = TRUE; + bHasAmbidex = TRUE; + } + else + { // ranger looses all dual wielding abilities if in medium armor or higher + bHasTWF = FALSE; + bHasAmbidex = FALSE; + } + + } + + // a tempest using two sided weapons or in medium or heavy armor looses absolute ambidex feat + int iTempestLevel = GetLevelByClass(CLASS_TYPE_TEMPEST, oAttacker); + int nBloodclaw = GetLevelByClass(CLASS_TYPE_BLOODCLAW_MASTER, oAttacker); + if(iTempestLevel) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oAttacker); + int iArmorType = GetArmorType(oArmor); + if(bIsDoubleSidedWeapon || iArmorType == ARMOR_TYPE_MEDIUM || iArmorType == ARMOR_TYPE_HEAVY) + bHasAbsoluteAmbidex = FALSE; + else if (iTempestLevel >= TEMPEST_LEVEL_ABS_AMBIDEX || nBloodclaw >= 2) + bHasAbsoluteAmbidex = TRUE; + } + + if(iOffhand && !bHasAmbidex) iTWFPenalty = 10; // motu99: old code was wrong, because of confusing variable name (what should have been called iOffhand was called iMainHand) + else iTWFPenalty = 6; + + if(bHasTWF) iTWFPenalty -= 2; + if(bOffHandLight) iTWFPenalty -= 2; + if(bHasAbsoluteAmbidex) iTWFPenalty -= 2; // motu99: actually, if the absoluteambidex adds a permanent +2 to AB, this should be deleted + + } + // power attack can only be used in melee + // Handle the PRC Power Attack BA pay, if any. + iCombatModeBonus -= GetLocalInt(oAttacker, "PRC_PowerAttack_Level"); + + // Power Attack combat modes. The stacking of these with PRC Power attack is handled by the PRC PA scripts + if (iCombatMode == COMBAT_MODE_POWER_ATTACK) iCombatModeBonus -= 5; + else if(iCombatMode == COMBAT_MODE_IMPROVED_POWER_ATTACK) iCombatModeBonus -= 10; + +// motu99: might have to add flurry combat mode (is covered in initialization of PerformAttack and PerformAttackRound, but it can change) +// if (iCombatMode == COMBAT_MODE_FLURRY_OF_BLOWS) iCombatModeBonus -= 2; + + // dirty fighting, although a combat mode, should not be checked here, because we forfeit all attacks in the round when we use it + + } + else // Ranged Specific Rules + { + // range penalty not yet accounted for as the 2da's are messed up + // the range increment for throwing axes is 63, while it's 20 for bows??? + + // dex or wis bonus + if(iWis > iDex && GetHasFeat(FEAT_ZEN_ARCHERY, oAttacker)) + iAbilityBonus = iWis; + else + iAbilityBonus = iDex; + + if(GetMeleeAttackers15ft(oAttacker)) // motu99: The function actually checks if attackers are within 10 feet + { + if(GetHasFeat(FEAT_POINT_BLANK_SHOT,oAttacker)) + iFeatBonus += 1; + else + iFeatBonus -= 4; + } + + // Halfling +1 bonus for throwing weapons + if (GetIsThrowingWeaponType(iWeaponType) && GetHasFeat(FEAT_GOOD_AIM, oAttacker)) + iFeatBonus += 1; // we add this to the feat bonus, because it only depends on the feat and the weapon used, not on the defender + } + + // if they have intuitive attack feat, checks if wisdom is highest, + // this applies to ranged attacks with crossbows and slings, + // and melee attacks with any other simple weapon, but not to touch attacks + // motu99: optimized the sequence of checks (least expensive check first) + if( iWis > iAbilityBonus + && GetIsSimpleWeaponType(iWeaponType) + && GetHasFeat(FEAT_INTUITIVE_ATTACK, oAttacker) ) + iAbilityBonus = iWis; + + //touch attacks always use dex, no matter what. Therefore override any calculations we have done so far + if(iTouchAttackType) + iAbilityBonus = iDex; + + // Expertise penalties apply to all attack rolls + if (iCombatMode == COMBAT_MODE_EXPERTISE) iCombatModeBonus -= 5; + else if(iCombatMode == COMBAT_MODE_IMPROVED_EXPERTISE) iCombatModeBonus -= 10; + + // get the magical attack bonus on the attacker from AB increasing spells + iMagicBonus = GetMagicalAttackBonus(oAttacker, oDefender); + + // everything starts from BAB + iAttackBonus = iBAB; + + // adds boni from feats + iAttackBonus += iFeatBonus; + + // adds ability modifiers to attack bonus + iAttackBonus += iAbilityBonus; + + // subtracts two weapon fighting penalties (iTWFPenalty is always positive or zero) + iAttackBonus -= iTWFPenalty; + + // adds bonus from combat modes (these are actually penalties, so iCombatModeBonus is always negative) + iAttackBonus += iCombatModeBonus; + + // Adds all spell bonuses / penalties on the PC + iCappedAttackBonus += iMagicBonus; + + // up to now iAttackBonus should be independent of Defender, so it is likely to remain constant during a whole round + // however, combat mode can change, weapons can be equipped / unequipped (not always starts a new combat round) + // spells can run out during the round, etc. + + // adds weapon enhancement to the bonus + iCappedAttackBonus += iWeaponEnhancement; + + // adds weapon attack boni to the bonus + iCappedAttackBonus += iWeaponAttackBonus; + + //ebonfowl: adds handedness attack bonus to the capped total + //You can add both as this is attack-specific so one will always be 0 + //Hand-specfic penalties are already subtracted out at this point + iCappedAttackBonus += iCappedLeftBonus; + iCappedAttackBonus += iCappedRightBonus; + + //ebonfowl: cutoff iCappedAttackBonus at the limit set globally if PRC_CAPPED_ATTACK_BONUS is TRUE + + if(GetPRCSwitch(PRC_CAPPED_ATTACK_BONUS)) + { + int iAttackBonusCap = GetAttackBonusLimit(); + + if (iCappedAttackBonus > iAttackBonusCap) iCappedAttackBonus = iAttackBonusCap; + } + + //ebonfowl: adds the capped effects to the other effects for the final total + + iAttackBonus += iCappedAttackBonus; + + if (GetPRCSwitch(PRC_COMBAT_DEBUG)) + { + string sDebugFeedback = PRC_TEXT_WHITE; + sDebugFeedback += ("AB = " + IntToString(iAttackBonus) + " : "); + sDebugFeedback += ("BAB (" + IntToString(iBAB) + ")"); + sDebugFeedback += (" + Feats (" + IntToString(iFeatBonus) + ")"); + sDebugFeedback += (" + Stat Bonus (" + IntToString(iAbilityBonus) + ")"); + sDebugFeedback += (" - TWF Penalty (" + IntToString(iTWFPenalty) + ")"); + sDebugFeedback += (" - Combat Mode (" + IntToString(-iCombatModeBonus) + ")"); + sDebugFeedback += (" + Spells (" + IntToString(iMagicBonus) + ")"); + sDebugFeedback += (" + WeapEnh (" + IntToString(iWeaponEnhancement) + ")"); + sDebugFeedback += (" + WeapAB (" + IntToString(iWeaponAttackBonus) + ")"); + DoDebug(sDebugFeedback); + } + + return iAttackBonus; +} + +// this is to be used in GetAttackRoll since it needs to be checked every attack instead of each round. +int GetAttackModVersusDefender(object oDefender, object oAttacker, object oWeapon, int iTouchAttackType = FALSE) +{ + int iAttackMod = 0; + + // add bonus +2 for flanking, invisible attacker, attacking blind opponent + // motu99: Note that GetIsFlanked does not work reliably (at least in PRC 3.1c) + if( GetIsFlanked(oDefender, oAttacker) ) + { + if (DEBUG_BATTLE_FLANKING) FloatingTextStringOnCreature("**** FLANKING AB BONUS ****", oDefender); + if (DEBUG_BATTLE_FLANKING) DoDebug("Flanking bonus to AB", oDefender); + iAttackMod += 2; + if(GetLocalInt(oAttacker, "UmbralStrike")) iAttackMod += 2; +// DoDebug("GetAttackModVersusDefender: Defender flanked"); + } + //if (DEBUG) DoDebug("GetAttackModVersusDefender: End Section #1"); + if ( ( PRCGetHasEffect(EFFECT_TYPE_INVISIBILITY, oAttacker) + || PRCGetHasEffect(EFFECT_TYPE_IMPROVEDINVISIBILITY, oAttacker) + && !GetHasFeat(FEAT_BLIND_FIGHT, oDefender) + ) + || PRCGetHasEffect(EFFECT_TYPE_BLINDNESS, oDefender) + ) + iAttackMod += 2; + + // +2 attack bonus if they are stunned or frightened + if( PRCGetHasEffect(EFFECT_TYPE_STUNNED, oDefender) + || PRCGetHasEffect(EFFECT_TYPE_FRIGHTENED, oDefender) ) + { + iAttackMod += 2; +// DoDebug("GetAttackModVersusDefender: Defender frightened or stunned"); + } + //if (DEBUG) DoDebug("GetAttackModVersusDefender: End Section #2"); + int bIsMeleeWeapon = !GetWeaponRanged(oWeapon); + int bIsRangedTouchAttack = iTouchAttackType == TOUCH_ATTACK_RANGED || iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL; + if(bIsRangedTouchAttack) + bIsMeleeWeapon = FALSE; + + int bIsKnockedDown = GetHasFeatEffect(FEAT_KNOCKDOWN, oDefender) || GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN, oDefender); + + if(bIsMeleeWeapon) + { + // +4 to attack in melee against a helpless target. + if(GetIsHelpless(oDefender)) iAttackMod += 4; + + // +4 attack bonus to a prone target (in melee) / -4 in ranged combat + if(bIsKnockedDown) iAttackMod += 4; + } + else // ranged combat + { + // -4 attack bonus to a prone target in ranged combat + if(bIsKnockedDown) iAttackMod -= 4; + } + //if (DEBUG) DoDebug("GetAttackModVersusDefender: End Section #3"); + + // Battle training (Gnomes and Dwarves) + // adds +1 based on enemy race +// int iRacialType = MyPRCGetRacialType(oAttacker); // motu99: don't need the attacker race, just check for the feats! +// if(iRacialType == RACIAL_TYPE_DWARF || iRacialType == RACIAL_TYPE_GNOME) + { + int bOrcTrain = GetHasFeat(FEAT_BATTLE_TRAINING_VERSUS_ORCS, oAttacker); + int bGobTrain = GetHasFeat(FEAT_BATTLE_TRAINING_VERSUS_GOBLINS, oAttacker); + int bLizTrain = GetHasFeat(FEAT_BATTLE_TRAINING_VERSUS_REPTILIANS, oAttacker); + int iEnemyRace = MyPRCGetRacialType(oDefender); + + if(bOrcTrain && iEnemyRace == RACIAL_TYPE_HUMANOID_ORC) iAttackMod += 1; + if(bGobTrain && iEnemyRace == RACIAL_TYPE_HUMANOID_GOBLINOID) iAttackMod += 1; + if(bLizTrain && iEnemyRace == RACIAL_TYPE_HUMANOID_REPTILIAN) iAttackMod += 1; + } + //if (DEBUG) DoDebug("GetAttackModVersusDefender: End Section #4"); +// if( GetHasFeat(FEAT_SMALL, oAttacker) || GetHasFeat(FEAT_LARGE, oAttacker) ) // don't really need the feat, just check for size difference + { + int iDefenderSize = PRCGetCreatureSize(oDefender); + int iAttackerSize = PRCGetCreatureSize(oAttacker); + if(iAttackerSize < iDefenderSize) // we could also use size difference to calculate the attack mod; have to check PnP rules + iAttackMod++; + if(iAttackerSize > iDefenderSize) + iAttackMod--; + } +// DoDebug("GetAttackModVersusDefender() returns " + IntToString(iAttackMod)); + //if (DEBUG) DoDebug("GetAttackModVersusDefender: End Section #5"); + return iAttackMod; +} + +int GetAttackRoll(object oDefender, object oAttacker, object oWeapon, int iOffhand = 0, int iAttackBonus = 0, int iMod = 0, int bShowFeedback = TRUE, float fDelay = 0.0, int iTouchAttackType = FALSE) +// returns 1 on a normal hit +// returns 2 on a critical hit +// never returns a 2, if defender is critical immune +{ + if (!iAttackBonus) // only calculate attack bonus, if not already done so + iAttackBonus = GetAttackBonus(oDefender, oAttacker, oWeapon, iOffhand, iTouchAttackType); + + int iDiceRoll = d20(); + //if (DEBUG) DoDebug("Starting DSPerfectOrder"); + // All rolls = 11 for this guy + if (GetLocalInt(oAttacker, "DSPerfectOrder")) + iDiceRoll = 11; + //if (DEBUG) DoDebug("Ending DSPerfectOrder"); + //string sDebugFeedback = ""; + //if (DEBUG) DoDebug("GetAttackRoll: Line #1"); + //int bDebug = GetPRCSwitch(PRC_COMBAT_DEBUG); + //if (DEBUG) DoDebug("GetAttackRoll: Line #2"); + //if (bDebug) sDebugFeedback += "d20 (" + IntToString(iDiceRoll) + ")"; + //if (DEBUG) DoDebug("GetAttackRoll: Line #3"); + //if (bDebug) sDebugFeedback += " + AB (" + IntToString(iAttackBonus) + ")"; + //if (DEBUG) DoDebug("GetAttackRoll: Line #4"); + iAttackBonus += iMod; + // Divine Fury ability + if(GetLocalInt(oAttacker, "RKVDivineFury")) + iAttackBonus += 4; + + // Master of Nine + if(GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oAttacker) > 2) + { + int nLastClass = GetLocalInt(oAttacker, "PRC_CurrentManeuver_InitiatingClass") - 1;//GetInitiatingClass(oAttacker); + if(nLastClass == CLASS_TYPE_WARBLADE + || nLastClass == CLASS_TYPE_SWORDSAGE + || nLastClass == CLASS_TYPE_CRUSADER) + { + // Increases maneuver attacks by 2 + iAttackBonus += 2; + } + } + + // Shadow Sun Ninja + if(GetLocalInt(oAttacker, "SSN_DARKWL")) + { + effect eSSN = GetFirstEffect(oDefender); + while(GetIsEffectValid(eSSN)) + { + if(GetEffectType(eSSN) == EFFECT_TYPE_BLINDNESS) + { + iAttackBonus += 4; + break; + } + eSSN = GetNextEffect(oDefender); + } + } + //if (DEBUG) DoDebug("GetAttackRoll: Line #5"); + //if(bDebug) sDebugFeedback += " - APR penalty (" + IntToString(iMod * -1) + ")"; + //if (DEBUG) DoDebug("Starting GetAttackModVersusDefender"); + int iDefenderMod = GetAttackModVersusDefender(oDefender, oAttacker, oWeapon, iTouchAttackType); + iAttackBonus += iDefenderMod; + + SetLocalInt(oAttacker, "PRCAttackBonus", iDiceRoll + iAttackBonus); + DelayCommand(1.5, DeleteLocalInt(oAttacker, "PRCAttackBonus")); + + //if(bDebug) sDebugFeedback += " + Atk vs Def Adj (" + IntToString(iDefenderMod) + ")"; + + //if (DEBUG) DoDebug("Starting GetDefenderAC"); + int iEnemyAC = GetDefenderAC(oDefender, oAttacker, iTouchAttackType); + + //if (bDebug) sDebugFeedback += " *versus* AC (" + IntToString(iEnemyAC) + ")"; + //if (bDebug) sDebugFeedback = COLOR_WHITE + "Attack Roll = " + IntToString(iAttackBonus + iDiceRoll) + ": " + sDebugFeedback; + //if (DEBUG) DoDebug("GetAttackRoll: End Section #1"); + int iWeaponType = GetBaseItemType(oWeapon); + int iCritThreat = GetWeaponCriticalRange(oAttacker, oWeapon); + + //If using Killing Shot, ciritical range improves by 2; + if(GetLocalInt(oAttacker, "KillingShotCritical") ) + { + iCritThreat -= 2; + DeleteLocalInt(oAttacker, "KillingShotCritical"); + } + + // print off-hand of off-hand attack + string sFeedback =""; + if(iOffhand) sFeedback += PRC_TEXT_ORANGE + GetStringByStrRef(1559)/*"Off Hand"*/ + " : "; + + // change color of attacker if it is Player or NPC + if(GetIsPC(oAttacker)) sFeedback += PRC_TEXT_LIGHT_BLUE; + else sFeedback += PRC_TEXT_LIGHT_PURPLE; + + // display name of attacker + sFeedback += GetName(oAttacker); + + int bIsMeleeTouchAttack = iTouchAttackType == TOUCH_ATTACK_MELEE || iTouchAttackType == TOUCH_ATTACK_MELEE_SPELL; + int bIsRangedTouchAttack = iTouchAttackType == TOUCH_ATTACK_RANGED || iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL; + // show proper message for touch attacks or normal attacks. + if(bIsRangedTouchAttack) + sFeedback += PRC_TEXT_PURPLE + " attempts ranged touch attack on "; + else if(bIsMeleeTouchAttack) + sFeedback += PRC_TEXT_PURPLE + " attempts touch attack on "; + else + sFeedback += PRC_TEXT_ORANGE + " attacks "; + + sFeedback += GetName(oDefender) + ": "; + //if (DEBUG) DoDebug("GetAttackRoll: End Section #2"); + int iReturn = 0; + // roll concealment check + int iConcealment = GetIsConcealed(oDefender, oAttacker); + int iConcealRoll = d100(); + int bEnemyIsConcealed = FALSE; + + if(iConcealRoll <= iConcealment) + { + // Those with blind-fight get a re-roll + if( GetHasFeat(FEAT_BLIND_FIGHT, oAttacker) ) + { + iConcealRoll = d100(); + } + + if(iConcealRoll <= iConcealment) + { + bEnemyIsConcealed = TRUE; + sFeedback += "*"+GetStringByStrRef(514)/*miss*/+"*: (Enemy is Concealed)"; + iReturn = 0; + } + } + // if (DEBUG) DoDebug("GetAttackRoll: End Section #3"); + if (!bEnemyIsConcealed) + { + // Autmatically dodge the first attack of each round + if(bFirstAttack && GetHasFeat(FEAT_EPIC_DODGE, oDefender)) + { + sFeedback += "*"+GetStringByStrRef(514)/*miss*/+"*: (Enemy Dodged)"; + iReturn = 0; + } + + // did we hit? meaning we overcome the enemy's AC and did not roll a one (iDiceRoll == 1) + int bHit = iDiceRoll + iAttackBonus > iEnemyAC && iDiceRoll != 1; + + // Check for a critical threat + if( iDiceRoll == 20 // we always score a critical hit on a twenty + || (bHit && iDiceRoll >= iCritThreat) ) // otherwise we must have hit and overcome the critical threat range + { + sFeedback += "*Critical Hit*: (" + IntToString(iDiceRoll) + " + " + IntToString(iAttackBonus) + " = " + IntToString(iDiceRoll + iAttackBonus) + "): "; + + //Roll again to see if we scored a critical hit + //FistOfRaziels of over level 3 automatically confirm critical hits + //when smiting evil + int iCritThreatRoll; + if(GetLocalInt(oAttacker, "FistOfRazielSpecialSmiteCritical") ) + { + iCritThreatRoll = 10000; + DeleteLocalInt(oAttacker, "FistOfRazielSpecialSmiteCritical"); + } + else + { + iCritThreatRoll = d20(); + if (GetLocalInt(oDefender, "BoneCrusher")) iCritThreatRoll += 10; + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oAttacker) >= 3) + { + iCritThreatRoll += GetAbilityModifier(ABILITY_INTELLIGENCE, oAttacker); //Warblade Battle Ardorr + if (DEBUG_BATTLE_ARDOR) DoDebug("Warblade Battle Ardor critical attack threat roll bonus"); + } + } + // if (DEBUG) DoDebug("GetAttackRoll: End Section #4"); + + if(!GetIsImmune(oDefender, IMMUNITY_TYPE_CRITICAL_HIT) ) + { + sFeedback += "*Threat Roll*: (" + IntToString(iCritThreatRoll) + " + " + IntToString(iAttackBonus) + " = " + IntToString(iCritThreatRoll + iAttackBonus) + ")"; + if(iCritThreatRoll + iAttackBonus > iEnemyAC) iReturn = 2; + else iReturn = 1; + } + else + { + sFeedback += "*Target Immune to Critical Hits*"; + iReturn = 1; + } + } + //Just a regular hit + else if(bHit) + { + sFeedback += "*hit*: (" + IntToString(iDiceRoll) + " + " + IntToString(iAttackBonus) + " = " + IntToString(iDiceRoll + iAttackBonus) + ")"; + iReturn = 1; + } + //Missed + else + { + sFeedback += "*miss*: (" + IntToString(iDiceRoll) + " + " + IntToString(iAttackBonus) + " = " + IntToString(iDiceRoll + iAttackBonus) + ")"; + iReturn = 0; + } + //if (DEBUG) DoDebug("GetAttackRoll: End Section #5"); + } + //arrow VFX + //this is done with crossbows and other ranged weapons + //at least you see some projectile rather than none at all + if(GetIsRangedWeaponType(iWeaponType)) + { + if(iReturn) + AssignCommand(oAttacker, ApplyEffectToObject(DURATION_TYPE_INSTANT, + EffectVisualEffect(NORMAL_ARROW, FALSE), oDefender)); + else + AssignCommand(oAttacker, ApplyEffectToObject(DURATION_TYPE_INSTANT, + EffectVisualEffect(NORMAL_ARROW, TRUE), oDefender)); + } + + if(bShowFeedback) + { + SendMessageToPC(oAttacker, sFeedback); // DelayCommand(fDelay, SendMessageToPC(oAttacker, sFeedback)); + //if (bDebug) SendMessageToPC(oAttacker, sDebugFeedback); + } + // if (DEBUG) DoDebug("GetAttackRoll: End Section #6"); + return iReturn; +} + +//::////////////////////////////////////////////// +//:: Damage Bonus Functions +//::////////////////////////////////////////////// + +int GetFavoredEnemyFeat(int iRacialType) +{ + switch(iRacialType) + { + case RACIAL_TYPE_DWARF: return FEAT_FAVORED_ENEMY_DWARF; + case RACIAL_TYPE_ELF: return FEAT_FAVORED_ENEMY_ELF; + case RACIAL_TYPE_GNOME: return FEAT_FAVORED_ENEMY_GNOME; + case RACIAL_TYPE_HALFLING: return FEAT_FAVORED_ENEMY_HALFLING; + case RACIAL_TYPE_HALFELF: return FEAT_FAVORED_ENEMY_HALFELF; + case RACIAL_TYPE_HALFORC: return FEAT_FAVORED_ENEMY_HALFORC; + case RACIAL_TYPE_HUMAN: return FEAT_FAVORED_ENEMY_HUMAN; + case RACIAL_TYPE_ABERRATION: return FEAT_FAVORED_ENEMY_ABERRATION; + case RACIAL_TYPE_ANIMAL: return FEAT_FAVORED_ENEMY_ANIMAL; + case RACIAL_TYPE_BEAST: return FEAT_FAVORED_ENEMY_BEAST; + case RACIAL_TYPE_CONSTRUCT: return FEAT_FAVORED_ENEMY_CONSTRUCT; + case RACIAL_TYPE_DRAGON: return FEAT_FAVORED_ENEMY_DRAGON; + case RACIAL_TYPE_HUMANOID_GOBLINOID: return FEAT_FAVORED_ENEMY_GOBLINOID; + case RACIAL_TYPE_HUMANOID_MONSTROUS: return FEAT_FAVORED_ENEMY_MONSTROUS; + case RACIAL_TYPE_HUMANOID_ORC: return FEAT_FAVORED_ENEMY_ORC; + case RACIAL_TYPE_HUMANOID_REPTILIAN: return FEAT_FAVORED_ENEMY_REPTILIAN; + case RACIAL_TYPE_ELEMENTAL: return FEAT_FAVORED_ENEMY_ELEMENTAL; + case RACIAL_TYPE_FEY: return FEAT_FAVORED_ENEMY_FEY; + case RACIAL_TYPE_GIANT: return FEAT_FAVORED_ENEMY_GIANT; + case RACIAL_TYPE_MAGICAL_BEAST: return FEAT_FAVORED_ENEMY_MAGICAL_BEAST; + case RACIAL_TYPE_OUTSIDER: return FEAT_FAVORED_ENEMY_OUTSIDER; + case RACIAL_TYPE_SHAPECHANGER: return FEAT_FAVORED_ENEMY_SHAPECHANGER; + case RACIAL_TYPE_UNDEAD: return FEAT_FAVORED_ENEMY_UNDEAD; + case RACIAL_TYPE_VERMIN: return FEAT_FAVORED_ENEMY_VERMIN; + case RACIAL_TYPE_PLANT: return FEAT_FAVORED_ENEMY_PLANT; + case RACIAL_TYPE_OOZE: return FEAT_FAVORED_ENEMY_OOZE; + } + return -1; +} + +int GetFavoredEnemyLevel(object oAttacker) +{ + return (GetLevelByClass(CLASS_TYPE_HARPER, oAttacker) + + GetLevelByClass(CLASS_TYPE_RANGER, oAttacker) + + GetLevelByClass(CLASS_TYPE_ULTIMATE_RANGER, oAttacker) // motu99: added ultimate ranger. might have to add more + // Additional PRC's + // + GetLevelByClass(CLASS_TYPE_*, oAttacker) + ); +} + +int GetFavoredEnemyDamageBonus(object oDefender, object oAttacker) +{ + int iRangerLevel = GetFavoredEnemyLevel(oAttacker); + + // Exit if the class can not have a favored enemy + // Prevents lots of useless code from running + if(!iRangerLevel) + return 0; + + int iDamageBonus = 0; + +// float fDistance = GetDistanceBetween(oAttacker, oDefender); +// if(fDistance <= FeetToMeters(30.0f) ) // motu99: Do you have to be within 30 feet to gain favorite enemy boni? +// { + int iRacialType = MyPRCGetRacialType(oDefender); + if(GetHasFeat(GetFavoredEnemyFeat(iRacialType), oAttacker)) + { + iDamageBonus = (iRangerLevel / 5) + 1; + // add in 2d6 damage for bane of enemies + if(GetHasFeat(FEAT_EPIC_BANE_OF_ENEMIES, oAttacker)) + iDamageBonus += d6(2); + } +// } + + return iDamageBonus; +} + +int GetMightyWeaponBonus(object oWeap) +{ + int iMighty = 0; + int iTemp = 0; + itemproperty ip = GetFirstItemProperty(oWeap); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_MIGHTY) + iTemp = GetItemPropertyCostTableValue(ip); + + if(iTemp > iMighty) iMighty = iTemp; + + ip = GetNextItemProperty(oWeap); + } + return iMighty; +} + +int GetWeaponEnhancement(object oWeapon, object oDefender, object oAttacker) +{ +// this function determines the maximum enhancement bonus from the weapon and subtracts the maximum penalty + int iEnhancement = 0; + int iPenalty = 0; + int iTemp; + + int iRace = MyPRCGetRacialType(oDefender); + + int iGoodEvil = GetAlignmentGoodEvil(oDefender); + int iLawChaos = GetAlignmentLawChaos(oDefender); + int iAlignSp = GetItemPropAlignment(iGoodEvil,iLawChaos); + int iAlignGr; + + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + iTemp = 0; + int iItemPropType = GetItemPropertyType(ip); + switch(iItemPropType) + { + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + iTemp = GetItemPropertyCostTableValue(ip); // motu99: was iTemp = GetDamageByConstant(GetItemPropertyCostTableValue(ip), TRUE); but this returned wrong values + break; + + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: + if(GetItemPropertySubType(ip) == iAlignSp) iTemp = GetItemPropertyCostTableValue(ip); + break; + + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + iAlignGr = GetItemPropertySubType(ip); + if (iAlignGr == ALIGNMENT_NEUTRAL) + { + if (iAlignGr == iLawChaos) iTemp = GetItemPropertyCostTableValue(ip); // motu99: was iTemp = GetDamageByConstant(GetItemPropertyCostTableValue(ip), TRUE); but that seemed as wrong as ITEM_PROPERTY_ENHANCEMENT_BONUS + } + else if (iAlignGr == iGoodEvil || iAlignGr == iLawChaos || iAlignGr == IP_CONST_ALIGNMENTGROUP_ALL) + iTemp = GetItemPropertyCostTableValue(ip); // motu99: was iTemp = GetDamageByConstant(GetItemPropertyCostTableValue(ip), TRUE); but that seemed as wrong as ITEM_PROPERTY_ENHANCEMENT_BONUS + break; + + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: + if(GetItemPropertySubType(ip) == iRace) iTemp = GetItemPropertyCostTableValue(ip); + break; + + // detects holy avenger property and adds proper enhancement bonus + case ITEM_PROPERTY_HOLY_AVENGER: + iTemp = 5; + break; + + case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER: + iTemp = -GetItemPropertyCostTableValue(ip); // motu99: was iTemp = -GetDamageByConstant(GetItemPropertyCostTableValue(ip), TRUE); but that seemed as wrong as ITEM_PROPERTY_ENHANCEMENT_BONUS + break; + + } + + if(iTemp > 0) + { + if (iTemp > iEnhancement) iEnhancement = iTemp; + } + else + { + if(iTemp < iPenalty) iPenalty = iTemp; + } + + ip = GetNextItemProperty(oWeapon); + } + + iEnhancement -= iPenalty; + + //if ranged check for ammo + if(GetWeaponRanged(oWeapon) ) + { + // Adds ammo bonus if it is higher than weapon bonus + int iAmmoEnhancement = GetAmmunitionEnhancement(oWeapon, oDefender, oAttacker); + if(iAmmoEnhancement > iEnhancement) iEnhancement = iAmmoEnhancement; + + // Arcane Archer Enchant Arrow Bonus + int iAALevel = GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER, oAttacker); + int iAAEnchantArrow = 0; + + if(iAALevel > 0) iAAEnchantArrow = ((iAALevel + 1) / 2); + if(iAAEnchantArrow > iEnhancement) iEnhancement = iAAEnchantArrow; + } + + return iEnhancement; +} + +int GetMonkEnhancement(object oWeapon, object oDefender, object oAttacker) +{ + int iMonkEnhancement = GetWeaponEnhancement(oWeapon, oDefender, oAttacker); + int iTemp; + + // returns enhancement bonus for ki strike + if(GetIsNaturalWeapon(oWeapon)) + { + if(GetHasFeat(FEAT_EPIC_IMPROVED_KI_STRIKE_5, oAttacker)) iTemp = 5; + else if(GetHasFeat(FEAT_EPIC_IMPROVED_KI_STRIKE_4, oAttacker)) iTemp = 4; + else if(GetHasFeat(FEAT_KI_STRIKE, oAttacker) ) + { + int iMonkLevel = GetLevelByClass(CLASS_TYPE_MONK, oAttacker); + iTemp = 1; + if(iMonkLevel > 12) iTemp = 2; + if(iMonkLevel > 15) iTemp = 3; + } + if(iTemp > iMonkEnhancement) iMonkEnhancement = iTemp; + } + + return iMonkEnhancement; +} + +int GetDamagePowerConstant(object oWeapon, object oDefender, object oAttacker) +{ +// motu99: call to GetMonkEnhancement is executed several times; first for attack bonus, then for enhancement, now for damage power +// better store the iEnhancement value (from attack bonus calculation) and use it later to determine damage + int iDamagePower = GetMonkEnhancement(oWeapon, oDefender, oAttacker); + + // Determine Damage Power (Enhancement Bonus of Weapon) + // Damage Power 6 is Magical and hits everything + // So for +6 and higher are actually 7-21, so add +1 + if(iDamagePower > 5) iDamagePower += 1; + if(iDamagePower < 0 ) iDamagePower = 0; + + //if (DEBUG) DoDebug("prc_inc_combat DamagePowerConstant "+IntToString(iDamagePower)); + + return iDamagePower; +} + +// motu99: Didn't check this +int GetAmmunitionEnhancement(object oWeapon, object oDefender, object oAttacker) +{ + int iTemp; + int iBonus, iDamageType; + int iType = GetBaseItemType(oWeapon); + + + int iRace = MyPRCGetRacialType(oDefender); + + int iGoodEvil = GetAlignmentGoodEvil(oDefender); + int iLawChaos = GetAlignmentLawChaos(oDefender); + int iAlignSp = GetItemPropAlignment(iGoodEvil, iLawChaos); + int iAlignGr; + + object oAmmu = GetAmmunitionFromWeapon(oWeapon, oAttacker); + int iBase = GetBaseItemType(oAmmu); + + //Get Damage Bonus Properties from oWeapon + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + int iCostVal = GetItemPropertyCostTableValue(ip); + int iPropType = GetItemPropertyType(ip); + if(iPropType == ITEM_PROPERTY_DAMAGE_BONUS) + { + iDamageType = GetItemPropertyParam1Value(ip); + + if( (iBase == BASE_ITEM_BOLT || iBase == BASE_ITEM_ARROW) && iDamageType == IP_CONST_DAMAGETYPE_PIERCING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + else if ( iBase == BASE_ITEM_BULLET && iDamageType == IP_CONST_DAMAGETYPE_BLUDGEONING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + } + if(iPropType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP) + { + iAlignGr = GetItemPropertySubType(ip); + iDamageType = GetItemPropertyParam1Value(ip); + + int bIsAttackingAlignment = FALSE; + + if (iAlignGr == ALIGNMENT_NEUTRAL) + { + if (iAlignGr == iLawChaos) bIsAttackingAlignment = TRUE; + } + else if (iAlignGr == iGoodEvil || iAlignGr == iLawChaos || iAlignGr == IP_CONST_ALIGNMENTGROUP_ALL) + bIsAttackingAlignment = FALSE; + + if(bIsAttackingAlignment) + { + if ( (iBase == BASE_ITEM_BOLT || iBase == BASE_ITEM_ARROW) && iDamageType == IP_CONST_DAMAGETYPE_PIERCING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + else if ( iBase == BASE_ITEM_BULLET && iDamageType == IP_CONST_DAMAGETYPE_BLUDGEONING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + } + } + if(iPropType == ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT) + { + if(GetItemPropertySubType(ip) == iAlignSp) iTemp = GetItemPropertyCostTableValue(ip); + else iTemp = 0; + + iDamageType = GetItemPropertyParam1Value(ip); + + if ( (iBase == BASE_ITEM_BOLT || iBase == BASE_ITEM_ARROW) && iDamageType == IP_CONST_DAMAGETYPE_PIERCING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + else if ( iBase == BASE_ITEM_BULLET && iDamageType == IP_CONST_DAMAGETYPE_BLUDGEONING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + } + if(iPropType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP) + { + if(GetItemPropertySubType(ip) == iRace) iTemp = GetItemPropertyCostTableValue(ip); + else iTemp = 0; iTemp = 0; + + iDamageType = GetItemPropertyParam1Value(ip); + + if ( (iBase == BASE_ITEM_BOLT || iBase == BASE_ITEM_ARROW) && iDamageType == IP_CONST_DAMAGETYPE_PIERCING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + else if ( iBase == BASE_ITEM_BULLET && iDamageType == IP_CONST_DAMAGETYPE_BLUDGEONING ) + { + iTemp = GetDamageByConstant(iCostVal, TRUE); + iBonus = iTemp> iBonus ? iTemp:iBonus ; + } + } + ip = GetNextItemProperty(oWeapon); + } + return iBonus; +} + +int GetDamageByConstant(int iDamageConst, int iItemProp) +{ + if(iItemProp) + { + switch(iDamageConst) + { + case IP_CONST_DAMAGEBONUS_1: + return 1; + case IP_CONST_DAMAGEBONUS_2: + return 2; + case IP_CONST_DAMAGEBONUS_3: + return 3; + case IP_CONST_DAMAGEBONUS_4: + return 4; + case IP_CONST_DAMAGEBONUS_5: + return 5; + case IP_CONST_DAMAGEBONUS_6: + return 6; + case IP_CONST_DAMAGEBONUS_7: + return 7; + case IP_CONST_DAMAGEBONUS_8: + return 8; + case IP_CONST_DAMAGEBONUS_9: + return 9; + case IP_CONST_DAMAGEBONUS_10: + return 10; + case IP_CONST_DAMAGEBONUS_11: + return 11; + case IP_CONST_DAMAGEBONUS_12: + return 12; + case IP_CONST_DAMAGEBONUS_13: + return 13; + case IP_CONST_DAMAGEBONUS_14: + return 14; + case IP_CONST_DAMAGEBONUS_15: + return 15; + case IP_CONST_DAMAGEBONUS_16: + return 16; + case IP_CONST_DAMAGEBONUS_17: + return 17; + case IP_CONST_DAMAGEBONUS_18: + return 18; + case IP_CONST_DAMAGEBONUS_19: + return 19; + case IP_CONST_DAMAGEBONUS_20: + return 20; + case IP_CONST_DAMAGEBONUS_1d4: + return d4(1); + case IP_CONST_DAMAGEBONUS_1d6: + return d6(1); + case IP_CONST_DAMAGEBONUS_1d8: + return d8(1); + case IP_CONST_DAMAGEBONUS_1d10: + return d10(1); + case IP_CONST_DAMAGEBONUS_1d12: + return d12(1); + case IP_CONST_DAMAGEBONUS_2d4: + return d4(2); + case IP_CONST_DAMAGEBONUS_2d6: + return d6(2); + case IP_CONST_DAMAGEBONUS_2d8: + return d8(2); + case IP_CONST_DAMAGEBONUS_2d10: + return d10(2); + case IP_CONST_DAMAGEBONUS_2d12: + return d12(2); + case IP_CONST_DAMAGEBONUS_3d4: + return d4(3); + case IP_CONST_DAMAGEBONUS_4d4: + return d4(4); + case IP_CONST_DAMAGEBONUS_5d4: + return d4(5); + case IP_CONST_DAMAGEBONUS_6d4: + return d4(6); + case IP_CONST_DAMAGEBONUS_7d4: + return d4(7); + case IP_CONST_DAMAGEBONUS_8d4: + return d4(8); + case IP_CONST_DAMAGEBONUS_9d4: + return d4(9); + case IP_CONST_DAMAGEBONUS_10d4: + return d4(10); + case IP_CONST_DAMAGEBONUS_3d6: + return d6(3); + case IP_CONST_DAMAGEBONUS_4d6: + return d6(4); + case IP_CONST_DAMAGEBONUS_5d6: + return d6(5); + case IP_CONST_DAMAGEBONUS_6d6: + return d6(6); + case IP_CONST_DAMAGEBONUS_7d6: + return d6(7); + case IP_CONST_DAMAGEBONUS_8d6: + return d6(8); + case IP_CONST_DAMAGEBONUS_9d6: + return d6(9); + case IP_CONST_DAMAGEBONUS_10d6: + return d6(10); + case IP_CONST_DAMAGEBONUS_3d8: + return d8(3); + case IP_CONST_DAMAGEBONUS_4d8: + return d8(4); + case IP_CONST_DAMAGEBONUS_5d8: + return d8(5); + case IP_CONST_DAMAGEBONUS_6d8: + return d8(6); + case IP_CONST_DAMAGEBONUS_7d8: + return d8(7); + case IP_CONST_DAMAGEBONUS_8d8: + return d8(8); + case IP_CONST_DAMAGEBONUS_9d8: + return d8(9); + case IP_CONST_DAMAGEBONUS_10d8: + return d8(10); + case IP_CONST_DAMAGEBONUS_3d10: + return d10(3); + case IP_CONST_DAMAGEBONUS_4d10: + return d10(4); + case IP_CONST_DAMAGEBONUS_5d10: + return d10(5); + case IP_CONST_DAMAGEBONUS_6d10: + return d10(6); + case IP_CONST_DAMAGEBONUS_7d10: + return d10(7); + case IP_CONST_DAMAGEBONUS_8d10: + return d10(8); + case IP_CONST_DAMAGEBONUS_9d10: + return d10(9); + case IP_CONST_DAMAGEBONUS_10d10: + return d10(10); + case IP_CONST_DAMAGEBONUS_3d12: + return d12(3); + case IP_CONST_DAMAGEBONUS_4d12: + return d12(4); + case IP_CONST_DAMAGEBONUS_5d12: + return d12(5); + case IP_CONST_DAMAGEBONUS_6d12: + return d12(6); + case IP_CONST_DAMAGEBONUS_7d12: + return d12(7); + case IP_CONST_DAMAGEBONUS_8d12: + return d12(8); + case IP_CONST_DAMAGEBONUS_9d12: + return d12(9); + case IP_CONST_DAMAGEBONUS_10d12: + return d12(10); + case IP_CONST_DAMAGEBONUS_1d3: + return d3(1); + case IP_CONST_DAMAGEBONUS_4d3: + return d3(4); + case IP_CONST_DAMAGEBONUS_21: + return 21; + case IP_CONST_DAMAGEBONUS_22: + return 22; + case IP_CONST_DAMAGEBONUS_23: + return 23; + case IP_CONST_DAMAGEBONUS_24: + return 24; + case IP_CONST_DAMAGEBONUS_25: + return 25; + case IP_CONST_DAMAGEBONUS_26: + return 26; + case IP_CONST_DAMAGEBONUS_27: + return 27; + case IP_CONST_DAMAGEBONUS_28: + return 28; + case IP_CONST_DAMAGEBONUS_29: + return 29; + case IP_CONST_DAMAGEBONUS_30: + return 30; + case IP_CONST_DAMAGEBONUS_31: + return 31; + case IP_CONST_DAMAGEBONUS_32: + return 32; + case IP_CONST_DAMAGEBONUS_33: + return 33; + case IP_CONST_DAMAGEBONUS_34: + return 34; + case IP_CONST_DAMAGEBONUS_35: + return 35; + case IP_CONST_DAMAGEBONUS_36: + return 36; + case IP_CONST_DAMAGEBONUS_37: + return 37; + case IP_CONST_DAMAGEBONUS_38: + return 38; + case IP_CONST_DAMAGEBONUS_39: + return 39; + case IP_CONST_DAMAGEBONUS_40: + return 40; + case IP_CONST_DAMAGEBONUS_41: + return 41; + case IP_CONST_DAMAGEBONUS_42: + return 42; + case IP_CONST_DAMAGEBONUS_43: + return 43; + case IP_CONST_DAMAGEBONUS_44: + return 44; + case IP_CONST_DAMAGEBONUS_45: + return 45; + case IP_CONST_DAMAGEBONUS_46: + return 46; + case IP_CONST_DAMAGEBONUS_47: + return 47; + case IP_CONST_DAMAGEBONUS_48: + return 48; + case IP_CONST_DAMAGEBONUS_49: + return 49; + case IP_CONST_DAMAGEBONUS_50: + return 50; + + } + } + else + { + switch(iDamageConst) + { + case DAMAGE_BONUS_1: + return 1; + case DAMAGE_BONUS_2: + return 2; + case DAMAGE_BONUS_3: + return 3; + case DAMAGE_BONUS_4: + return 4; + case DAMAGE_BONUS_5: + return 5; + case DAMAGE_BONUS_6: + return 6; + case DAMAGE_BONUS_7: + return 7; + case DAMAGE_BONUS_8: + return 8; + case DAMAGE_BONUS_9: + return 9; + case DAMAGE_BONUS_10: + return 10; + case DAMAGE_BONUS_11: + return 11; // motu99: The following up to DAMAGE_BONUS_20 all returned 10; doesn't seem right, changed it + case DAMAGE_BONUS_12: + return 12; + case DAMAGE_BONUS_13: + return 13; + case DAMAGE_BONUS_14: + return 14; + case DAMAGE_BONUS_15: + return 15; + case DAMAGE_BONUS_16: + return 16; + case DAMAGE_BONUS_17: + return 17; + case DAMAGE_BONUS_18: + return 18; + case DAMAGE_BONUS_19: + return 19; + case DAMAGE_BONUS_20: + return 20; + case DAMAGE_BONUS_1d4: + return d4(1); + case DAMAGE_BONUS_1d6: + return d6(1); + case DAMAGE_BONUS_1d8: + return d8(1); + case DAMAGE_BONUS_1d10: + return d10(1); + case DAMAGE_BONUS_1d12: + return d12(1); + case DAMAGE_BONUS_2d4: + return d4(2); + case DAMAGE_BONUS_2d6: + return d6(2); + case DAMAGE_BONUS_2d8: + return d8(2); + case DAMAGE_BONUS_2d10: + return d10(2); + case DAMAGE_BONUS_2d12: + return d12(2); + case DAMAGE_BONUS_21: + return 21; + case DAMAGE_BONUS_22: + return 22; + case DAMAGE_BONUS_23: + return 23; + case DAMAGE_BONUS_24: + return 24; + case DAMAGE_BONUS_25: + return 25; + case DAMAGE_BONUS_26: + return 26; + case DAMAGE_BONUS_27: + return 27; + case DAMAGE_BONUS_28: + return 28; + case DAMAGE_BONUS_29: + return 29; + case DAMAGE_BONUS_30: + return 30; + case DAMAGE_BONUS_31: + return 31; + case DAMAGE_BONUS_32: + return 32; + case DAMAGE_BONUS_33: + return 33; + case DAMAGE_BONUS_34: + return 34; + case DAMAGE_BONUS_35: + return 35; + case DAMAGE_BONUS_36: + return 36; + case DAMAGE_BONUS_37: + return 37; + case DAMAGE_BONUS_38: + return 38; + case DAMAGE_BONUS_39: + return 39; + case DAMAGE_BONUS_40: + return 40; + case DAMAGE_BONUS_41: + return 41; + case DAMAGE_BONUS_42: + return 42; + case DAMAGE_BONUS_43: + return 43; + case DAMAGE_BONUS_44: + return 44; + case DAMAGE_BONUS_45: + return 45; + case DAMAGE_BONUS_46: + return 46; + case DAMAGE_BONUS_47: + return 47; + case DAMAGE_BONUS_48: + return 48; + case DAMAGE_BONUS_49: + return 49; + case DAMAGE_BONUS_50: + return 50; + } + } + return 0; +} + +int GetDiceMaxRoll(int iDamageConst) +// Gets the maximum roll of the dice; zero if no dice constant +{ + switch(iDamageConst) + { + case IP_CONST_DAMAGEBONUS_1d4: + return 4; + case IP_CONST_DAMAGEBONUS_1d6: + return 6; + case IP_CONST_DAMAGEBONUS_1d8: + return 8; + case IP_CONST_DAMAGEBONUS_1d10: + return 10; + case IP_CONST_DAMAGEBONUS_1d12: + return 12; + case IP_CONST_DAMAGEBONUS_2d4: + return 8; + case IP_CONST_DAMAGEBONUS_2d6: + return 12; + case IP_CONST_DAMAGEBONUS_2d8: + return 16; + case IP_CONST_DAMAGEBONUS_2d10: + return 20; + case IP_CONST_DAMAGEBONUS_2d12: + return 24; + } + return 0; +} + +// all of the ten ip dice constants are supposed to lie between 6 and 15 +int GetIsDiceConstant(int iDice) +{ + return (iDice > 5 && iDice < 16); +} + + +// motu99: quite expensive: Passing the whole struct, filling in one single value, passing it out again +// practically disabled this function by pasting its code directly into GetWeaponBonusDamage() +struct BonusDamage GetItemPropertyDamageConstant(int iDamageType, int iDice, struct BonusDamage weapBonusDam) +{ + switch(iDamageType) + { + case -1: + break; + + case IP_CONST_DAMAGETYPE_ACID: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Acid) weapBonusDam.dice_Acid = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Acid) weapBonusDam.dam_Acid = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_COLD: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Cold) weapBonusDam.dice_Cold = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Cold) weapBonusDam.dam_Cold = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_FIRE: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Fire) weapBonusDam.dice_Fire = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Fire) weapBonusDam.dam_Fire = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_ELECTRICAL: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Elec) weapBonusDam.dice_Elec = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Elec) weapBonusDam.dam_Elec = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_SONIC: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Son) weapBonusDam.dice_Son = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Son) weapBonusDam.dam_Son = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_DIVINE: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Div) weapBonusDam.dice_Div = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Div) weapBonusDam.dam_Div = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_NEGATIVE: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Neg) weapBonusDam.dice_Neg = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Neg) weapBonusDam.dam_Neg = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_POSITIVE: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Pos) weapBonusDam.dice_Pos = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Pos) weapBonusDam.dam_Pos = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_MAGICAL: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Mag) weapBonusDam.dice_Mag = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Mag) weapBonusDam.dam_Mag = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_BLUDGEONING: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Blud) weapBonusDam.dice_Blud = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Blud) weapBonusDam.dam_Blud = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_PIERCING: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Pier) weapBonusDam.dice_Pier = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Pier) weapBonusDam.dam_Pier = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_SLASHING: + if(GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Slash) weapBonusDam.dice_Slash = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Slash) weapBonusDam.dam_Slash = iDice; + } + break; + } + + return weapBonusDam; +} + + + +// motu99: generally it does not make too much sense to precalculate the WeaponDamage on the beginning of the round and store the various damage types in a large struct +// the reason is that the calculation is quite cheap on CPU time (weapons don't have many item properties to loop through) +// so that passing the large struct back and forth between AttackLoopLogic() and AttackLoopMain() takes more time than the calculation itself + +// @TODO: change logic of AttackLoopLogic() such, that we calculate the damage directly in GetDamage() when we hit! +// then we need not store the dice constants, but rather do the rolls directly, merely summing up the real damage +// for the various damage types in one single struct (with half the members than here) +// also note, that some damage depends on the enemy (alignment, race etc.), so it can change during one round +struct BonusDamage GetWeaponBonusDamage(object oWeapon, object oTarget) +{ + struct BonusDamage weapBonusDam; // lets hope that everything is initialized to zero + + int iDamageType; + int iDice; + int iDamage; + int iDamageDarkfire=0; + int iDamageFlameWeapon=0; + + int iRace = MyPRCGetRacialType(oTarget); + + int iGoodEvil = GetAlignmentGoodEvil(oTarget); + int iLawChaos = GetAlignmentLawChaos(oTarget); + int iAlignSp = GetItemPropAlignment(iGoodEvil,iLawChaos); + int iAlignGr; + + int iSpellType; + + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + iDamageType = -1; // always reset damage type to -1 (no damage) + int ipType = GetItemPropertyType(ip); +// DoDebug("GetWeaponBonusDamage() found " + DebugIProp2Str(ip)); + switch(ipType) + { + // normal damage + case ITEM_PROPERTY_DAMAGE_BONUS: + iDice = GetItemPropertyCostTableValue(ip); + iDamageType = GetItemPropertySubType(ip); + break; + + // Checks weapon for Holy Avenger property + case ITEM_PROPERTY_HOLY_AVENGER: + iAlignGr = GetItemPropertySubType(ip); + if (iAlignGr == ALIGNMENT_EVIL) + { + iDamageType = IP_CONST_DAMAGETYPE_DIVINE; + iDice = IP_CONST_DAMAGEBONUS_1d6; + } + break; + + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + iAlignGr = GetItemPropertySubType(ip); + iDice = GetItemPropertyCostTableValue(ip); + if (iAlignGr == ALIGNMENT_NEUTRAL) + { + if (iAlignGr == iLawChaos) iDamageType = GetItemPropertyParam1Value(ip); + } + else if (iAlignGr == iGoodEvil || iAlignGr == iLawChaos || iAlignGr == IP_CONST_ALIGNMENTGROUP_ALL) + { + iDamageType = GetItemPropertyParam1Value(ip); + } + break; + + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + if(GetItemPropertySubType(ip) == iRace) + { + iDamageType = GetItemPropertyParam1Value(ip); + iDice = GetItemPropertyCostTableValue(ip); + } + break; + + case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT: + if(GetItemPropertySubType(ip) == iAlignSp) + { + iDamageType = GetItemPropertyParam1Value(ip); + iDice = GetItemPropertyCostTableValue(ip); + } + break; + + /* motu99: Apr 7, 2007: made onhitcastspell system work (at least for flame weapon and darkfire), so this is not needed here! + + case ITEM_PROPERTY_ONHITCASTSPELL: + iSpellType = GetItemPropertySubType(ip); + iDamage = GetItemPropertyCostTableValue(ip)+1; // spell level: always one to low +DoDebug("GetWeaponBonusDamage() found onhitcastspell with Spell type: " + IntToString(iSpellType) +" and spell level (ItemPropertyCostTableValue): " + IntToString(iDamage)); + switch(iSpellType) + { + // dark fire 1d6 + X dmg. X = CasterLevel/2 + case IP_CONST_ONHIT_CASTSPELL_ONHIT_DARKFIRE: + + // iDamageType = IP_CONST_DAMAGETYPE_FIRE; + // iDice = IP_CONST_DAMAGEBONUS_1d6; + + iDamage /= 2; + if(iDamage > 10) iDamage = 10; + + if(iDamage > iDamageDarkfire) iDamageDarkfire = iDamage; + break; + + // flame blade 1d4 + X dmg. X = CasterLevel/2; motu99: in Grimoire it says +1 per casterlevel + case IP_CONST_ONHIT_CASTSPELL_ONHIT_FIREDAMAGE: + + // iDamageType = IP_CONST_DAMAGETYPE_FIRE; + // iDice = IP_CONST_DAMAGEBONUS_1d4; + + +// iDamage /= 2; // motu99: changed this, because it should be +1 per casterlevel according to Grimoire + if(iDamage > 10) iDamage = 10; + + if(iDamage > iDamageFlameWeapon) iDamageFlameWeapon = iDamage; + break; + } + +*/ // motu99: end ONHITCASTSPELL (for Darkfire and Flame Weapon) + + } + + // weapBonusDam = GetItemPropertyDamageConstant(iDamageType, iDice, weapBonusDam); // motu99: don't need this any more, because we fill in the struct here + + // before we look for another itemproperty we fill in the the struct + //to find the right struct subelement we check iDamageType; the amount of damage is found in iDice + // for any damage of the same type, we take the maximum damage + // fire damage from flame weapon and darkfire are treated in a special way at the end of the function. + switch (iDamageType) + { + case -1: + break; + + case IP_CONST_DAMAGETYPE_SLASHING: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Slash) weapBonusDam.dice_Slash = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Slash) weapBonusDam.dam_Slash = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_PIERCING: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Pier) weapBonusDam.dice_Pier = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Pier) weapBonusDam.dam_Pier = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_BLUDGEONING: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Blud) weapBonusDam.dice_Blud = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Blud) weapBonusDam.dam_Blud = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_FIRE: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Fire) weapBonusDam.dice_Fire = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Fire) weapBonusDam.dam_Fire = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_ELECTRICAL: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Elec) weapBonusDam.dice_Elec = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Elec) weapBonusDam.dam_Elec = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_COLD: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Cold) weapBonusDam.dice_Cold = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Cold) weapBonusDam.dam_Cold = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_ACID: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Acid) weapBonusDam.dice_Acid = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Acid) weapBonusDam.dam_Acid = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_SONIC: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Son) weapBonusDam.dice_Son = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Son) weapBonusDam.dam_Son = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_DIVINE: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Div) weapBonusDam.dice_Div = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Div) weapBonusDam.dam_Div = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_NEGATIVE: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Neg) weapBonusDam.dice_Neg = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Neg) weapBonusDam.dam_Neg = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_POSITIVE: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Pos) weapBonusDam.dice_Pos = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Pos) weapBonusDam.dam_Pos = iDice; + } + break; + + case IP_CONST_DAMAGETYPE_MAGICAL: + if (GetIsDiceConstant(iDice)) // is a dice constant + { + if(iDice > weapBonusDam.dice_Mag) weapBonusDam.dice_Mag = iDice; + } + else // is +1 to +20 + { + if(iDice > weapBonusDam.dam_Mag) weapBonusDam.dam_Mag = iDice; + } + break; + + } + + ip = GetNextItemProperty(oWeapon); + } + // we are through + // now add flame weapon and darkfire damage +/* +// motu99: Not using this any more, rather using prc_onhitcast to do any onhitcast spells on the weapon + int bStack = GetPRCSwitch(PRC_FLAMEWEAPON_DARKFIRE_STACK); + + if(bStack) + iDamage = iDamageDarkfire + iDamageFlameWeapon; + else + // otherwise we take the maximum + iDamage = PRCMax(iDamageDarkfire, iDamageFlameWeapon); + if(iDamage) + { + // now either (if stacking) add the spell's fire damage to any other fire damage that is already on the weapon, + // or take the maximum of all fire damage types on the weapon(if not stacking) + if(bStack) + iDamage += GetDamageByConstant(weapBonusDam.dam_Fire, TRUE); + else + iDamage = PRCMax(GetDamageByConstant(weapBonusDam.dam_Fire, TRUE), iDamage); + + if(iDamage > 20) iDamage = 20; // make sure that the damage does not exceed 20 + // convert integer damage back to DamageBonusConstant + iDamage = IPGetDamageBonusConstantFromNumber(iDamage); + weapBonusDam.dam_Fire = iDamage; + + // check if there is already a dice constant for "normal" fire damage and find out the maximum dice roll + iDice = GetDiceMaxRoll(weapBonusDam.dice_Fire); + if(bStack) + { // calculate the maximum of all three dice rolls (flame weapon damage, darkfire damage, normal fire damage) + if (iDamageDarkfire) iDice += 6; + if (iDamageFlameWeapon) iDice += 4; + // now take the dice roll that is closest to the combined dice roll of all three + if (iDice < 5) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_1d4; + else if (iDice < 7) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_1d6; + else if (iDice < 9) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_2d4; + else if (iDice < 11) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_1d10; + else if (iDice < 13) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_2d6; + else if (iDice < 17) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_2d8; + else if (iDice < 21) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_2d10; + else weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_2d12; + } + else + { // if the Darkfire d6 dice is highest, take that, otherwise if the flameweapon d4 dice is highest, take that + if(iDamageDarkfire && iDice < 6) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_1d6; + else if (iDamageFlameWeapon && iDice < 4) weapBonusDam.dice_Fire = IP_CONST_DAMAGEBONUS_1d4; + } + } +*/ + return weapBonusDam; +} + +// finds any ITEM_PROPERTY_MONSTER_DAMAGE on the weapon and returns the dice number and sides for this type of damage +// note that this returns the first such damage property. Won't work when there are several ITEM_PROPERTY_MONSTER_DAMAGE properties +// on the weapon (this should not happen, really) +struct Dice GetWeaponMonsterDamage(object oWeapon) +{ + struct Dice sDice; // lets hope that everything is initialized to zero + + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + int ipType = GetItemPropertyType(ip); + if (ipType == ITEM_PROPERTY_MONSTER_DAMAGE) + { +// DoDebug("GetWeaponMonsterDamage() found " + DebugIProp2Str(ip)); + int iDamage = GetItemPropertyCostTableValue(ip); + sDice.iSides = StringToInt(Get2DACache("iprp_monstcost", "Die", iDamage)); + sDice.iNum = StringToInt(Get2DACache("iprp_monstcost", "NumDice", iDamage)); + return sDice; + } + ip = GetNextItemProperty(oWeapon); + } + + return sDice; +} + +// contrary to GetWeaponBonusDamage this usually remains constant during the round (unless the spells happen to expire in that round, or are dispelled) +struct BonusDamage GetMagicalBonusDamage(object oAttacker, object oTarget) +{ +// this searches for spell effects on the attacker that increase damage +// note that these stack, because they are from different spells (you shouldn't be able to cast a spell of the same type at you twice) +// contrary to GetWeaponBonusDamage this does not fill in the IP damage bonus constants, but directly fills in the damage +// @TODO: research if all damage affecting spells are covered; might also look if there is a better way to find out the caster level of the effect creator, in particular if we are casting from scrolls or runes +// note that this does not look for alignment or race specific effects. I think I recall that some spells increase damage only versus specific enemies +// @TODO: check for any such spells and add them to this function +//ebonfowl: this function has been updated to work automatically, it will now capture all damage effects, get their values and damage types, and add the appropriate bonus/decrement to the struct + + struct BonusDamage spellBonusDam; + + effect eEffect; + int nDamage, eType, eSpellID, nEssentia, nBonus, nDamageType; + string sTag; + + object oCaster; + int nCharismaBonus, nLvl; + + eEffect = GetFirstEffect(oAttacker); + while (GetIsEffectValid(eEffect) ) + { + eType = GetEffectType(eEffect); + sTag = GetEffectTag(eEffect); + + int iRace = MyPRCGetRacialType(oTarget); + + int iEffectRace = GetEffectInteger(eEffect, 2); + + int iGoodEvil = GetAlignmentGoodEvil(oTarget); + int iLawChaos = GetAlignmentLawChaos(oTarget); + + int iEffectLawChaos = GetEffectInteger(eEffect, 3); + int iEffectGoodEvil = GetEffectInteger(eEffect, 4); + + if (eType == EFFECT_TYPE_DAMAGE_INCREASE) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based damage and load into struct + nBonus = GetEffectInteger(eEffect, 0); + nDamage = GetDamageByConstant(nBonus, FALSE); + nDamageType = GetEffectInteger(eEffect, 1); + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + spellBonusDam.dam_Acid += nDamage; + break; + + case DAMAGE_TYPE_BASE_WEAPON: + spellBonusDam.dam_Slash += nDamage; + break; + + case DAMAGE_TYPE_BLUDGEONING: + spellBonusDam.dam_Blud += nDamage; + break; + + case DAMAGE_TYPE_COLD: + spellBonusDam.dam_Cold += nDamage; + break; + + case DAMAGE_TYPE_DIVINE: + spellBonusDam.dam_Div += nDamage; + break; + + case DAMAGE_TYPE_ELECTRICAL: + spellBonusDam.dam_Elec += nDamage; + break; + + case DAMAGE_TYPE_FIRE: + spellBonusDam.dam_Fire += nDamage; + break; + + case DAMAGE_TYPE_MAGICAL: + spellBonusDam.dam_Mag += nDamage; + break; + + case DAMAGE_TYPE_NEGATIVE: + spellBonusDam.dam_Neg += nDamage; + break; + + case DAMAGE_TYPE_PIERCING: + spellBonusDam.dam_Pier += nDamage; + break; + + case DAMAGE_TYPE_POSITIVE: + spellBonusDam.dam_Pos += nDamage; + break; + + case DAMAGE_TYPE_SLASHING: + spellBonusDam.dam_Slash += nDamage; + break; + + case DAMAGE_TYPE_SONIC: + spellBonusDam.dam_Son += nDamage; + break; + } + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based damage and load into struct + nBonus = GetEffectInteger(eEffect, 0); + nDamage = GetDamageByConstant(nBonus, FALSE); + nDamageType = GetEffectInteger(eEffect, 1); + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + spellBonusDam.dam_Acid += nDamage; + break; + + case DAMAGE_TYPE_BASE_WEAPON: + spellBonusDam.dam_Slash += nDamage; + break; + + case DAMAGE_TYPE_BLUDGEONING: + spellBonusDam.dam_Blud += nDamage; + break; + + case DAMAGE_TYPE_COLD: + spellBonusDam.dam_Cold += nDamage; + break; + + case DAMAGE_TYPE_DIVINE: + spellBonusDam.dam_Div += nDamage; + break; + + case DAMAGE_TYPE_ELECTRICAL: + spellBonusDam.dam_Elec += nDamage; + break; + + case DAMAGE_TYPE_FIRE: + spellBonusDam.dam_Fire += nDamage; + break; + + case DAMAGE_TYPE_MAGICAL: + spellBonusDam.dam_Mag += nDamage; + break; + + case DAMAGE_TYPE_NEGATIVE: + spellBonusDam.dam_Neg += nDamage; + break; + + case DAMAGE_TYPE_PIERCING: + spellBonusDam.dam_Pier += nDamage; + break; + + case DAMAGE_TYPE_POSITIVE: + spellBonusDam.dam_Pos += nDamage; + break; + + case DAMAGE_TYPE_SLASHING: + spellBonusDam.dam_Slash += nDamage; + break; + + case DAMAGE_TYPE_SONIC: + spellBonusDam.dam_Son += nDamage; + break; + } + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic damage and load into struct + nBonus = GetEffectInteger(eEffect, 0); + nDamage = GetDamageByConstant(nBonus, FALSE); + nDamageType = GetEffectInteger(eEffect, 1); + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + spellBonusDam.dam_Acid += nDamage; + break; + + case DAMAGE_TYPE_BASE_WEAPON: + spellBonusDam.dam_Slash += nDamage; + break; + + case DAMAGE_TYPE_BLUDGEONING: + spellBonusDam.dam_Blud += nDamage; + break; + + case DAMAGE_TYPE_COLD: + spellBonusDam.dam_Cold += nDamage; + break; + + case DAMAGE_TYPE_DIVINE: + spellBonusDam.dam_Div += nDamage; + break; + + case DAMAGE_TYPE_ELECTRICAL: + spellBonusDam.dam_Elec += nDamage; + break; + + case DAMAGE_TYPE_FIRE: + spellBonusDam.dam_Fire += nDamage; + break; + + case DAMAGE_TYPE_MAGICAL: + spellBonusDam.dam_Mag += nDamage; + break; + + case DAMAGE_TYPE_NEGATIVE: + spellBonusDam.dam_Neg += nDamage; + break; + + case DAMAGE_TYPE_PIERCING: + spellBonusDam.dam_Pier += nDamage; + break; + + case DAMAGE_TYPE_POSITIVE: + spellBonusDam.dam_Pos += nDamage; + break; + + case DAMAGE_TYPE_SLASHING: + spellBonusDam.dam_Slash += nDamage; + break; + + case DAMAGE_TYPE_SONIC: + spellBonusDam.dam_Son += nDamage; + break; + } + } + +/* Remove this to re-enable the functions you commented out -ebonfowl + eSpellID = GetEffectSpellId(eEffect); + + switch(eSpellID) + { + case SPELL_PRAYER: + spellBonusDam.dam_Slash += 1; + break; + + case SPELL_WAR_CRY: + spellBonusDam.dam_Slash += 2; + break; + + case SPELL_BATTLETIDE: + spellBonusDam.dam_Mag += 2; + break; + + // Bard Song + case SPELL_BARD_SONG: + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + nDamage = 1; + if (GetIsObjectValid(oCaster)) + { + int nLvl = GetLevelByClass(CLASS_TYPE_BARD, oCaster); + int iPerform = GetSkillRank(SKILL_PERFORM, oCaster); + + if (nLvl>=BARD_LEVEL_FOR_BARD_SONG_DAM_3 && iPerform>= BARD_PERFORM_SKILL_FOR_BARD_SONG_DAM_3) + nDamage = 3; + else if (nLvl>= BARD_LEVEL_FOR_BARD_SONG_DAM_2 && iPerform>= BARD_PERFORM_SKILL_FOR_BARD_SONG_DAM_2) + nDamage = 2; + } + spellBonusDam.dam_Blud += nDamage; + break; + + case SPELL_DIVINE_MIGHT: + // divine damage + // find out the caster (should be the attacker, but beware of runes) + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + nDamage = 1 + GetHasFeat(FEAT_EPIC_DIVINE_MIGHT, oCaster); + nCharismaBonus = GetAbilityModifier(ABILITY_CHARISMA,oCaster) * nDamage; + + if(nCharismaBonus > 1) nDamage = nCharismaBonus; + else nDamage = 1; + + spellBonusDam.dam_Div += nDamage; + break; + + // Divine Wrath + case SPELLABILITY_DC_DIVINE_WRATH: + // magical damage + // here the caster must be the attacker (could not be cast on a rune) + oCaster = oAttacker; + + nDamage = 3; + nLvl = GetLevelByClass(CLASS_TYPE_DIVINECHAMPION, oCaster); + nLvl = (nLvl / 5)-1; // motu99: didn't check this + + if (nLvl > 6) nDamage = 15; + else if (nLvl > 5) nDamage = 12; + else if (nLvl > 4) nDamage = 10; + else if (nLvl > 3) nDamage = 8; + else if (nLvl > 2) nDamage = 6; + else if (nLvl > 1) nDamage = 4; + + spellBonusDam.dam_Mag += nDamage; + break; + + case SPELL_DIVINE_FAVOR: + // divine + // find out the caster (should be the attacker, but beware of runes) + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + nDamage = 1; + if (GetIsObjectValid(oCaster)) + { + nLvl = GetLevelByTypeDivine(oCaster); // GetLevelByClass(CLASS_TYPE_PALADIN, oAttacker) + GetLevelByClass(CLASS_TYPE_CLERIC, oAttacker); + nLvl /= 3; + + if(nLvl > 4) nLvl = 4; + } + nDamage += nLvl; + spellBonusDam.dam_Div += nDamage; + break; + + // Power Shot + case SPELL_PA_POWERSHOT: + spellBonusDam.dam_Pier += 5; + break; + + case SPELL_PA_IMP_POWERSHOT: + spellBonusDam.dam_Pier += 10; + break; + + case SPELL_PA_SUP_POWERSHOT: + spellBonusDam.dam_Pier += 15; + break; + + case MELD_BLUESTEEL_BRACERS: + nDamage = GetEssentiaInvested(oAttacker, MELD_BLUESTEEL_BRACERS); + spellBonusDam.dam_Mag += nDamage; + break; + + case MELD_LUCKY_DICE: + spellBonusDam.dam_Slash += 1; + break; + + case MELD_BLOODWAR_GAUNTLETS: + nDamage = GetEssentiaInvested(oAttacker, MELD_BLOODWAR_GAUNTLETS); + spellBonusDam.dam_Slash += nDamage; + break; + + case MELD_IRONSOUL_WEAPON: + nEssentia = GetEssentiaInvested(oAttacker, MELD_IRONSOUL_WEAPON); + nDamage = nEssentia*2; + spellBonusDam.dam_Blud += nDamage; + break; + + case MELD_INCANDESCENT_STRIKE: + nDamage = GetEssentiaInvested(oAttacker, MELD_INCANDESCENT_STRIKE); + spellBonusDam.dam_Slash += nDamage; + break; + + case MELD_INCARNUM_RADIANCE: + nLvl = GetLevelByClass(CLASS_TYPE_INCARNATE, oAttacker); + nBonus = 1 + nLvl/5; + nDamage = nBonus*2; + spellBonusDam.dam_Slash += nDamage; + break; + + case MELD_BLADEMELD_HANDS: + spellBonusDam.dam_Pos += 1; + break; + } + + //General effect damage bonuses requiring tags go here + //ebonfowl + + if(sTag == "LolthsMeatBonus") + { + spellBonusDam.dam_Div += 1; + } + else if(sTag == "RageClawsBonus") + { + spellBonusDam.dam_Slash += 2; + } + else if(sTag == "RidingBracersBonus") + { + spellBonusDam.dam_Slash += 2; + } + else if(sTag == "TotemAvatarBonus") + { + nEssentia = GetEssentiaInvested(oAttacker, MELD_TOTEM_AVATAR); + nDamage = 0; + if (GetIsMeldBound(oAttacker, MELD_TOTEM_AVATAR) == CHAKRA_SHOULDERS) nDamage += 2; + if (GetIsMeldBound(oAttacker, MELD_TOTEM_AVATAR) == CHAKRA_TOTEM) nDamage += nEssentia; + spellBonusDam.dam_Slash += nDamage; + } + else if(sTag == "MaulingGauntletsBonus") + { + nEssentia = GetEssentiaInvested(oAttacker, MELD_MAULING_GAUNTLETS); + nDamage = nEssentia*2; + spellBonusDam.dam_Slash += nDamage; + } + + //Now we are checking for damage bonuses versus specific racial types and adding those + //Any damage bonus effect versus racial types should go here + //If the effect can be identified by SpellID place it in the switch + //Otherwise tag the effect in the relevant script and identify it below the switch in the conditional + //ebonfowl + + int iRace = MyPRCGetRacialType(oTarget); + + int iEffectRace = GetEffectInteger(eEffect, 2); + + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + switch(eSpellID) + { + case MELD_NECROCARNUM_WEAPON: + nEssentia = GetEssentiaInvested(oAttacker, MELD_NECROCARNUM_WEAPON); + nDamage = FloatToInt(nEssentia*1.5); + spellBonusDam.dam_Neg += nDamage; + break; + + case MELD_HEART_OF_FIRE: + nEssentia = GetEssentiaInvested(oAttacker, MELD_HEART_OF_FIRE); + nDamage = nEssentia; + spellBonusDam.dam_Slash += nDamage; + break; + + case MELD_APPARITION_RIBBON: + nEssentia = GetEssentiaInvested(oAttacker, MELD_APPARITION_RIBBON); + nDamage = nEssentia*2; + spellBonusDam.dam_Slash += nDamage; + break; + } + + //Conditional for tagged racial damage effects + + if(sTag == "AzureEnmityBonus") + { + nEssentia = GetEssentiaInvestedFeat(oAttacker, FEAT_AZURE_ENMITY); + nDamage = nEssentia; + spellBonusDam.dam_Slash += nDamage; + } + } + } + + //Now we are checking for damage bonuses versus specific alignments and adding those + //Any damage bonus effect versus alignments should go here + //If the effect can be identified by SpellID place it in the switch + //Otherwise tag the effect in the relevant script and identify it below the switch in the conditional + //ebonfowl + + int iGoodEvil = GetAlignmentGoodEvil(oTarget); + int iLawChaos = GetAlignmentLawChaos(oTarget); + + int iEffectLawChaos = GetEffectInteger(eEffect, 3); + int iEffectGoodEvil = GetEffectInteger(eEffect, 4); + + //Single alignment effects here + + if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + switch(eSpellID) + { + case MELD_BLADEMELD_SOUL: + nDamage = d6(1); + spellBonusDam.dam_Slash += nDamage; + break; + + case MELD_OPEN_SOUL_CHAKRA: + spellBonusDam.dam_Pos += 2; + break; + } + + //Conditional for tagged alignment damage effects + } + } + + //Effects that have the potential to double-dip on alignment go here + + + /* + // prevents power shot and power attack from stacking + if(!GetHasFeatEffect(FEAT_PA_POWERSHOT, oAttacker) && + !GetHasFeatEffect(FEAT_PA_IMP_POWERSHOT, oAttacker) && + !GetHasFeatEffect(FEAT_PA_SUP_POWERSHOT, oAttacker) ) + { + switch(eSpellID) + { + case SPELL_POWER_ATTACK10: + spellBonusDam.dam_Slash += 10; + break; + case SPELL_POWER_ATTACK9: + spellBonusDam.dam_Slash += 9; + break; + case SPELL_POWER_ATTACK8: + spellBonusDam.dam_Slash += 8; + break; + case SPELL_POWER_ATTACK7: + spellBonusDam.dam_Slash += 7; + break; + case SPELL_POWER_ATTACK6: + spellBonusDam.dam_Slash += 6; + break; + case SPELL_POWER_ATTACK5: + spellBonusDam.dam_Slash += 5; + break; + case SPELL_POWER_ATTACK4: + spellBonusDam.dam_Slash += 4; + break; + case SPELL_POWER_ATTACK3: + spellBonusDam.dam_Slash += 3; + break; + case SPELL_POWER_ATTACK2: + spellBonusDam.dam_Slash += 2; + break; + case SPELL_POWER_ATTACK1: + spellBonusDam.dam_Slash += 1; + break; + case SPELL_SUPREME_POWER_ATTACK: + spellBonusDam.dam_Slash += 20; + break; + } + } + */ + + } + else if (eType == EFFECT_TYPE_DAMAGE_DECREASE) + { + if(iEffectRace != 28) + { + if(iEffectRace == iRace) + { + //Define racial type-based damage and load into struct + nBonus = GetEffectInteger(eEffect, 0); + nDamage = GetDamageByConstant(nBonus, FALSE); + nDamageType = GetEffectInteger(eEffect, 1); + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + spellBonusDam.dam_Acid -= nDamage; + break; + + case DAMAGE_TYPE_BASE_WEAPON: + spellBonusDam.dam_Slash -= nDamage; + break; + + case DAMAGE_TYPE_BLUDGEONING: + spellBonusDam.dam_Blud -= nDamage; + break; + + case DAMAGE_TYPE_COLD: + spellBonusDam.dam_Cold -= nDamage; + break; + + case DAMAGE_TYPE_DIVINE: + spellBonusDam.dam_Div -= nDamage; + break; + + case DAMAGE_TYPE_ELECTRICAL: + spellBonusDam.dam_Elec -= nDamage; + break; + + case DAMAGE_TYPE_FIRE: + spellBonusDam.dam_Fire -= nDamage; + break; + + case DAMAGE_TYPE_MAGICAL: + spellBonusDam.dam_Mag -= nDamage; + break; + + case DAMAGE_TYPE_NEGATIVE: + spellBonusDam.dam_Neg -= nDamage; + break; + + case DAMAGE_TYPE_PIERCING: + spellBonusDam.dam_Pier -= nDamage; + break; + + case DAMAGE_TYPE_POSITIVE: + spellBonusDam.dam_Pos -= nDamage; + break; + + case DAMAGE_TYPE_SLASHING: + spellBonusDam.dam_Slash -= nDamage; + break; + + case DAMAGE_TYPE_SONIC: + spellBonusDam.dam_Son -= nDamage; + break; + } + } + } + else if(iEffectLawChaos != 0 || iEffectGoodEvil != 0) + { + if(iEffectLawChaos == iLawChaos || iEffectGoodEvil == iGoodEvil) + { + //Define alignment-based damage and load into struct + nBonus = GetEffectInteger(eEffect, 0); + nDamage = GetDamageByConstant(nBonus, FALSE); + nDamageType = GetEffectInteger(eEffect, 1); + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + spellBonusDam.dam_Acid -= nDamage; + break; + + case DAMAGE_TYPE_BASE_WEAPON: + spellBonusDam.dam_Slash -= nDamage; + break; + + case DAMAGE_TYPE_BLUDGEONING: + spellBonusDam.dam_Blud -= nDamage; + break; + + case DAMAGE_TYPE_COLD: + spellBonusDam.dam_Cold -= nDamage; + break; + + case DAMAGE_TYPE_DIVINE: + spellBonusDam.dam_Div -= nDamage; + break; + + case DAMAGE_TYPE_ELECTRICAL: + spellBonusDam.dam_Elec -= nDamage; + break; + + case DAMAGE_TYPE_FIRE: + spellBonusDam.dam_Fire -= nDamage; + break; + + case DAMAGE_TYPE_MAGICAL: + spellBonusDam.dam_Mag -= nDamage; + break; + + case DAMAGE_TYPE_NEGATIVE: + spellBonusDam.dam_Neg -= nDamage; + break; + + case DAMAGE_TYPE_PIERCING: + spellBonusDam.dam_Pier -= nDamage; + break; + + case DAMAGE_TYPE_POSITIVE: + spellBonusDam.dam_Pos -= nDamage; + break; + + case DAMAGE_TYPE_SLASHING: + spellBonusDam.dam_Slash -= nDamage; + break; + + case DAMAGE_TYPE_SONIC: + spellBonusDam.dam_Son -= nDamage; + break; + } + } + } + else if(iEffectLawChaos == 0 && iEffectGoodEvil == 0 && iEffectRace == 28) + { + //Define generic damage and load into struct + nBonus = GetEffectInteger(eEffect, 0); + nDamage = GetDamageByConstant(nBonus, FALSE); + nDamageType = GetEffectInteger(eEffect, 1); + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + spellBonusDam.dam_Acid -= nDamage; + break; + + case DAMAGE_TYPE_BASE_WEAPON: + spellBonusDam.dam_Slash -= nDamage; + break; + + case DAMAGE_TYPE_BLUDGEONING: + spellBonusDam.dam_Blud -= nDamage; + break; + + case DAMAGE_TYPE_COLD: + spellBonusDam.dam_Cold -= nDamage; + break; + + case DAMAGE_TYPE_DIVINE: + spellBonusDam.dam_Div -= nDamage; + break; + + case DAMAGE_TYPE_ELECTRICAL: + spellBonusDam.dam_Elec -= nDamage; + break; + + case DAMAGE_TYPE_FIRE: + spellBonusDam.dam_Fire -= nDamage; + break; + + case DAMAGE_TYPE_MAGICAL: + spellBonusDam.dam_Mag -= nDamage; + break; + + case DAMAGE_TYPE_NEGATIVE: + spellBonusDam.dam_Neg -= nDamage; + break; + + case DAMAGE_TYPE_PIERCING: + spellBonusDam.dam_Pier -= nDamage; + break; + + case DAMAGE_TYPE_POSITIVE: + spellBonusDam.dam_Pos -= nDamage; + break; + + case DAMAGE_TYPE_SLASHING: + spellBonusDam.dam_Slash -= nDamage; + break; + + case DAMAGE_TYPE_SONIC: + spellBonusDam.dam_Son -= nDamage; + break; + } + } + + /* + switch(eSpellID) + { + case SPELLABILITY_HOWL_DOOM: + case SPELLABILITY_GAZE_DOOM: + case SPELL_DOOM: + spellBonusDam.dam_Mag -= 2; + break; + + case SPELL_GHOUL_TOUCH: + spellBonusDam.dam_Mag -= 2; + break; + + case SPELL_BATTLETIDE: + spellBonusDam.dam_Mag -= 2; + break; + + case SPELL_PRAYER: + spellBonusDam.dam_Slash -= 1; + break; + + case SPELL_SCARE: + spellBonusDam.dam_Mag -= 2; + break; + + // Hell Inferno + case SPELL_HELLINFERNO_2: + spellBonusDam.dam_Mag -= 4; + break; + + // Curse Song + case SPELL_BARD_CURSE_SONG: + // find out the caster (a bard in the vicinity, but beware of runes) + oCaster = GetEffectCreator(eEffect); + if(!GetIsObjectValid(oCaster)) // if we cannot find the caster, we assume the attacker was the caster + oCaster = oAttacker; + + nDamage = 1; + + if (GetIsObjectValid(oCaster)) + { + nLvl = GetLevelByClass(CLASS_TYPE_BARD, oCaster); + int iPerform = GetSkillRank(SKILL_PERFORM, oCaster); + + if (nLvl>=BARD_LEVEL_FOR_BARD_SONG_DAM_3 && iPerform>= BARD_PERFORM_SKILL_FOR_BARD_SONG_DAM_3) + nDamage = 3; + else if (nLvl>= BARD_LEVEL_FOR_BARD_SONG_DAM_2 && iPerform>= BARD_PERFORM_SKILL_FOR_BARD_SONG_DAM_2) + nDamage = 2; + } + spellBonusDam.dam_Blud -= nDamage; + } + */ + } + eEffect = GetNextEffect(oAttacker); + } + return spellBonusDam; +} + +// motu99: This partially depends on the defender, which might change during a round. But usually it is only calculated once at beginning of round +int GetWeaponDamagePerRound(object oDefender, object oAttacker, object oWeap, int iOffhand = 0) +{ + string sDebugMessage = PRC_TEXT_WHITE; + int bDebug = GetPRCSwitch(PRC_COMBAT_DEBUG); + + int iDamage = 0; + int iWeaponType = GetBaseItemType(oWeap); + + int iStr = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); + + // ranged weapon specific rules + if(GetIsRangedWeaponType(iWeaponType)) + { + // add mighty weapon strength damage + int iMighty = GetMightyWeaponBonus(oWeap); + if(iMighty > 0) + { + if(iStr > iMighty) iStr = iMighty; + + iDamage += iStr; + if (bDebug) sDebugMessage += "Mighty (" + IntToString(iStr) + ")"; + } + } + // melee weapon rules + else + { + // double str bonus to damage + if(GetIsTwoHandedMeleeWeaponType(iWeaponType)) + iStr += iStr/2; + + // off-hand weapons deal half str bonus + if(iOffhand) + iStr /= 2; + + iDamage += iStr; + + if (bDebug) sDebugMessage += "Str Bonus (" + IntToString(iStr) + ")"; + + // Handle the damage bonus from PRC Power Attack + iDamage += GetLocalInt(oAttacker, "PRC_PowerAttack_DamageBonus"); + } + + // weapon specializations + int iSpecializationBonus = 0; + + // determine the feat constants appropriate for the weapon base type + struct WeaponFeat sWeaponFeat = GetAllFeatsOfWeaponType(iWeaponType); + + // check for specialization feat + if(GetHasFeat(sWeaponFeat.Specialization, oAttacker)) + { + iSpecializationBonus += 2; + + // now check for epic specialization feat, can only have it, if we already have specialization + // +4 of epic specialization stacks with "normal" specialization + if(GetHasFeat(sWeaponFeat.EpicSpecialization, oAttacker)) + iSpecializationBonus += 4; + } + + iDamage += iSpecializationBonus; + if (bDebug) sDebugMessage += " + WeapSpec (" + IntToString(iSpecializationBonus) + ")"; + + // adds weapon enhancement bonus to damage + int iEnhancement = GetWeaponEnhancement(oWeap, oDefender, oAttacker); // motu: we are calling this quite often (for attack rolls, for damage, etc); better call it once at beginning of round and remember + iDamage += iEnhancement; + if (bDebug) sDebugMessage += " + WeapEnh (" + IntToString(iEnhancement) + ")"; + + // support for power attack and expertise modes + int iCombatMode = GetLastAttackMode(oAttacker); + if( iCombatMode == COMBAT_MODE_POWER_ATTACK /*&& + !GetHasSpellEffect(SPELL_SUPREME_POWER_ATTACK) && + !GetHasSpellEffect(SPELL_POWER_ATTACK10) && + !GetHasSpellEffect(SPELL_POWER_ATTACK9) && + !GetHasSpellEffect(SPELL_POWER_ATTACK8) && + !GetHasSpellEffect(SPELL_POWER_ATTACK7) && + !GetHasSpellEffect(SPELL_POWER_ATTACK6) && + !GetHasSpellEffect(SPELL_POWER_ATTACK5) && + !GetHasSpellEffect(SPELL_POWER_ATTACK4) && + !GetHasSpellEffect(SPELL_POWER_ATTACK3) && + !GetHasSpellEffect(SPELL_POWER_ATTACK2) && + !GetHasSpellEffect(SPELL_POWER_ATTACK1) */) + { + iDamage += 5; + if (bDebug) sDebugMessage += " + PowAtk (" + IntToString(5) + ")"; + } + else if( iCombatMode == COMBAT_MODE_IMPROVED_POWER_ATTACK /*&& + !GetHasSpellEffect(SPELL_SUPREME_POWER_ATTACK) && + !GetHasSpellEffect(SPELL_POWER_ATTACK10) && + !GetHasSpellEffect(SPELL_POWER_ATTACK9) && + !GetHasSpellEffect(SPELL_POWER_ATTACK8) && + !GetHasSpellEffect(SPELL_POWER_ATTACK7) && + !GetHasSpellEffect(SPELL_POWER_ATTACK6) && + !GetHasSpellEffect(SPELL_POWER_ATTACK5) && + !GetHasSpellEffect(SPELL_POWER_ATTACK4) && + !GetHasSpellEffect(SPELL_POWER_ATTACK3) && + !GetHasSpellEffect(SPELL_POWER_ATTACK2) && + !GetHasSpellEffect(SPELL_POWER_ATTACK1) */) + { + iDamage += 10; + if (bDebug) sDebugMessage += " + ImpPowAtk (" + IntToString(10) + ")"; + } + + // calculates bonus damage for Favored Enemies + // this is just added each round to help prevent lag + // can be moved if this becomes an issue of course. + int iFavoredEnemyBonus = GetFavoredEnemyDamageBonus(oDefender, oAttacker); + iDamage += iFavoredEnemyBonus; + + // Damage from the Shadow Blade feat + iDamage += GetLocalInt(oAttacker, "ShadowBladeDam"); + + if (bDebug) sDebugMessage += " + FavEnmy (" + IntToString(iFavoredEnemyBonus) + ")"; + if (bDebug) sDebugMessage = PRC_TEXT_WHITE + "Weapon Damage = " + IntToString(iDamage) + ": " + sDebugMessage; + if (bDebug) DoDebug(sDebugMessage); + + return iDamage; +} + +// returns the highest critical bonus damage constant on the weapon +// note that this is the IP_DAMAGE_CONSTANT, not the damage itself +// only compares the damage constants, so does not differentiate between dice and constant damage (and assumes, that damage constants are ordered appropriately) +int GetMassiveCriticalBonusDamageConstantOfWeapon(object oWeapon) +{ + int iMassCritBonusDamage = 0; + int iCostVal; + + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_MASSIVE_CRITICALS) + { + // get the damage constant + iCostVal = GetItemPropertyCostTableValue(ip); + + // is the damage constant higher than our highest yet? + if(iCostVal > iMassCritBonusDamage) + { + iMassCritBonusDamage = iCostVal; + } + } + ip = GetNextItemProperty(oWeapon); + } + // convert damage constant to an integer + return iMassCritBonusDamage; +} + +// this function assumes that we have scored a hit; it returns the damage effect that we need to apply to the Defender +// if we kill the critter immediately ( devastating crit), we return an invalid effect, because we don't need to apply any damage to an already dead defender +effect GetAttackDamage(object oDefender, object oAttacker, object oWeapon, struct BonusDamage sWeaponBonusDamage, struct BonusDamage sSpellBonusDamage, int iOffhand = 0, int iDamage = 0, int bIsCritical = FALSE, int iNumDice = 0, int iNumSides = 0, int iCriticalMultiplier = 0) +{ +// we assume that critical immunity of defender has been already checked in the calling function +// and that the bIsCritical flag is only true, if we scored a critical hit against a non-critical immune defender + + int iWeaponType = GetBaseItemType(oWeapon); + effect eDeath; + + // create an invalid effect to check whether we did a death attack or not + effect eLink = eDeath; + + // first check Devastating Critical + if(bIsCritical && GetHasFeat(GetDevastatingCriticalFeatOfWeaponType(iWeaponType), oAttacker) ) + { + // DC = 10 + 1/2 char level + str mod. + int iStr = GetAbilityModifier(ABILITY_STRENGTH, oAttacker); + int iLevelMod = GetHitDice(oAttacker) / 2; + int iSaveDC = 10 + iStr + iLevelMod; + + if(!FortitudeSave(oDefender, iSaveDC, SAVING_THROW_TYPE_NONE, oAttacker) ) + { + string sMes = "*Devastating Critical*"; + if (DEBUG) + { + sMes = "scripted " + sMes; +// SendMessageToPC(oAttacker, sMes); + } + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); + + // circumvents death immunity... since anyone CDG'ed is dead. + eDeath = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oDefender); + } + } + + // if we didn't score a devastating critical, proceed normally + if(eLink == eDeath) + { + string sDebugMessage = ""; + int bDebug = GetPRCSwitch(PRC_COMBAT_DEBUG); + int iWeaponDamage = 0; + int iBonusWeaponDamage = 0; + int iMassCritBonusDamage = 0; + + // only read the data if it is not already given + if(!iNumSides) iNumSides = StringToInt(Get2DACache("baseitems", "DieToRoll", iWeaponType)); + if(!iNumDice) iNumDice = StringToInt(Get2DACache("baseitems", "NumDice", iWeaponType)); + if(bIsCritical && !iCriticalMultiplier) iCriticalMultiplier = GetWeaponCritcalMultiplier(oAttacker, oWeapon); + + // Returns proper unarmed damage if they are a monk + // or have a creature weapon from a PrC class. - Brawler, Shou, IoDM, etc. + // Note: When using PerformAttackRound gloves are passed to this function + // as oWeapon, so this will not be called twice. + if( iWeaponType == BASE_ITEM_INVALID && GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oAttacker) != OBJECT_INVALID + || iWeaponType == BASE_ITEM_INVALID && GetLevelByClass(CLASS_TYPE_MONK, oAttacker) ) + { + int iUnarmedDamage = FindUnarmedDamage(oAttacker); + iNumSides = StringToInt(Get2DACache("iprp_monstcost", "Die", iUnarmedDamage)); + iNumDice = StringToInt(Get2DACache("iprp_monstcost", "NumDice", iUnarmedDamage)); + } + else if(iWeaponType == BASE_ITEM_INVALID) + { + // unarmed non-monk 1d3 damage + iNumSides = 3; + iNumDice = 1; + } + + int iDiceRoll = 0; + //Roll the base damage dice. + if(iNumSides == 2) iDiceRoll = d2(iNumDice); + if(iNumSides == 3) iDiceRoll = d3(iNumDice); + if(iNumSides == 4) iDiceRoll = d4(iNumDice); + if(iNumSides == 6) iDiceRoll = d6(iNumDice); + if(iNumSides == 8) iDiceRoll = d8(iNumDice); + if(iNumSides == 10) iDiceRoll = d10(iNumDice); + if(iNumSides == 12) iDiceRoll = d12(iNumDice); + if(iNumSides == 20) iDiceRoll = d20(iNumDice); + + // Normal rolling + iWeaponDamage += iDiceRoll; + //if (DEBUG) DoDebug("Starting Aura of Chaos"); + // Aura of Chaos rerolls and adds if the dice rolled is max. + if (GetLocalInt(oAttacker, "DSChaos")) + { + // Maximum possible result + while ((iNumSides * iNumDice) == iDiceRoll) + { + // This should cover things properly + if(iNumSides == 2) iDiceRoll = d2(iNumDice); + if(iNumSides == 3) iDiceRoll = d3(iNumDice); + if(iNumSides == 4) iDiceRoll = d4(iNumDice); + if(iNumSides == 6) iDiceRoll = d6(iNumDice); + if(iNumSides == 8) iDiceRoll = d8(iNumDice); + if(iNumSides == 10) iDiceRoll = d10(iNumDice); + if(iNumSides == 12) iDiceRoll = d12(iNumDice); + if(iNumSides == 20) iDiceRoll = d20(iNumDice); + + // Chaos bonuses + iWeaponDamage += iDiceRoll; + } + } + //if (DEBUG) DoDebug("Ending Aura of Chaos"); + + if (bDebug) sDebugMessage += IntToString(iNumDice) + "d" + IntToString(iNumSides) + " (" + IntToString(iDiceRoll) + ")"; + + int iOCRoll = 0; + // Determine Massive Critical Bonuses + if(bIsCritical) + { // note that critical hit immuniy already has been checked in the attack roll, so we need not check it here + + // get the highest massive critical bonus damage constant on the weapon + iMassCritBonusDamage = GetMassiveCriticalBonusDamageConstantOfWeapon(oWeapon); + + // convert it to an integer, if not zero + if (iMassCritBonusDamage) + iMassCritBonusDamage = GetDamageByConstant(iMassCritBonusDamage, TRUE); + + if(GetHasFeat(FEAT_EPIC_THUNDERING_RAGE, oAttacker) && GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oAttacker) ) + iMassCritBonusDamage += d8(2); + + + // if player has Overwhelming Critical with this weapon type. + if(GetHasFeat(GetOverwhelmingCriticalFeatOfWeaponType(iWeaponType), oAttacker) ) + { + // should do +1d6 damage, 2d6 if crit X 3, 3d6 if X4, etc. + int iOCDice = iCriticalMultiplier - 1; + if(iOCDice < 1) iOCDice = 1; + iOCRoll = d6(iOCDice); + } + } + + // Get bonus damage, unless we already calculated it + if(iDamage) iBonusWeaponDamage = iDamage; + else iBonusWeaponDamage = GetWeaponDamagePerRound(oDefender, oAttacker, oWeapon, iOffhand); + + // dpr = damage per round (assumed to be the same on every attack) + if (bDebug) sDebugMessage += " + Weap Bon DPR (" + IntToString(iBonusWeaponDamage) + ")"; + + iWeaponDamage += iBonusWeaponDamage; + + // PnP Rules State: + // Extra Damage over and above a weapons normal damage + // such as that dealt by a sneak attack or special ability of + // a flaming sword are not multiplied when you score a critical hit + // so no magical effects or bonuses are doubled. + + if(bIsCritical) + { + // determine critical damage + if (bDebug) sDebugMessage += " + Crit (" + IntToString(iWeaponDamage * (iCriticalMultiplier-1)) + ") [* " + IntToString(iCriticalMultiplier) + "]"; + iWeaponDamage *= iCriticalMultiplier; + + if(iMassCritBonusDamage) + { + iWeaponDamage += iMassCritBonusDamage; + if (bDebug) sDebugMessage += " + MassCrit (" + IntToString(iMassCritBonusDamage) + ")"; + } + + if(iOCRoll) + { + iWeaponDamage += iOCRoll; + if (bDebug) sDebugMessage += " + OvwhlmgCrit (" + IntToString(iOCRoll) + ")"; + } + } + + int iOldWeaponDamage = iWeaponDamage; + // add weapon bonus melee damage (constant damage) + iWeaponDamage += GetDamageByConstant(sWeaponBonusDamage.dam_Blud, TRUE); + iWeaponDamage += GetDamageByConstant(sWeaponBonusDamage.dam_Pier, TRUE); + iWeaponDamage += GetDamageByConstant(sWeaponBonusDamage.dam_Slash, TRUE); + + // add weapon bonus melee damage (dice damage) + iWeaponDamage += GetDamageByConstant(sWeaponBonusDamage.dice_Blud, TRUE); + iWeaponDamage += GetDamageByConstant(sWeaponBonusDamage.dice_Pier, TRUE); + iWeaponDamage += GetDamageByConstant(sWeaponBonusDamage.dice_Slash, TRUE); + + // weapon physical bonus damage - BPS = bludgeoning Piercing Slashing + if (bDebug) sDebugMessage += " + Weap Bon Phys (" + IntToString(iWeaponDamage - iOldWeaponDamage) + ")"; + iOldWeaponDamage = iWeaponDamage; + + // damage from spells is stored as solid number (note, these are not spells on the weapon, such as darkfire) + iWeaponDamage += sSpellBonusDamage.dam_Blud; + iWeaponDamage += sSpellBonusDamage.dam_Pier; + iWeaponDamage += sSpellBonusDamage.dam_Slash; + + iWeaponDamage += sSpellBonusDamage.dice_Blud; // motu99: Shouldn't we roll the dice? + iWeaponDamage += sSpellBonusDamage.dice_Pier; + iWeaponDamage += sSpellBonusDamage.dice_Slash; + + // motu99: why do we store different physical damage types (Bludg, Pierc, Slash) in the WeaponBonusDamage struct + // when we sum up all physical damage here and only use the weapon base damage type? + // wouldn't it be better to keep the different physical damage types separate and link them in one effect? + + // spell physical bonus damage + if (bDebug) sDebugMessage += " + Spell Phys (" + IntToString(iWeaponDamage - iOldWeaponDamage) + ")"; + + // Logic to determine if enemy can be sneak attacked + // and to add sneak attack damage + int iSneakDamage = 0; + if(GetCanSneakAttack(oDefender, oAttacker) ) + { + int iSneakDice = GetTotalSneakAttackDice(oAttacker); + if(iSneakDice > 0) + { + iSneakDamage = GetSneakAttackDamage(iSneakDice); + iWeaponDamage += iSneakDamage; + if (bDebug) sDebugMessage += " + Sneak (" + IntToString(iSneakDamage) + ")"; + + string sMes = "*Sneak Attack*"; + if (DEBUG) sMes = "scripted "+ sMes; + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); +// SendMessageToPC(oAttacker, sMes); + } + + if(GetHasFeat(FEAT_CRIPPLING_STRIKE, oAttacker) ) + { + //effect eCrippleStrike = EffectAbilityDecrease(ABILITY_STRENGTH, 2); + //ApplyEffectToObject(DURATION_TYPE_INSTANT, eCrippleStrike, oDefender); + ApplyAbilityDamage(oDefender, ABILITY_STRENGTH, 2, DURATION_TYPE_PERMANENT, TRUE); + } + } + + // Elemental damage effects + int iAcid, iCold, iFire, iElec, iSon; + int iDiv, iNeg, iPos; + int iMag; + + // first only do the constant damage effects (no dice rolls) on the weapon and from spells + iAcid = sSpellBonusDamage.dam_Acid; + iAcid += GetDamageByConstant(sWeaponBonusDamage.dam_Acid, TRUE); + + iCold = sSpellBonusDamage.dam_Cold; + iCold += GetDamageByConstant(sWeaponBonusDamage.dam_Cold, TRUE); + + iFire = sSpellBonusDamage.dam_Fire; + iFire += GetDamageByConstant(sWeaponBonusDamage.dam_Fire, TRUE); + + iElec = sSpellBonusDamage.dam_Elec; + iElec += GetDamageByConstant(sWeaponBonusDamage.dam_Elec, TRUE); + + iSon = sSpellBonusDamage.dam_Son; + iSon += GetDamageByConstant(sWeaponBonusDamage.dam_Son, TRUE); + + iDiv = sSpellBonusDamage.dam_Div; + iDiv += GetDamageByConstant(sWeaponBonusDamage.dam_Div, TRUE); + + iNeg = sSpellBonusDamage.dam_Neg; + iNeg += GetDamageByConstant(sWeaponBonusDamage.dam_Neg, TRUE); + + iPos = sSpellBonusDamage.dam_Pos; + iPos += GetDamageByConstant(sWeaponBonusDamage.dam_Pos, TRUE); + + iMag = sSpellBonusDamage.dam_Mag; + iMag += GetDamageByConstant(sWeaponBonusDamage.dam_Mag, TRUE); + + + // now add the dice damage from the weapon and spells + iAcid += GetDamageByConstant(sSpellBonusDamage.dice_Acid, TRUE); + iAcid += GetDamageByConstant(sWeaponBonusDamage.dice_Acid, TRUE); + + iCold += GetDamageByConstant(sSpellBonusDamage.dice_Cold, TRUE); + iCold += GetDamageByConstant(sWeaponBonusDamage.dice_Cold, TRUE); + + iFire += GetDamageByConstant(sSpellBonusDamage.dice_Fire, TRUE); + iFire += GetDamageByConstant(sWeaponBonusDamage.dice_Fire, TRUE); + + iElec += GetDamageByConstant(sSpellBonusDamage.dice_Elec, TRUE); + iElec += GetDamageByConstant(sWeaponBonusDamage.dice_Elec, TRUE); + + iSon += GetDamageByConstant(sSpellBonusDamage.dice_Son, TRUE); + iSon += GetDamageByConstant(sWeaponBonusDamage.dice_Son, TRUE); + + iDiv += GetDamageByConstant(sSpellBonusDamage.dice_Div, TRUE); + iDiv += GetDamageByConstant(sWeaponBonusDamage.dice_Div, TRUE); + + iNeg += GetDamageByConstant(sSpellBonusDamage.dice_Neg, TRUE); + iNeg += GetDamageByConstant(sWeaponBonusDamage.dice_Neg, TRUE); + + iPos += GetDamageByConstant(sSpellBonusDamage.dice_Pos, TRUE); + iPos += GetDamageByConstant(sWeaponBonusDamage.dice_Pos, TRUE); + + iMag += GetDamageByConstant(sSpellBonusDamage.dice_Mag, TRUE); + iMag += GetDamageByConstant(sWeaponBonusDamage.dice_Mag, TRUE); + + // Magical damage is not multiplied by criticals, at least not in PnP + // Since it is in NwN, I left it default on in a switch. + // Can be turned off to better emulate PnP rules. + // motu99: moved this down, because where it was before we only criticalled the constant damage, but not the dice damage + if(bIsCritical && !GetPRCSwitch(PRC_PNP_ELEMENTAL_DAMAGE)) + { + iAcid *=iCriticalMultiplier; + iCold *= iCriticalMultiplier; + iFire *= iCriticalMultiplier; + iElec *= iCriticalMultiplier; + iSon *= iCriticalMultiplier; + + iDiv *= iCriticalMultiplier; + iNeg *= iCriticalMultiplier; + iPos *= iCriticalMultiplier; + + iMag *= iCriticalMultiplier; + } + + if (bDebug) + { + if (iAcid) sDebugMessage += PRC_TEXT_GREEN + " + Acid (" + IntToString(iAcid) + ")"; + if (iCold) sDebugMessage += PRC_TEXT_LIGHT_BLUE + " + Cold (" + IntToString(iCold) + ")"; + if (iFire) sDebugMessage += PRC_TEXT_RED + " + Fire (" + IntToString(iFire) + ")"; + if (iElec) sDebugMessage += PRC_TEXT_DARK_BLUE + " + Elec (" + IntToString(iElec) + ")"; + if (iSon) sDebugMessage += PRC_TEXT_LIGHT_ORANGE + " + Son (" + IntToString(iSon) + ")"; + if (iDiv) sDebugMessage += PRC_TEXT_PURPLE + " + Div (" + IntToString(iDiv) + ")"; + if (iNeg) sDebugMessage += PRC_TEXT_GRAY + " + Neg (" + IntToString(iNeg) + ")"; + if (iPos) sDebugMessage += " + Pos (" + IntToString(iPos) + ")"; + if (iMag) sDebugMessage += PRC_TEXT_PURPLE + " + Mag (" + IntToString(iMag) + ")"; + } + + // sum up all magical damage, as we need it later + int iMagicalDamage = iAcid + iCold + iFire + iElec + iSon + iDiv + iNeg + iPos + iMag; + + // just in case damage is somehow less than 1 + if(iWeaponDamage < 1) iWeaponDamage = 1; + //if (DEBUG) DoDebug("Starting NightmareBlade"); + // Nightmare Blades double to quadruple the damage dealt for the normal attack + if (GetLocalInt(oDefender, "NightmareBlade") > 0) iWeaponDamage = iWeaponDamage * GetLocalInt(oDefender, "NightmareBlade"); + //if (DEBUG) DoDebug("Ending NightmareBlade"); + // create an invalid effect to return on a coup de grace + + // the rest of the code for a Coup De Grace + int bDisableCoupDeGrace = GetPRCSwitch(PRC_DISABLE_COUP_DE_GRACE); + if (!bDisableCoupDeGrace && !GetLocalInt(oAttacker, "PactQuality"+IntToString(VESTIGE_BUER)) && GetHasSpellEffect(VESTIGE_BUER, oAttacker)) bDisableCoupDeGrace = TRUE; + + if(bFirstAttack && bIsCritical && !bDisableCoupDeGrace && GetIsHelpless(oDefender)) + { + // DC = 10 + damage dealt. + int iSaveDC = 10; + iSaveDC += iWeaponDamage; + iSaveDC += iMagicalDamage; + + if(!FortitudeSave(oDefender, iSaveDC, SAVING_THROW_TYPE_NONE, oAttacker) ) + { + string sMes = "*Coup De Grace*"; + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); +// SendMessageToPC(oAttacker, sMes); + + // circumvents death immunity... since anyone CDG'ed is dead. + effect eDeath = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oDefender); + } + } + + // if we didn't succeed in our coup the grace, apply the damage + if (eLink == eDeath) + { + // motu99: inefficient, because this calls GetWeaponEnhancement(), which already was called in AttackLoopLogic() or PerformAttack() + // @TODO: store weaponEnhancement value and make new function that takes this value + int iDamagePower = GetDamagePowerConstant(oWeapon, oDefender, oAttacker); + int iDamageType = GetDamageTypeByWeaponType(iWeaponType); + //if (DEBUG) DoDebug("prc_inc_combat Calculating Damage"); + // When this maneuver is in effect, weapon damage is fire + // Also put here so it doesn't muck up things looking for weapon damage type + if (GetLocalInt(oAttacker, "DWBurningBrand")) iDamageType = DAMAGE_TYPE_FIRE; + // Swordsage Insightful Strike, grants wisdom to damage on maneuvers + if (GetLocalInt(oAttacker, "InsightfulStrike")) + { + iWeaponDamage += GetAbilityModifier(ABILITY_WISDOM, oAttacker); + } + // RKV Divine Fury grants 1d10 bonus damage + if (GetLocalInt(oAttacker, "RKVDivineFury")) + { + iWeaponDamage += d10(); + DeleteLocalInt(oAttacker, "RKVDivineFury"); + } + // Blade Meditation grants 10 bonus damage + if (GetLocalInt(oAttacker, "BladeMeditationDamage")) + { + iWeaponDamage += 1; + DeleteLocalInt(oAttacker, "BladeMeditationDamage"); + } + // Warblade Battle Cunning: Int To Damage on Flatfoots. + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oAttacker) >= 7 && (GetIsFlanked(oDefender, oAttacker) || GetIsDeniedDexBonusToAC(oDefender, oAttacker))) + { + if (DEBUG_BATTLE_CUNNING) + { + FloatingTextStringOnCreature("**** BATTLE CUNNING BONUS ****", oDefender); + DoDebug("Battle Cunning damage bonus"); + } + iWeaponDamage += GetAbilityModifier(ABILITY_INTELLIGENCE, oAttacker); + } + if (GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oAttacker) >= 3) + { + // Check the persistent locals + int i, nCount; + for(i = 1; i <= 9; i++) + { + // Loop over all disciplines, and total up how many he knows + nCount += GetPersistantLocalInt(oAttacker, "MasterOfNine" + IntToString(i)); + } + + iWeaponDamage += nCount; + } + // Bypass damage reduction if set + // Done by increasing total damage done to bypass existing DR taking immunity into account + if(GetLocalInt(oAttacker, "MoveIgnoreDR")) + { + struct DamageReducers drReduced = GetTotalReduction(oAttacker, oDefender, oWeapon); + int nRedDR = drReduced.nStaticReductions * 100 / (100 - drReduced.nPercentReductions); + iWeaponDamage += nRedDR; + if(DEBUG) DoDebug("Damage increased by " + IntToString(nRedDR) + " to ignore DR"); + } + // Shadow Sun Ninja + if (GetLocalInt(oAttacker, "SSN_DARKWL")) + { + effect eSSN = GetFirstEffect(oDefender); + while(GetIsEffectValid(eSSN)) + { + if(GetEffectType(eSSN) == EFFECT_TYPE_BLINDNESS) + { + iWeaponDamage += 4; + break; + } + eSSN = GetNextEffect(oDefender); + } + } + // This is for the Lightning Throw Maneuver. + if (GetLocalInt(oAttacker, "LightningThrowSave")) iWeaponDamage /= 2; + //if (DEBUG) DoDebug("Ending LightningThrowSave"); + + if (iDamagePower > 0) + { + struct DamReduction nDR = OvercomeDR(oDefender); + if (iDamagePower >= nDR.nRedLevel) + iWeaponDamage += nDR.nRedAmount; + + if (DEBUG) DoDebug("Overcoming DR "+IntToString(nDR.nRedAmount)); + } + + int nNoDamage = FALSE; + itemproperty ip = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ip)) + { + int ipType = GetItemPropertyType(ip); + if (ipType == ITEM_PROPERTY_NO_DAMAGE) + { + nNoDamage = TRUE; + if (DEBUG) DoDebug("nNoDamage is TRUE"); + eLink = EffectLinkEffects(EffectDamage(1), EffectHeal(1)); + } + + ip = GetNextItemProperty(oWeapon); + } + + if (!nNoDamage) + { + // motu99: why do we store different physical damage types (Bludg, Pierc, Slash) in the WeaponBonusDamage struct + // when we sum up all physical damage here and only use the weapon base damage type? + // wouldn't it be better to keep the different physical damage types separate and link them in one effect? + effect eEffect = PRCEffectDamage(oDefender, iWeaponDamage, iDamageType, iDamagePower); + //if (DEBUG) DoDebug("prc_inc_combat WeaponDamage: "+IntToString(iWeaponDamage)+" DamageType" +IntToString(iDamageType)+" DamagePower "+IntToString(iDamagePower)); + // create eLink starting with the melee weapon damage eEffect (calculated above) + // then add all the other possible effects. + eLink = eEffect; + + if (iAcid > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iAcid, DAMAGE_TYPE_ACID)), EffectVisualEffect(VFX_COM_HIT_ACID)); + if (iCold > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iCold, DAMAGE_TYPE_COLD)), EffectVisualEffect(VFX_COM_HIT_FROST )); + if (iFire > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iFire, DAMAGE_TYPE_FIRE)), EffectVisualEffect(VFX_IMP_FLAME_S)); + if (iElec > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iElec, DAMAGE_TYPE_ELECTRICAL)), EffectVisualEffect(VFX_COM_HIT_ELECTRICAL )); + if (iSon > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iSon, DAMAGE_TYPE_SONIC)), EffectVisualEffect(VFX_COM_HIT_SONIC )); + + if (iDiv > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iDiv, DAMAGE_TYPE_DIVINE)), EffectVisualEffect(VFX_COM_HIT_DIVINE)); + if (iNeg > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iNeg, DAMAGE_TYPE_NEGATIVE)), EffectVisualEffect(VFX_COM_HIT_NEGATIVE )); + if (iPos > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iPos, DAMAGE_TYPE_POSITIVE)), EffectVisualEffect(VFX_COM_HIT_DIVINE)); + + if (iMag > 0) eLink = EffectLinkEffects(EffectLinkEffects(eLink, EffectDamage(iMag, DAMAGE_TYPE_MAGICAL)), EffectVisualEffect(VFX_COM_HIT_DIVINE)); + } + } + if (bDebug) sDebugMessage = PRC_TEXT_WHITE + "Damage = " + IntToString(iWeaponDamage +iMagicalDamage) + ": " + sDebugMessage; + if (bDebug) DoDebug(sDebugMessage); + } + return eLink; +} + + +//::////////////////////////////////////////////// +//:: Attack Logic Functions +//::////////////////////////////////////////////// + +// adapted from prc_alterations +void ActionCastSpellFromPlaceable(int iSpell, object oTarget, int iCasterLvl, int nMetaMagic = METAMAGIC_ANY, object oCaster = OBJECT_SELF) +{ + object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oCaster)); + + AssignCommand(oCastingObject, ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, iCasterLvl, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + + DestroyObject(oCastingObject, 6.0); +} + + +void ApplyOnHitDurationAbiltiies(object oTarget, int iDurationVal, effect eAbility, effect eVis) +{ + int iChance = StringToInt( Get2DACache("iprp_onhitdur", "EffectChance", iDurationVal) ); + int iRoll = d100(); + + if(iRoll <= iChance) + { + int iDuration = StringToInt( Get2DACache("iprp_onhitdur", "DurationRounds", iDurationVal) ); +// effect eLink = EffectLinkEffects(eAbility, eVis); // motu99: The visual effect eVis is instant, so linking with the temporary eAbility is not a good idea +// ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(iDuration) ); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eAbility, oTarget, RoundsToSeconds(iDuration) ); + } +} + +// motu99: added saving throws (most were missing) +// @TODO: check if all saving throws are correct (will? fortitude?) and are called with appropriate SAVING_THROW_TYPE_* +// might also have to replace the Bioware saving throws with PRCMySavingThrow, eventually + +// Note, that for the IP_CONST_ONHIT_ constants, where is a spell that should be cast +// (such as IP_CONST_ONHIT_KNOCK, IP_CONST_ONHIT_LESSERDISPEL, etc.) +// we have to call the Impact spell scripts directly, because the commands ActionCastSpell* do not work within PRC combat! +/* +// the reason why we can't cast the onhitcast spells as a normal spellcast action is, that the spell casts are inserted into the action queue of the PRC attacker, +// but as long as the attacker is in physical combat (which is always the case for PerformAttack or PerformAttackRound), the (physical) attack action is +// at the top of the action queue and remains there throughout the whole combat process (which can be several rounds) +// Now the spell cast actions are inserted *after* the physical attack action, and therefore the spell cast actions are never executed +// (unless we stop attacking - which usually only happens if all enemies or the PC is dead) +// We can circumvent the problem with the action queue, by calling the Impact spell scripts directly. The problem with this approach is, that we are lacking +// the internal setup (done in the ActionCastSpell* commands), which is required so that the spell scripts receive the essential information they need. +// This essential information is retreived (in the spell script) via the functions GetSpellCastItem, GetSpellTarget etc. +// Unfortunately these information functions do not return sensible values when the impact spell scripts are called directly, +// because the necessary setup (usually done in ActionCastSpell*) has not been done +// What needs to be done, therefore, is to let the spell script know - by other means - what the essential parameters are +//(for onhit cast spells we usually need SpellTarget and SpellCastItem) +// As we don't know how Bioware passes the information to the GetSpellCastItem(), GetSpellTarget() etc. functions (most likely by local objects stored on the caster) +// the most straight forward approach seems to be, to replace all calls to GetSpellCastItem(), GetSpellTarget() etc. in all of the onhitcast spell impact scripts +// with PRC-wrapper functions, that use special local ints/objects (set on the caster or the module) in order to communicate the essential parameters to the spell impact script +// The only thing we then need to do, is to properly set up these local ints/objects by ourselves, before we execute +// the impact spell scripts, and delete them right after execution (so that they don't interfere with the normal spellcasting process) +// See ExecuteSpellScript() in prc_inc_spells how this can be done +*/ +struct OnHitSpell DoOnHitProperties(itemproperty ip, object oTarget) +{ + // covers poison, vorpal, stun, disease, etc. + // ipSubType = IP_CONST_ONHIT_* + // ipCostVal = IP_CONST_ONHIT_SAVEDC_* + +// int iType = GetItemPropertyType(ip); + struct OnHitSpell sSpell; + int iDC = GetItemPropertyCostTableValue(ip); + int iSubType = GetItemPropertySubType(ip); + int iParam1 = GetItemPropertyParam1Value(ip); + + // change to proper save DC + if (iDC < 0) iDC = 0; + else if (iDC > 6) iDC = 6; + iDC += (14 + iDC); + +/* + // change to proper save DC + if(iDC < 10) + { + switch (iDC) + { + case 0: iDC = 14; + break; + case 1: iDC = 16; + break; + case 2: iDC = 18; + break; + case 3: iDC = 20; + break; + case 4: iDC = 22; + break; + case 5: iDC = 24; + break; + case 6: iDC = 26; + break; + } + } +*/ + + // sMes += " | I have On Hit: "; + + // motu99: moved variable declations out of switch statement, because declaration within produced a stack underflow error + // we could also enclose the statements in the case with curly brackets {}, (seems to work in other places), but got paranoid after 4 hours of tracking down the error + effect eEffect; + effect eVis; + int iStat; + string sDiseaseType; + + // alignment code +// int iGoodEvil = GetAlignmentGoodEvil(oTarget); +// int iLawChaos = GetAlignmentLawChaos(oTarget); +// int iAlignSpecific = GetItemPropAlignment(iGoodEvil, iLawChaos); + + switch (iSubType) + { + // set global vars for vorpal + case IP_CONST_ONHIT_VORPAL: + { + bIsVorpalWeaponEquiped = TRUE; + iVorpalSaveDC = iDC; + break; + } + // iParam1 should be the ammout of levels to drain + case IP_CONST_ONHIT_LEVELDRAIN: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NEGATIVE) ) + { + if(iParam1 < 1) iParam1 = 1; + + eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + eEffect = SupernaturalEffect( EffectNegativeLevel(iParam1) ); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + } + break; + } + // NEEDS TESTING + case IP_CONST_ONHIT_WOUNDING: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + if(iParam1 < 1) iParam1 = 1; + iParam1 = -iParam1; + + eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + // in theory this will drain them 1 HP per round. + eEffect = ExtraordinaryEffect( EffectRegenerate(iParam1, 6.0 ) ); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oTarget, 9999.0); + } + break; + } + // motu99: ActionCastSpell* will not work within scripted combat + // @TODO: call the spell scripts directly via ExecuteSpellScript() and modify the spell scripts, + // so that they can retrieve the SpellTarget and the SpellCastItem (use the PRC wrappers) + // see new onhitcast section in prc_inc_spells for details + + /** WARNING: + // It is extremely unsafe to call the spell scripts from within a loop over item properties + // But DoOnHitProperties() is called from such a loop (the loop is done in ApplyOnHitAbilities) + // So what we must do, is to pass the spell ID back to ApplyOnHitAbilities, there store it in an array + // and then execute all impact spell scripts AFTER we have cycled through the item properties! + // see prc_inc_spells (in particular the routines ApplyAllOnHitCastSpellsOnItem*) for details + */ + case IP_CONST_ONHIT_KNOCK: + { + sSpell.iSpellID = SPELL_KNOCK; + // ActionCastSpellAtObject(SPELL_KNOCK, oTarget, METAMAGIC_ANY, TRUE, iDC, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + break; + } + case IP_CONST_ONHIT_LESSERDISPEL: + { + sSpell.iSpellID = SPELL_LESSER_DISPEL; + // ActionCastSpellAtObject(SPELL_LESSER_DISPEL, oTarget, METAMAGIC_ANY, TRUE, iDC, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + break; + } + case IP_CONST_ONHIT_DISPELMAGIC: + { + sSpell.iSpellID = SPELL_DISPEL_MAGIC; + // ActionCastSpellAtObject(SPELL_DISPEL_MAGIC, oTarget, METAMAGIC_ANY, TRUE, iDC, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + break; + } + case IP_CONST_ONHIT_GREATERDISPEL: + { + sSpell.iSpellID = SPELL_GREATER_DISPELLING; + // ActionCastSpellAtObject(SPELL_GREATER_DISPELLING, oTarget, METAMAGIC_ANY, TRUE, iDC, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + break; + } + case IP_CONST_ONHIT_MORDSDISJUNCTION: + { + sSpell.iSpellID = SPELL_MORDENKAINENS_DISJUNCTION; + // ActionCastSpellAtObject(SPELL_MORDENKAINENS_DISJUNCTION, oTarget, METAMAGIC_ANY, TRUE, iDC, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + break; + } + // iParam1 = iprp_abilities.2da + // both have the same effect in game + // this "poison" property is 1d2 ability damage + // not the actial poison.2da poison abilities. + case IP_CONST_ONHIT_ITEMPOISON: + case IP_CONST_ONHIT_ABILITYDRAIN: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + if (iParam1 <= 0) iStat = ABILITY_STRENGTH; + else if (iParam1 == 1) iStat = ABILITY_DEXTERITY; + else if (iParam1 == 2) iStat = ABILITY_CONSTITUTION; + else if (iParam1 == 3) iStat = ABILITY_INTELLIGENCE; + else if (iParam1 == 4) iStat = ABILITY_WISDOM; + else iStat = ABILITY_CHARISMA; + + eVis = EffectVisualEffect(VFX_IMP_REDUCE_ABILITY_SCORE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + //eEffect = EffectAbilityDecrease(iStat, d2() ); + //ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + ApplyAbilityDamage(oTarget, iStat, d2(), DURATION_TYPE_PERMANENT, TRUE); + } + break; + } + // ipParam1 = disease.2da + case IP_CONST_ONHIT_DISEASE: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + + sDiseaseType = Get2DACache("disease", "Type", iParam1); + eEffect = EffectDisease(iParam1); + + if(sDiseaseType == "EXTRA") eEffect = ExtraordinaryEffect(eEffect); + else if(sDiseaseType == "SUPER") eEffect = SupernaturalEffect(eEffect); + + eVis = EffectVisualEffect(VFX_IMP_DISEASE_S); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + } + break; + } + // ipParam1 = IPRP_ALIGNMENT + case IP_CONST_ONHIT_SLAYALIGNMENT: + { + // int iGoodEvil = GetAlignmentGoodEvil(oTarget); + // int iLawChaos = GetAlignmentLawChaos(oTarget); + // int iAlignSpecific = GetItemPropAlignment(GetAlignmentGoodEvil(oTarget), GetAlignmentLawChaos(oTarget)); + + // ipParam1 - specific alignment + if(iParam1 == GetItemPropAlignment(GetAlignmentGoodEvil(oTarget), GetAlignmentLawChaos(oTarget))) + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eVis = EffectVisualEffect(VFX_IMP_DEATH); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + // circumvent death immunity + eEffect = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oTarget); + } + } + break; + } + + // ipParam1 = IPRP_ALIGNGRP + case IP_CONST_ONHIT_SLAYALIGNMENTGROUP: + { + // int iGoodEvil = GetAlignmentGoodEvil(oTarget); + // int iLawChaos = GetAlignmentLawChaos(oTarget); + + // ipParam1 - alignment group + if( iParam1 == IP_CONST_ALIGNMENTGROUP_ALL + || iParam1 == GetAlignmentGoodEvil(oTarget) + || iParam1 == GetAlignmentLawChaos(oTarget)) + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eVis = EffectVisualEffect(VFX_IMP_DEATH); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + // circumvent death immunity + eEffect = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oTarget); + } + } + break; + } + // ipParam1 = racialtypes.2da + case IP_CONST_ONHIT_SLAYRACE: + { + if(iParam1 == MyPRCGetRacialType(oTarget) ) + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + + eVis = EffectVisualEffect(VFX_IMP_DEATH); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + // circumvent death immunity + eEffect = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oTarget); + } + } + break; + } + // ipParam1 = iprp_onhitdur.2da + case IP_CONST_ONHIT_BLINDNESS: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectBlindness(); + eVis = EffectVisualEffect(VFX_IMP_BLIND_DEAF_M); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_CONFUSION: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = PRCEffectConfused(); + eVis = EffectVisualEffect(VFX_IMP_CONFUSION_S); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_DAZE: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectDazed(); + eVis = EffectVisualEffect(VFX_IMP_DAZED_S); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_DEAFNESS: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectDeaf(); + eVis = EffectVisualEffect(VFX_IMP_BLIND_DEAF_M); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_DOOM: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectAttackDecrease(2); + eEffect = EffectLinkEffects(eEffect, EffectDamageDecrease(2, DAMAGE_TYPE_BLUDGEONING|DAMAGE_TYPE_PIERCING|DAMAGE_TYPE_SLASHING)); + eEffect = EffectLinkEffects(eEffect, EffectSavingThrowDecrease(SAVING_THROW_ALL, 2)); + eEffect = EffectLinkEffects(eEffect, EffectSkillDecrease(SKILL_ALL_SKILLS, 2)); + eVis = EffectVisualEffect(VFX_IMP_DOOM); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_FEAR: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectFrightened(); + eVis = EffectVisualEffect(VFX_IMP_HEAD_EVIL); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_HOLD: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectParalyze(); + eVis = EffectVisualEffect(VFX_DUR_FREEZE_ANIMATION); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_SILENCE: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectSilence(); + eVis = EffectVisualEffect(VFX_IMP_SILENCE); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_SLEEP: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectSleep(); + eVis = EffectVisualEffect(VFX_IMP_SLEEP); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_SLOW: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectSlow(); + eVis = EffectVisualEffect(VFX_IMP_SLOW); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONHIT_STUN: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectStunned(); + eVis = EffectVisualEffect(VFX_IMP_STUN); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + default: + { + if (DEBUG) DoDebug("DoOnHitProperties: subtype not known, iSubType = " + IntToString(iSubType)); + break; + } + } + + if (sSpell.iSpellID) + sSpell.iDC = iDC; + + return sSpell; +} + +// motu99: added saving throws (most were missing) +// @TODO: check if all saving throws are correct (will? fortitude?) and are called with appropriate SAVING_THROW_TYPE_* +// might also have to replace the Bioware saving throws with PRCMySavingThrow, eventually +void DoOnMonsterHit(itemproperty ip, object oTarget) +{ +// int iType = GetItemPropertyType(ip); + int iDC = GetItemPropertyCostTableValue(ip); + int iSubType = GetItemPropertySubType(ip); + int iParam1 = GetItemPropertyParam1Value(ip); + + // change to proper save DC + if (iDC < 0) iDC = 0; + else if (iDC > 6) iDC = 6; + iDC += (14 + iDC); +/* + if(iDC < 10) + { + switch (iDC) + { + case 0: iDC = 14; + break; + case 1: iDC = 16; + break; + case 2: iDC = 18; + break; + case 3: iDC = 20; + break; + case 4: iDC = 22; + break; + case 5: iDC = 24; + break; + case 6: iDC = 26; + break; + } + } +*/ + + // motu99: moved variable declations out of switch statement, because declaration within produced a stack underflow error + // we could also enclose the statements in the case with curly brackets {}, but got paranoid after 4 hours of tracking down the error + effect eEffect; + effect eVis; + int iStat; + string sDiseaseType; + + switch(iSubType) + { + // ipParam1 should be the ammout of levels to drain + case IP_CONST_ONMONSTERHIT_LEVELDRAIN: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NEGATIVE) ) + { + if(iParam1 < 1) iParam1 = 1; + + eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + eEffect = SupernaturalEffect( EffectNegativeLevel(iParam1) ); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + } + break; + } + // NEEDS TESTING + case IP_CONST_ONMONSTERHIT_WOUNDING: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + if(iParam1 < 1) iParam1 = 1; + iParam1 = -iParam1; + + eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + // in theory this will drain them 1 HP per round. + eEffect = ExtraordinaryEffect( EffectRegenerate(iParam1, 6.0 ) ); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oTarget, 9999.0); + } + break; + } + // iParam1 = iprp_abilities.2da + // both have the same effect in game + // this "poison" property is 1d2 ability damage + // not the actial poison.2da poison abilities. + case IP_CONST_ONMONSTERHIT_POISON: + case IP_CONST_ONMONSTERHIT_ABILITYDRAIN: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + if (iParam1 <= 0) iStat = ABILITY_STRENGTH; + else if (iParam1 == 1) iStat = ABILITY_DEXTERITY; + else if (iParam1 == 2) iStat = ABILITY_CONSTITUTION; + else if (iParam1 == 3) iStat = ABILITY_INTELLIGENCE; + else if (iParam1 == 4) iStat = ABILITY_WISDOM; + else iStat = ABILITY_CHARISMA; + + eVis = EffectVisualEffect(VFX_IMP_REDUCE_ABILITY_SCORE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + //eEffect = EffectAbilityDecrease(iStat, d2() ); + //ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + ApplyAbilityDamage(oTarget, iStat, d2(), DURATION_TYPE_PERMANENT, TRUE); + } + break; + } + // ipParam1 = disease.2da + case IP_CONST_ONMONSTERHIT_DISEASE: + { + if( !FortitudeSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + sDiseaseType = Get2DACache("disease", "Type", iParam1); + eEffect = EffectDisease(iParam1); + + if(sDiseaseType == "EXTRA") eEffect = ExtraordinaryEffect(eEffect); + else if(sDiseaseType == "SUPER") eEffect = SupernaturalEffect(eEffect); + + eVis = EffectVisualEffect(VFX_IMP_DISEASE_S); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + } + break; + } + case IP_CONST_ONMONSTERHIT_CONFUSION: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = PRCEffectConfused(); + eVis = EffectVisualEffect(VFX_IMP_CONFUSION_S); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONMONSTERHIT_DOOM: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectAttackDecrease(2); + eEffect = EffectLinkEffects(eEffect, EffectDamageDecrease(2, DAMAGE_TYPE_BLUDGEONING|DAMAGE_TYPE_PIERCING|DAMAGE_TYPE_SLASHING)); + eEffect = EffectLinkEffects(eEffect, EffectSavingThrowDecrease(SAVING_THROW_ALL, 2)); + eEffect = EffectLinkEffects(eEffect, EffectSkillDecrease(SKILL_ALL_SKILLS, 2)); + + eVis = EffectVisualEffect(VFX_IMP_DOOM); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONMONSTERHIT_FEAR: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectFrightened(); + eVis = EffectVisualEffect(VFX_IMP_HEAD_EVIL); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONMONSTERHIT_SLOW: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectSlow(); + eVis = EffectVisualEffect(VFX_IMP_SLOW); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + case IP_CONST_ONMONSTERHIT_STUN: + { + if( !WillSave(oTarget, iDC, SAVING_THROW_TYPE_NONE) ) + { + eEffect = EffectStunned(); + eVis = EffectVisualEffect(VFX_IMP_STUN); + ApplyOnHitDurationAbiltiies(oTarget, iParam1, eEffect, eVis); + } + break; + } + default: + { + if(DEBUG) DoDebug("DoOnMonsterHit: item property subtype not known, ipSubType = "+ IntToString(iSubType)); + break; + } + } +} + + + +// motu99: modified function so that it does not produce a stack underflow error +// added saving throws (could be done more elegantly, but why bother, if it works) +// @TODO: check if all saving throws are correct (will? fortitude?) and are called with appropriate SAVING_THROW_TYPE_* + +// made modifications to onhitcast system, so that onhitcasting should work - in principle - with all onhitcast spells +// ( modifications to the spell scripts are required, in PRC 3.1d only done for Darkfire and Flame Weapon) +// @TODO: do the necessary modifications for all impact spell scripts (see prc_inc_spells, what to do) +void ApplyOnHitAbilities(object oTarget, object oItemWielder, object oItem) +{ +// string sMes = ""; + + // motu99: moved declaration of these variables outside of switch statement + // because it says in NWNLexicon that you cannot declare / initialize a variable in a case statement + // in the old version the function produced a stack underflow run-time error (hard to track down: compiler does not issue a compilation warning!) + int iNr; + int iType; + int iSubType; + int iCostVal; + int iParam1; + int bOnHitCastSpell = FALSE; + struct OnHitSpell sSpell; + array_create(oItemWielder, "ohspl"); // This is used here and in prc_inc_onhit + + effect eEffect; + + itemproperty ip = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ip)) + { + iType = GetItemPropertyType(ip); + + switch (iType) + { + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + { + iCostVal = GetItemPropertyCostTableValue(ip); + eEffect = EffectHeal(iCostVal); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oItemWielder, 0.0); + break; + } + case ITEM_PROPERTY_ONHITCASTSPELL: + { + // route all on-hit cast spells through the prc_onhitcast script + // originally prc_onhitcast was only meant for unique powers, + // but it has been extended to do all other onhit cast spells on the weapon + // in order to bypass the biobug, that will only execute the first onhitcast spell on an item + bOnHitCastSpell = TRUE; + break; + } + case ITEM_PROPERTY_ON_HIT_PROPERTIES: + { + sSpell = DoOnHitProperties(ip, oTarget); + + // was it a spell that must be cast? + if (sSpell.iSpellID) + { + iNr++; + // store the spell ID in an array and execute the spell later, this is safer than trying to execute the spell script directly + array_set_int(oItemWielder, "ohspl", iNr, sSpell.iSpellID); + + iNr++; + array_set_int(oItemWielder, "ohspl", iNr, sSpell.iDC); + } + break; + } + // much like above but for creature weapons + case ITEM_PROPERTY_ON_MONSTER_HIT: + { + DoOnMonsterHit(ip, oTarget); + break; + } + // poisons from poison.2da + case ITEM_PROPERTY_POISON: + { + // @TODO: check if poison requires a Fortitude save + iSubType = GetItemPropertySubType(ip); + eEffect = EffectPoison( iSubType ); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oTarget); + + eEffect = EffectVisualEffect(VFX_IMP_POISON_L); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oTarget); + break; + } + default: + { + break; + } + } // end switch iType + ip = GetNextItemProperty(oItem); + } + + // now route all on hit cast spells through "prc_onhitcast" + if (bOnHitCastSpell) + { + ApplyOnHitUniquePower(oTarget, oItem, oItemWielder); + } + + // now execute the spell scripts (note that the local array will not be deleted) + while (iNr) + { + sSpell.iDC = array_get_int(oItemWielder, "ohspl", iNr); + iNr--; + sSpell.iSpellID = array_get_int(oItemWielder, "ohspl", iNr); + iNr--; + // we might have to determine an appropriate caster level (minimum for spell?) and an appropriate class from the spellID + CastSpellAtObject(sSpell.iSpellID, oTarget, METAMAGIC_NONE, 0, 0, sSpell.iDC, OBJECT_INVALID, oItemWielder); + } + +// FloatingTextStringOnCreature(sMes, oAttacker); +} + +/** +// these files formerly contained references to the local object "PRC_CombatSystem_OnHitCastSpell_Item" +// references to this local int have been replaced by calls to PRCGetSpellCastItem() +scripts.hak: - prc_evnt_bonebld + - prc_evnt_strmtl + - prc_onhitcast +psionics.hak: psi_sk_onhit +*/ + +// checks all inventory slots for the haste item property +// we need this, because looping over all effects on the oPC does not find Haste from items +int GetHasHasteItemProperty(object oPC) +{ + int nInventorySlot; + object oItem; + + for (nInventorySlot = 0; nInventorySlot < NUM_INVENTORY_SLOTS; nInventorySlot++) + { + oItem = GetItemInSlot(nInventorySlot, oPC); + + if(GetIsObjectValid(oItem)) + { + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_HASTE) ) + return TRUE; + } + } + return FALSE; +} + +struct BonusAttacks GetBonusAttacks(object oAttacker) +{ + int iSpell; + struct BonusAttacks sBonusAttacks; + int bHasHaste = FALSE; + int bMartialFlurry = FALSE; + + effect eEffect = GetFirstEffect(oAttacker); + + // loop through all effects as long as they are valid + while(GetIsEffectValid(eEffect)) + { + // might have to guard against multiple haste effects, so we set a flag + // could do this with the spell effects as well, but these should be only once on the PC + if (GetEffectType(eEffect) == EFFECT_TYPE_HASTE) + bHasHaste = TRUE; + else + { + iSpell = GetEffectSpellId(eEffect); + switch(iSpell) + { + case SPELL_FURIOUS_ASSAULT: + sBonusAttacks.iNumber++; + sBonusAttacks.iPenalty += 2; + break; + + case SPELL_MARTIAL_FLURRY_LIGHT: + case SPELL_MARTIAL_FLURRY_ALL: + bMartialFlurry = TRUE; + break; + + case SPELL_EXTRASHOT: + case SPELL_ONE_STRIKE_TWO_CUTS: // hopefully this spell is only on, if a katana is equipped + sBonusAttacks.iNumber++; + break; + } + } + eEffect = GetNextEffect(oAttacker); + } + + // if there is no Haste effect directly on the PC, check for haste on equipped items + if (!bHasHaste) + bHasHaste = GetHasHasteItemProperty(oAttacker); + + if (bHasHaste) + sBonusAttacks.iNumber++; + + if (bMartialFlurry) + { + sBonusAttacks.iNumber++; + sBonusAttacks.iPenalty += 2; + } + + return sBonusAttacks; +} + +// equips the first ammunition it finds in the inventory that works with the (ranged) weapon in the right hand +// returns the equipped new ammunition +object EquipAmmunition(object oPC) +{ +// no sanity checks other than invalid right hand weapon; assumes we are really wielding a ranged weapon +// if called with a non ranged weapon, it will equip a weapon of the same base type from the inventory, if it finds one + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (oWeapon == OBJECT_INVALID) + return oWeapon; + + int iWeaponType = GetBaseItemType(oWeapon); + int iAmmoSlot = GetAmmunitionInventorySlotFromWeaponType(iWeaponType); + int iNeededAmmoType; + + if (iAmmoSlot == INVENTORY_SLOT_ARROWS) + iNeededAmmoType = BASE_ITEM_ARROW; + else if (iAmmoSlot == INVENTORY_SLOT_BOLTS) + iNeededAmmoType = BASE_ITEM_BOLT; + else if (iAmmoSlot == INVENTORY_SLOT_BULLETS) + iNeededAmmoType = BASE_ITEM_BULLET; + else // darts, throwing axes or shuriken + iNeededAmmoType = iWeaponType; + + int bNotEquipped = TRUE; + object oItem = GetFirstItemInInventory(oPC); + + while (GetIsObjectValid(oItem) && bNotEquipped) + { + int iAmmoType = GetBaseItemType(oItem); + if( iAmmoType == iNeededAmmoType) + { + AssignCommand(oPC, ActionEquipItem(oItem, iAmmoSlot)); + bNotEquipped = FALSE; + } + oItem = GetNextItemInInventory(oPC); + } + + return oItem; +} + + + +object FindNearestEnemy(object oAttacker) +{ + return GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); +} + +object FindNearestNewEnemy(object oAttacker, object oOldDefender) +{ + // Find nearest enemy creature that is not the oOldDefender + int iCreatureCounter = 1; + + object oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iCreatureCounter, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + + // if there is no valid target at all, return directly + if (!GetIsObjectValid(oTarget)) + return OBJECT_INVALID; + + // skip over any old defender + else if (oTarget == oOldDefender) + { + iCreatureCounter++; + oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iCreatureCounter, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + } + + // either the target is invalid, or we found our closest target that is not the old defender + // if this creature is not the closest living, no other will (unless GetNearestCreature is bugged, and returns dead creatures) + return oTarget; +} + +// Find nearest (valid) living enemy creature, that is not oOldDefender and that is within the specified range (in meters) +// default range is melee distance (=10 feet, 3.05 meters) +// If there is no (valid) living enemy within range, return the closest (living) enemy out of range +// If the first valid (supposedly living) creature found out of range is dead, or there is no (living) creature out of range, return OBJECT_INVALID +object FindNearestNewEnemyWithinRange(object oAttacker, object oOldDefender, float fDistance = MELEE_RANGE_METERS) +{ + int iCreatureCounter = 1; + object oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iCreatureCounter, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + + // is the old defender valid? + int bOldDefenderValid = oOldDefender != OBJECT_INVALID; + + // skip over any target that is equal to the old defender + // this only makes sense if old defender is a valid object + if(bOldDefenderValid && oTarget == oOldDefender) + { + iCreatureCounter++; + oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iCreatureCounter, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + } + + // if oTarget is invalid, there is no new enemy, so we return + if (!GetIsObjectValid(oTarget)) + return OBJECT_INVALID; + // we only return non-dead targets + else if (!GetIsDead(oTarget)) + return oTarget; + + // in the unlikely case that we found a dead, but valid target we only look for new candidate targets + // as long as the distance to our last found (valid but dead) candidate target is within the specified range + while (GetDistanceBetween(oTarget, oAttacker) <= fDistance) + { + iCreatureCounter++; + oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iCreatureCounter, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + + // skip over any target that is equal to the old defender + // this only makes sense if old defender is a valid object + if(bOldDefenderValid && oTarget == oOldDefender) + { + iCreatureCounter++; + oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iCreatureCounter, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + } + + // if there is no valid target, we abort + if (!GetIsObjectValid(oTarget)) + return OBJECT_INVALID; + // otherwise we only return non-dead targets + else if (!GetIsDead(oTarget)) + return oTarget; + } + // the last candidate target must have been valid, but dead and out of range, so we return OBJECT_INVALID + // we could also return the dead target, but that does not really make sense + return OBJECT_INVALID; +} + +// debug function, might move this to inc_utility +string GetActionName(int iAction) +{ + switch(iAction) + { + case ACTION_ANIMALEMPATHY: return "ACTION_ANIMALEMPATHY"; + case ACTION_ATTACKOBJECT: return "ACTION_ATTACKOBJECT"; + case ACTION_CASTSPELL: return "ACTION_CASTSPELL"; + case ACTION_CLOSEDOOR: return "ACTION_CLOSEDOOR"; + case ACTION_COUNTERSPELL: return "ACTION_COUNTERSPELL"; + case ACTION_DIALOGOBJECT: return "ACTION_DIALOGOBJECT"; + case ACTION_DISABLETRAP: return "ACTION_DISABLETRAP"; + case ACTION_DROPITEM: return "ACTION_DROPITEM"; + case ACTION_EXAMINETRAP: return "ACTION_EXAMINETRAP"; + case ACTION_FLAGTRAP: return "ACTION_FLAGTRAP"; + case ACTION_FOLLOW: return "ACTION_FOLLOW"; + case ACTION_HEAL: return "ACTION_HEAL"; + case ACTION_INVALID: return "ACTION_INVALID"; + case ACTION_ITEMCASTSPELL: return "ACTION_ITEMCASTSPELL"; + case ACTION_KIDAMAGE: return "ACTION_KIDAMAGE"; + case ACTION_LOCK: return "ACTION_LOCK"; + case ACTION_MOVETOPOINT: return "ACTION_MOVETOPOINT"; + case ACTION_OPENDOOR: return "ACTION_OPENDOOR"; + case ACTION_OPENLOCK: return "ACTION_OPENLOCK"; + case ACTION_PICKPOCKET: return "ACTION_PICKPOCKET"; + case ACTION_PICKUPITEM: return "ACTION_PICKUPITEM"; + case ACTION_RANDOMWALK: return "ACTION_RANDOMWALK"; + case ACTION_RECOVERTRAP: return "ACTION_RECOVERTRAP"; + case ACTION_REST: return "ACTION_REST"; + case ACTION_SETTRAP: return "ACTION_SETTRAP"; + case ACTION_SIT: return "ACTION_SIT"; + case ACTION_SMITEGOOD: return "ACTION_SMITEGOOD"; + case ACTION_TAUNT: return "ACTION_TAUNT"; + case ACTION_USEOBJECT: return "ACTION_USEOBJECT"; + case ACTION_WAIT: return "ACTION_WAIT"; + } + return "unknown"; +} + +//returns a struct describing the applicable damage reductions with the given weapon and target +struct DamageReducers GetTotalReduction(object oPC, object oTarget, object oWeapon) +{ + int nDamageType = GetWeaponDamageType(oWeapon); + //Note: DamageType is a bitwise number. 1 is B, 2 is P, 4 is S. + //if(DEBUG) DoDebug("Damage Type: " + IntToString(nDamageType)); + int nAttackBonus = GetMonkEnhancement(oWeapon, oTarget, oPC); + + //handling for ammo + if(oWeapon == GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC) + || oWeapon == GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC) + || oWeapon == GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC)) + nAttackBonus = GetWeaponAttackBonusItemProperty(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC), oTarget); + + if(DEBUG) DoDebug("Weapon Atk Bonus: " + IntToString(nAttackBonus)); + + struct DamReduction nBestDamageReduction; + int nBestDamageResistance = 0; + int nApplicableReduction; + int nBestImmunutyLevel; + struct DamReduction nCurrentReduction; + nCurrentReduction.nRedLevel = DAMAGE_POWER_NORMAL; + nCurrentReduction.nRedAmount = 0; + nBestDamageReduction = nCurrentReduction; + + if(nAttackBonus < 1) nApplicableReduction = DAMAGE_POWER_NORMAL; + else nApplicableReduction = IPGetDamageBonusConstantFromNumber(nAttackBonus); + + + //loop through spell/power effects first + effect eLoop=GetFirstEffect(oTarget); + + while (GetIsEffectValid(eLoop)) + { + int nSpellID = GetEffectSpellId(eLoop); + + //Stoneskin + if( nSpellID == 172 + || nSpellID == 342 + || nSpellID == SPELL_URDINNIR_STONESKIN) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 10; + } + //GreaterStoneskin + if( nSpellID == 74) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 20; + } + //Premonition + if( nSpellID == 134) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 30; + } + //Ghostly Visage + if( nSpellID == 351 + || nSpellID == 605 + || nSpellID == 120) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_ONE; + nCurrentReduction.nRedAmount = 5; + } + //Ethereal Visage + if( nSpellID == 121) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_THREE; + nCurrentReduction.nRedAmount = 20; + } + //Shadow Shield and Shadow Evade(best case) + if( nSpellID == 160 + || nSpellID == 477 + || nSpellID == SPELL_SHADOWSHIELD) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_THREE; + nCurrentReduction.nRedAmount = 10; + } + //Iron Body + if( nSpellID == POWER_IRONBODY) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_FIVE; + nCurrentReduction.nRedAmount = 15; + } + //Oak Body + if( nSpellID == POWER_OAKBODY && nDamageType == DAMAGE_TYPE_SLASHING) + { + nBestDamageResistance = 10; + } + //Shadow Body + if( nSpellID == POWER_SHADOWBODY) + { + nCurrentReduction.nRedLevel = DAMAGE_POWER_PLUS_ONE; + nCurrentReduction.nRedAmount = 10; + } + + //if it applies and prevents more damage, replace + if(nCurrentReduction.nRedLevel > nApplicableReduction + && nCurrentReduction.nRedAmount > nBestDamageReduction.nRedAmount) + nBestDamageReduction = nCurrentReduction; + + + eLoop=GetNextEffect(oTarget); + } + + //now loop through items + int nSlot; + object oItem; + itemproperty ipResist = ItemPropertyDamageResistance(nDamageType, IP_CONST_DAMAGERESIST_5); + int nSubType; + + for (nSlot=0; nSlot nBestDamageResistance) nBestDamageResistance = nResist; + } + } + + if(GetItemPropertyType(ipLoop) == ITEM_PROPERTY_DAMAGE_REDUCTION/* + && GetItemPropertySubType(ipLoop) > nApplicableReduction*/) + { + int nReduce = GetItemPropertyCostTableValue(ipLoop) * 5; + if (nReduce > nBestDamageReduction.nRedAmount) + nBestDamageReduction.nRedAmount = nReduce; + } + + if(GetItemPropertyType(ipLoop) == ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE) + { + nSubType = GetItemPropertySubType(ipLoop); + if(nSubType == 0) nSubType = 1; + if(nSubType == 1) nSubType = 2; + if(nSubType == 2) nSubType = 4; + if((nSubType & nDamageType) == nSubType) + { + int nImmune = 0; + if(GetItemPropertyCostTableValue(ipLoop) == 1) + nImmune = 5; + else if(GetItemPropertyCostTableValue(ipLoop) == 2) + nImmune = 10; + else if(GetItemPropertyCostTableValue(ipLoop) == 3) + nImmune = 25; + else if(GetItemPropertyCostTableValue(ipLoop) == 4) + nImmune = 50; + else if(GetItemPropertyCostTableValue(ipLoop) == 5) + nImmune = 75; + else if(GetItemPropertyCostTableValue(ipLoop) == 6) + nImmune = 90; + else if(GetItemPropertyCostTableValue(ipLoop) == 7) + nImmune = 100; + + if(nImmune > nBestImmunutyLevel) nBestImmunutyLevel = nImmune; + } + } + + //Next itemproperty on the list... + ipLoop=GetNextItemProperty(oItem); + } + + //}//end item prop check + + }//end validity check + }//end for + if(DEBUG) DoDebug("Best Resistance: " + IntToString(nBestDamageResistance)); + if(DEBUG) DoDebug("Best Reduction: " + IntToString(nBestDamageReduction.nRedAmount)); + if(DEBUG) DoDebug("Best Percent Immune: " + IntToString(nBestImmunutyLevel)); + + struct DamageReducers drOverallReduced; + drOverallReduced.nStaticReductions = nBestDamageResistance + nBestDamageReduction.nRedAmount; + drOverallReduced.nPercentReductions = nBestImmunutyLevel; + + return drOverallReduced; +} + + +// experimental: not functional +// checks the action type (to be determined by a call to GetCurrentAction()) and returns TRUE, +// if this action type is compatible with being in physical combat +// (physical combat - including touch attack spells - is the combat done by PerformAttack and PerformAttackRound) +// motu99: so far not clear, whether the categorization of the actions is sensible, +// @TODO: either comment out or comment in the appropriate line, if the categorization for a specific actions must be changed +// this function should eventually be used by AttackLoopLogic() in order to determine, whether oAttacker shall attack a new target or do nothing +//(for instance, we would do nothing when the player has decided to cast a spell, run away or do any other non physical combat action) +int GetIsPhysicalCombatAction(int iAction) +{ + switch(iAction) + { +// case ACTION_ANIMALEMPATHY: return TRUE; + case ACTION_ATTACKOBJECT: return TRUE; +// case ACTION_ANIMALEMPATHY: return TRUE; +// case ACTION_CASTSPELL: return TRUE; +// case ACTION_CLOSEDOOR: return TRUE; +// case ACTION_COUNTERSPELL: return TRUE; // not clear if we can counterspell while physically attacking, probably not +// case ACTION_DIALOGOBJECT: return TRUE; +// case ACTION_DISABLETRAP: return TRUE; + case ACTION_DROPITEM: return TRUE; +// case ACTION_EXAMINETRAP: return TRUE; +// case ACTION_FLAGTRAP: return TRUE; +// case ACTION_FOLLOW: return TRUE; +// case ACTION_HEAL: return TRUE; + case ACTION_INVALID: return TRUE; +// case ACTION_ITEMCASTSPELL: return TRUE; + case ACTION_KIDAMAGE: return TRUE; +// case ACTION_LOCK: return TRUE; + case ACTION_MOVETOPOINT: return TRUE; +// case ACTION_OPENDOOR: return TRUE; +// case ACTION_OPENLOCK: return TRUE; +// case ACTION_PICKPOCKET: return TRUE; +// case ACTION_PICKUPITEM: return TRUE; +// case ACTION_RANDOMWALK: return TRUE; +// case ACTION_RECOVERTRAP: return TRUE; +// case ACTION_REST: return TRUE; +// case ACTION_SETTRAP: return TRUE; +// case ACTION_SIT: return TRUE; + case ACTION_SMITEGOOD: return TRUE; + case ACTION_TAUNT: return TRUE; +// case ACTION_USEOBJECT: return TRUE; +// case ACTION_WAIT: return TRUE; + } + return FALSE; +} + +float GetSizeAdjustment(object oDefender, object oAttacker) +{ + int iSize = PRCGetCreatureSize(oAttacker) - CREATURE_SIZE_MEDIUM; +//DoDebug("GetSizeAdjustment: attacker size = "+IntToString(iSize)); + if (iSize < 0) + iSize = 0; + + int iSize2 = PRCGetCreatureSize(oDefender) - CREATURE_SIZE_MEDIUM; +//DoDebug("GetSizeAdjustment: defender size = "+IntToString(iSize2)); + if (iSize2 < 0) + iSize2 = 0; + + // for size above medium add a meter per (size - creature_size_medium) + return(IntToFloat(iSize+iSize2)); +} + +// this function is needed in order to have the aurora combat system and scripted prc combat to run smoothly in parallel +// oDefender is the target selected by the prc combat functions +// CheckForChangeOfTarget() tries to return the "best" target for the next (prc) attack +object CheckForChangeOfTarget(object oAttacker, object oDefender, int bAllowSwitchOfTarget = FALSE) +{ +// First we determine the attempted (or last attacked) target (oTarget) and check if it is equal to oDefender. +// If they are not equal, the preference is on oTarget, as it was the last attempted (last attacked) target +// (oTarget most likely reflects the last actions of the aurora combat system, and this most likely reflects the human player's wishes) +// we never switch targets if it is a ranged attack, unless our preferred target (oTarget) is invalid or dead +// if we are in melee combat, we never switch targets if our preferred target (oTarget) is already in melee range +// if we are in melee combat and our preferred target is out of melee range, we switch targets if we can find another target (usually oDefender) in melee range +// if both the preferred target and the closest other target are out of melee range (and both live), we only switch targets +// when the distance to the other target is 2 feet closer than the distance to our preferred target (oTarget) + + // find the target on which we are currently attempting an attack + object oTarget = GetAttemptedAttackTarget(); + + // if dead or invalid, try the last target we actually attacked - quite likely this will be equal to GetAttemptedTarget(), + // but it might still be worthwhile to try + if (!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) + oTarget = GetAttackTarget(oAttacker); + + // check whether we might have to change targets + // find the "best pick" of oDefender and oTarget and make it oTarget + if (oTarget != oDefender) + { + //if (DEBUG) DoDebug(PRC_TEXT_WHITE + "PRC combat system: prc_inc_combat and aurora engine have selected different targets."); + // our preference is for oTarget, on which we attempted the most recent attack + // so we will return oTarget, unless + // the attempted (or last attacked) target is invalid or dead + if (!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) + { + oTarget = oDefender; + } + // if oTarget lives, we replace oTarget only if + else if ( bAllowSwitchOfTarget // we have allowed a switch of targets (this should never be the case for a ranged attack) + && !GetIsInMeleeRange(oTarget, oAttacker) // oTarget is not in melee range + && GetIsObjectValid(oDefender) // oDefender is valid + && !GetIsDead(oDefender) // oDefender lives + // and the distance to oDefender is more than two feet (0.67 meters) less than to oTarget + && GetDistanceBetween(oAttacker, oDefender) + 0.67 < GetDistanceBetween(oAttacker, oTarget) ) + { + oTarget = oDefender; + } + } + + // our best pick for oTarget might still be dead + // this would only happen, when oDefender and oTarget are both dead or invalid + if( !GetIsObjectValid(oTarget) || GetIsDead(oTarget)) + { + // motu99: in the original code we aborted no matter what + // but I think we should at least try to find a valid target + // as long as we are still attacking, it is quite natural to look for + // new enemies and attack them, instead of just standing around, taking the + // hits and waiting for the human player to select our target for us +/* + // it really only makes sense to look for a new target, if we are still attacking + // otherwise our actions will interfere with any other (non-combat) actions (such as running away, drinking potions, etc.) + // OTOH this is a combat function, so we implicitly assume that we are still in combat + // (we should have checked this on every entry to AttackLoopLogic() or AttackLoopMain(), therefore it is commented out here) + if (GetCurrentAction(oAttacker) != ACTION_ATTACKOBJECT) + return OBJECT_INVALID; +*/ + oTarget = FindNearestEnemy(oAttacker); + + // if the nearest (living) enemy is dead or invalid still, we must abort + if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) + { + return OBJECT_INVALID; + } + } + // oTarget lives, but (s)he might be out of melee range, so find a closer target if the switch permits (and we are not doing ranged combat) + else if (bAllowSwitchOfTarget) + { + // only attempt a target switch, if oTarget is not in melee range + if (!GetIsInMeleeRange(oTarget, oAttacker)) + { + // find the nearest enemy (this could well be oTarget) + oDefender = FindNearestEnemy(oAttacker); + + // if oTarget is already the closest enemy, then we are finished + if (oDefender == oTarget) + return oTarget; + + // only makes sense to switch targets, if oDefender lives + if (GetIsObjectValid(oDefender) && !GetIsDead(oDefender)) + { + // if oDefender is in melee range, than that is our preferred choice + if(GetIsInMeleeRange(oDefender, oAttacker)) + oTarget = oDefender; + + // oTarget and oDefender are both out of melee range. + // in this case our preference is still on oTarget! So we articially increase the distance to oDefender by 2 feet before we compare + if (GetDistanceBetween(oAttacker, oDefender) + 0.67 <= GetDistanceBetween(oAttacker, oTarget)) + oTarget = oDefender; + } + } + } + + return oTarget; +} + +// this is to cancel any other "move to location" commands +// it will not reset the combat status +// note that if you make several calls to PerformAttack() via AssignCommand(DelayCommand(fDelay, PerformAttack())) +// this might cancel any PerformAttack() actions that are still in the "pipeline" +// so better change the order to DelayCommand(fDelay, AssignCommand(PerformAttack())) +// or just don't use AssignCommand +void ClearAllActionsAndMoveToLocation(object oTarget) +{ + ClearAllActions(); + ActionMoveToLocation(GetLocation(oTarget), TRUE); +} + +void ClearAllActionsAndAttack(object oTarget) +{ + ClearAllActions(); + ActionAttack(oTarget); +} + +void ClearAllActionsAndMoveToObject(object oTarget, float fRange = 2.0) +{ + ClearAllActions(); + ActionMoveToObject(oTarget, TRUE, fRange); +} + +void ClearAllActionsMoveToObjectAndAttack(object oTarget, float fRange = 2.0) +{ + ClearAllActions(); + ActionMoveToObject(oTarget, TRUE, fRange); + ActionAttack(oTarget); +} + +// AttackLoopLogic actually does the attack, e.g. it does all the rolls, and then calculates and applies the damage +/** +// INTERNAL LOGIC: + +// AttackLoopLogic is called with the number of attacks left *after* the attack is done +// This info is needed,when we call AttackLoopMain() at the end of the AttackLoopLogic in order to schedule more attacks. +// AttackLoopLogic will *first* do the attack with the parameters given, +// it will only check iBonusAttacks, iMainhandAttacks and iOffhandAttacks later +// in order to decrement the Attack-modifier after it actually did the attack + +// When doing the attack,we first make some checks (if defender is still valid) and whether we actually can attack the defender +// in order to run smoothly with the parallel running aurora system, we will select the "best" target for the next attack + +// if we are in melee combat, the target is out of melee range, but a 5 foot step can bring us into range, we do the 5 foot step +// we can only do a 5 foot step once in a full combat round. If we cannot get into melee range with a 5 foot step, we cancel +// the full attack round and do a move action to the target (we do this by an ActionAttack() command, so that +// the aurora engine will initiate a new full combat round as soon as we are in melee range) + +// if we found a target in range, and it is the first attack, the attacker will be put out of stealth mode or invisibility etc. +// then we check whether oDefender helpless. If so, we can coup the grace him. We forfeit all remaining attacks and try the coup + +// if oAttacker does not do a coup de grace, we roll a normal attack roll +// if the attack roll hits, AttackLoopLogic applies the damage, including any special effects +// special effects can be applied to all attacks, or only to the first attack in the round. + +// if we hit on our attack (and applied the damage), AttackLoopLogic determines whether it can do a circle kick +// if we can do the circle kick (e.g. have the feat, there is a different target in melee range, and we did not already do +// a circle kick in the round) AttackLoopLogic calls itself recursively to do the circle kick + +// after the attack was performed (successful or not), AttackLoopLogic checks whether the defender is dead +// if (s)he dead, AttackLoopLogic will look for a new defender + +// it the new defender is in melee range (and it actually was oAttacker that killed it), we check for the +// cleave feat, and do the cleave by calling AttackLoopLogic recursively + +// if the new defender is out of range, we move to it (unless we have a ranged weapon), and hope we +// are in range on the next attack + +// when AttackLoopLogic has done the attack (with all associated cleave and circle kick attacks), it checks whether there are any attacks left in the round +// if so, it decrements the AB-modifier for multiple attacks and calls AttackLoopMain (with the proper delay) to schedule the next attack +*/ +void AttackLoopLogic(object oDefender, object oAttacker, + int iBonusAttacks, int iMainAttacks, int iOffHandAttacks, int iMod, + struct AttackLoopVars sAttackVars, struct BonusDamage sMainWeaponDamage, + struct BonusDamage sOffHandWeaponDamage, struct BonusDamage sSpellBonusDamage, + int iOffhand, int bIsCleaveAttack) +{ + + int iAction = GetCurrentAction(oAttacker); + bFirstAttack = !sAttackVars.iAttackNumber; + + if (DEBUG) DoDebug("entered AttackLoopLogic: bFirstAttack = " + IntToString(bFirstAttack) + ", cleave = " + IntToString(bIsCleaveAttack) + ", current action = " + GetActionName(iAction)); + if (DEBUG) DoDebug("AttackLoopLogic: iMainAttacks = " + IntToString(iMainAttacks) + ", iOffHandAttacks = " + IntToString(iOffHandAttacks) + ", iBonusAttacks = " + IntToString(iBonusAttacks)); + + int bIsRangedAttack = sAttackVars.bIsRangedWeapon || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED; + + // check for valid target etc., but only if it is not a cleave or circle kick (in this case we checked all of this before) + if (!bIsCleaveAttack) + { + // if we are not attacking, abort (we loose all attacks which might be left in the round) + // we only check for an abort, if we are in the heartbeat combat mode (such as natural weapon + // attacks, or offhand attacks scheduled from a HB and running in parallel with aurora physical combat) + if ((sAttackVars.bMode & PRC_COMBATMODE_HB)) + { + // the following check only works, if PRC and aurora combat systems run in parallel, so that aurora sets the attack action properly + // we check the current action, and if it is not equal to ACTION_ATTACKOBJECT or ACTION_MOVETOPOINT, we return + // if PRC combat is to do an attack regardless of the current action state of oAttacker + // we must set the local int "prc_action_attack" to TRUE in advance (and then delete it with a DelayCommand() after we did the attack) + if (iAction != ACTION_ATTACKOBJECT && iAction != ACTION_MOVETOPOINT) +// if(!GetIsPhysicalCombatAction(GetCurrentAction(oAttacker))) + { + if (DEBUG) DoDebug("AttackLoopLogic: current action is not ACTION_ATTACKOBJECT or ACTION_MOVETOPOINT - aborting"); + return; + } + } + + // now determine whether it makes sense to switch to a better target in the following function CheckForChangeOfTarget() + // if the original target lives, we only allow a switch to a better target, if it is not a ranged attack and we set the respective PRC switch + int bAllowSwitchOfTarget = !bIsRangedAttack && GetPRCSwitch(PRC_ALLOW_SWITCH_OF_TARGET); + if (sAttackVars.bMode & PRC_COMBATMODE_ALLOW_TARGETSWITCH) + { + // now catch any changes in targeting that the parallell running aurora engine might have enforced and return the best target + oDefender = CheckForChangeOfTarget(oAttacker, oDefender, bAllowSwitchOfTarget); + } + + // if after all the trouble looking for a valid target we did not find one, abort the attack + if(oDefender == OBJECT_INVALID || GetIsDead(oDefender)) + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: no enemies left - aborting"); + return; + } + + // If oDefender is not within melee range and it is not a ranged attack + // move to oDefender so that we can attack next round. We give up all remaining attacks in the round, unless we can do a 5 foot step + if(!bIsRangedAttack && !GetIsInMeleeRange(oDefender, oAttacker)) + { + // can we do a 5 foot step in order to get into melee range? + float fDistance = GetDistanceBetween(oDefender, oAttacker) - GetSizeAdjustment(oDefender, oAttacker); + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: target is out of melee range, size adjusted distance = "+FloatToString(fDistance)+", size adjustment = "+FloatToString(GetSizeAdjustment(oDefender, oAttacker))); + if (!sAttackVars.bFiveFootStep && fDistance <= RANGE_15_FEET_IN_METERS) + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: waiting for aurora engine to do 5 foot step to move to melee range of " + GetName(oDefender) + ", current action: " + GetActionName(GetCurrentAction(oAttacker))); + + // motu99: The problem is, in order to move into range we must clear the attack action, otherwise the move will not be done + // If we clear the attack action, we must issue an ActionAttack after the move + // But this will initiate a new combat round (cutting off any attacks left from the old round) + // The ugly workaround is to just wait and hope the aurora engine does the move for us + // therefore the following statement is commented out + // ClearAllActionsMoveToObjectAndAttack(oDefender, MELEE_RANGE_METERS-1.); + + sAttackVars.bFiveFootStep = TRUE; // remember that we did a five foot step + + // now call attackLoopLogic with a delay and exactly the same parameters (and hope we are in range then) + DelayCommand(1.0, + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, + iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, + iOffhand, bIsCleaveAttack)); + // this return statement will not give up any pending attacks, because we called AttackLoopLogic in a DelayCommand beforehand + return; + } + else if (fDistance <= fMaxAttackDistance) + { + // Our closest enemy is out of melee range (e.g. more than 10 feet away) and we cannot do a 5 foot step to get into range + // This means we need a full move action, e.g. we must give up all remaining attacks in the round + // so we call ActionAttack() in order to move to our enemy and let the aurora engine start a new combat round + // against oDefender, whenever we are in range + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: doing full move action to get into melee range of " + GetName(oDefender) + ", current action: " + GetActionName(GetCurrentAction(oAttacker))); + + // Note that in general we do not wan't to use the ActionAttack() command within PRC combat, because this will initiate + // a new attack round by the aurora engine. But here the rules require us to start a new combat round anyway. + // AssignCommand(oAttacker, ClearAllActionsAndAttack(oDefender)); + ClearAllActions(); + ActionAttack(oDefender); + + // now determine whether we abort the scripted combat round, or if we just delay the next attack (and hope we are in range then) + if (sAttackVars.bMode & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE) + { + // The following return statement will terminate the whole combat round + return; + } + else + { + // call attackLoopLogic with a delay and exactly the same parameters (and hope we are in range then) + DelayCommand(1.0, + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, + iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, + iOffhand, bIsCleaveAttack)); + // this return statement will not give up any pending attacks, because we called AttackLoopLogic in a DelayCommand beforehand + return; + } + } + else + { + // our closest enemy is so far away, it does not make sense to attack it; just drop the attack + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: target " + GetName(oDefender) + " is too far away, current action: " + GetActionName(GetCurrentAction(oAttacker))); + // The following return statement will terminate the whole combat round + return; + } + } + + // Since we are attacking, remove sanctuary / invisibility effects. + // Only bother to do this on the first attack... + // as they won't have the effect anymore on subsequent iterations. + if (bFirstAttack) + { + // FrikaC: Ghost strike doesn't cancel ethereal / invisible + if( !GetLocalInt(oAttacker, "prc_ghost_strike") + && ( PRCGetHasEffect(EFFECT_TYPE_INVISIBILITY, oAttacker) + || PRCGetHasEffect(EFFECT_TYPE_SANCTUARY, oAttacker) + ) + ) + { // now remove sanctuary and invisibility effects from attacker + // if (DEBUG) DoDebug("AttackLoopLogic: remove invisibility and sanctuary"); + + effect eEffect = GetFirstEffect(oAttacker); + while (GetIsEffectValid(eEffect) ) + { + int iType = GetEffectType(eEffect); + if( iType == EFFECT_TYPE_INVISIBILITY || iType == EFFECT_TYPE_SANCTUARY ) + // motu99: Why delay? What with instant attacks? + DelayCommand(0.01, RemoveEffect(oAttacker, eEffect)); + + eEffect = GetNextEffect(oAttacker); + } + } + + // take the player out of stealth mode + if(GetActionMode(oAttacker, ACTION_MODE_STEALTH) ) + { + // if (DEBUG) DoDebug("AttackLoopLogic: take attacker out of stealth mode"); + SetActionMode(oAttacker, ACTION_MODE_STEALTH, FALSE); + } + } + } + + // everything is set in order to actually do the attack + + effect eDamage; + effect eInvalid; + string sMes = ""; + int iAttackRoll = 0; + int bIsCritcal = FALSE; + + // set duration type of special effect based on passed value + int iDurationType = DURATION_TYPE_INSTANT; + if (sAttackVars.eDuration > 0.0) iDurationType = DURATION_TYPE_TEMPORARY; + if (sAttackVars.eDuration < 0.0) iDurationType = DURATION_TYPE_PERMANENT; + + // check defender HP before attacking + // motu99: HP check is most likely redundant, because we checked for a dead oDefender beforehand + if(GetCurrentHitPoints(oDefender) > 0) + { +// DoDebug("AttackLoopLogic: found living target - " + GetName(oDefender)); + + // weapon variables have to be initialized for the hand that does the attack + object oWeapon; + int iAttackBonus; + int iWeaponDamageRound; + int iNumDice; + int iNumSides; + int iCritMult; + struct BonusDamage sWeaponDamage; + + if (iOffhand) + { // if attack is from left hand set vars to left hand values + oWeapon = sAttackVars.oWeaponL; + iAttackBonus = sAttackVars.iOffHandAttackBonus; + iWeaponDamageRound = sAttackVars.iOffHandWeaponDamageRound; + iNumDice = sAttackVars.iOffHandNumDice; + iNumSides = sAttackVars.iOffHandNumSides; + iCritMult = sAttackVars.iOffHandCritMult; + sWeaponDamage = sOffHandWeaponDamage; + } + else + { // attack is from main hand, set vars to right hand values + oWeapon = sAttackVars.oWeaponR; + iAttackBonus = sAttackVars.iMainAttackBonus; + iWeaponDamageRound = sAttackVars.iMainWeaponDamageRound; + iNumDice = sAttackVars.iMainNumDice; + iNumSides = sAttackVars.iMainNumSides; + iCritMult = sAttackVars.iMainCritMult; + sWeaponDamage = sMainWeaponDamage; + } + + int bIsTouchAttackSpell = sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || sAttackVars.iTouchAttackType == TOUCH_ATTACK_MELEE_SPELL; + int bHasCriticalImmunity = GetIsImmune(oDefender, IMMUNITY_TYPE_CRITICAL_HIT, oAttacker); + + // will be true on any instant death effects (Coup de Grace, devastating critical) + int bInstantKill = FALSE; + + // Coup De Grace + // Automatic critical hit: Fort save DC: 10 + damage dealt + // Note: The rest of the code is in GetAttackDamage + // this is because that part has the damage dealt in it. + + // motu99: Do we always want a coup de grace? A strong fighter might do better without (cleaving several enemies to death in a round) + // maybe we should use a switch in order to disable automatic coup de grace? + // this should be a switch on the PC, not the module, and we need to access it via the PRC-menu + int bDisableCoupDeGrace = GetPRCSwitch(PRC_DISABLE_COUP_DE_GRACE); + + if (!bDisableCoupDeGrace && !GetLocalInt(oAttacker, "PactQuality"+IntToString(VESTIGE_BUER)) && GetHasSpellEffect(VESTIGE_BUER, oAttacker)) bDisableCoupDeGrace = TRUE; + + if( !bDisableCoupDeGrace + && bFirstAttack + && !bHasCriticalImmunity + && GetIsHelpless(oDefender)) + { + if(DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: attempting coup the grace"); + // make hit a crit + iAttackRoll = 2; + + // remove all other attacks this round + // you give up all other attacks to make a coupe de grace + iBonusAttacks = 0; + iMainAttacks = 0; + iOffHandAttacks = 0; + + // apply the CDG directly, if spell ability (otherwise do it in the GetDamageRoll() function) + if(bIsTouchAttackSpell) + { + // DC = 10 + damage dealt. + int iSaveDC = 10; + int iDamage = sAttackVars.iDamageModifier; + + // if the attack effects all attacks use DAMAGE_BONUS_* const + if(sAttackVars.bEffectAllAttacks) iDamage = GetDamageByConstant(iDamage, FALSE); + + iSaveDC += iDamage; + + if (DEBUG) DoDebug("AttackLoopLogic: coup de grace as a spell like touch attack - trying fortitude save with DC = " + IntToString(iSaveDC)); + if(!FortitudeSave(oDefender, iSaveDC, SAVING_THROW_TYPE_NONE, oAttacker) ) + { + sMes = "*Coup De Grace*"; + if (DEBUG) + { + sMes = "scripted " + sMes; + //SendMessageToPC(oAttacker, sMes); + } + + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); + + // circumvents death immunity... since anyone CDG'ed is dead. + effect eDeath = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oDefender); + if (GetIsDead(oDefender)) + bInstantKill = TRUE; + } + } + } // End Coup de Grace (CDG) + else + { + // if not CDG, perform normal attack roll + if (DEBUG) DoDebug("AttackLoopLogic: do normal attack roll"); + iAttackRoll = GetAttackRoll(oDefender, oAttacker, oWeapon, iOffhand, iAttackBonus, iMod, TRUE, 0.0, sAttackVars.iTouchAttackType); + } + + // was it a critical? + if(iAttackRoll == 2) + { + bIsCritcal = TRUE; + SetLocalInt(oDefender, "PRCCombat_CriticalHit", TRUE); + DelayCommand(1.0, DeleteLocalInt(oDefender, "PRCCombat_CriticalHit")); + } + + // This sets a local variable on the target that is struck + // Allows you to apply saves and such based on the success or failure + if(bFirstAttack && iAttackRoll) + { + if (DEBUG) DoDebug("AttackLoopLogic: hit on first attack - setting PRC local int"); + SetLocalInt(oDefender, "PRCCombat_StruckByAttack", TRUE); + DelayCommand(1.0, DeleteLocalInt(oDefender, "PRCCombat_StruckByAttack")); + } + + // if critical hit and vorpal weapon, apply vorpal effect, but only if we didn't coup de grace them before + if(!bInstantKill && bIsCritcal && bIsVorpalWeaponEquiped) + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: critical hit with vorpal weapon effect - Defender must do fortitude save with DC " + IntToString(iVorpalSaveDC)); + if( !FortitudeSave(oDefender, iVorpalSaveDC, SAVING_THROW_TYPE_NONE) ) + { + sMes = "*Vorpal Blade*"; + if (DEBUG) + { + sMes = "scripted " + sMes; + //SendMessageToPC(oAttacker, sMes); + } + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); + + effect eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oDefender); + + effect eDeath = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oDefender); + if (GetIsDead(oDefender)) + bInstantKill = TRUE; + } + } // end of code for vorpal weapon + + // reset global vorpal variables + bIsVorpalWeaponEquiped = FALSE; + iVorpalSaveDC = 0; + + int bDoSpecialEffect = ( bFirstAttack || sAttackVars.bEffectAllAttacks ); + // now do the messages + if(iAttackRoll) + { // messages for *hit* + // motu99: moved this from special effects section to here + // Don't quite sure if the sMessageSuccess/sMessageFailure strings are only for special attacks, or all attacks + // I would assume all. If not correct, move this code to the "special effects" section + if(bDoSpecialEffect && sAttackVars.sMessageSuccess != "") + FloatingTextStringOnCreature(sAttackVars.sMessageSuccess, oAttacker, FALSE); + if (DEBUG) + SendMessageToPC(oAttacker, sAttackVars.sMessageSuccess); + + // if this attack is a cleave attack or a circle kick + if(bIsCleaveAttack) + { // motu99: 3 means circle kick, 2 means great cleave, 1 is normal cleave (don't need to check for feats twice) + if(bIsCleaveAttack == 3) + sMes = "*Circle Kick Hit*"; + else if(bIsCleaveAttack == 2) + sMes = "*Great Cleave Hit*"; + else + sMes = "*Cleave Attack Hit*"; + + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); + } + } + else // messages for *miss* + { + if(bDoSpecialEffect && sAttackVars.sMessageFailure != "") + FloatingTextStringOnCreature(sAttackVars.sMessageFailure, oAttacker, FALSE); + if (DEBUG) SendMessageToPC(oAttacker, sAttackVars.sMessageFailure); + + // we tried a cleave attack and missed + if(bIsCleaveAttack) + { + if(bIsCleaveAttack == 3) + sMes = "*Circle Kick Miss*"; + else if(bIsCleaveAttack == 2) + sMes = "*Great Cleave Miss*"; + else + sMes = "*Cleave Attack Miss*"; + + FloatingTextStringOnCreature(sMes, oAttacker, FALSE); + } + } // end of code for messages + + // now do the real stuff + // if we hit the enemy (and did not kill it instantly by CDG or vorpal) + if(!bInstantKill && iAttackRoll) + { + // only calculate damage if it is not a touch attack spell + if (!bIsTouchAttackSpell) + eDamage = GetAttackDamage(oDefender, oAttacker, oWeapon, sWeaponDamage, sSpellBonusDamage, iOffhand, iWeaponDamageRound, bIsCritcal, iNumDice, iNumSides, iCritMult); +/* + // apply the damage after a short delay + // motu99: why delay? If we delay, we cannot check for cleave later on + DelayCommand(0.01, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oDefender)); +*/ + // apply the damage directly, unless there is none + if (eDamage != eInvalid) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oDefender); +/* + // apply any on hit abilities from attackers weapon to defender shortly after damage; motu99: why delay? Check for dead target (and cleave/circle kick) is useless if we apply the damage outside AttackLoopLogic + DelayCommand(0.02,ApplyOnHitAbilities(oDefender, oAttacker, oWeapon)); +*/ + // apply on hit abilities of the attackers weapon to the defendor + ApplyOnHitAbilities(oDefender, oAttacker, oWeapon); + + // motu99: if it is a ranged attack, also apply the on hit abilities of the ammunition to the target + // (don't know if this makes sense, but there are blessed arrows and other stuff) + if (sAttackVars.oAmmo != OBJECT_INVALID && sAttackVars.oAmmo != oWeapon) + ApplyOnHitAbilities(oDefender, oAttacker, sAttackVars.oAmmo); + + // immediately apply any on hit abilities from defenders armor to attacker + // the bioware engine applies the onhit abilities of the armor in the context of the defender; we do it in the context of the attacker (so that we can apply the abilities instantly) + // If in the future we must do it the Bioware way, we have to use AssignCommand(oDefender, AppyOnHitAbilities(oAttacker, oDefender, oArmor)), which will apply the onhit + // capabilities of the armor in the context of oDefender. However, AssignCommands are executed AFTER the script is finished, so this will not be an instant application + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oDefender); + if( GetIsObjectValid(oArmor) ) + ApplyOnHitAbilities(oAttacker, oDefender, oArmor); + } + + // we hit: now do special effect (either applies to all attacks or to the first attack) + if(bDoSpecialEffect) + { + if (DEBUG) DoDebug("AttackLoopLogic: looking for special effects"); + // motu99: don't know if this works with negative damage + // if not, change "!= 0" to "> 0" + if(sAttackVars.iDamageModifier != 0) + { + int iDamagePower; + int iDamage; + + if (bIsTouchAttackSpell) // set damage power to normal for a touch spell + iDamagePower = DAMAGE_POWER_NORMAL; + else // otherwise, calculate damage power + iDamagePower = GetDamagePowerConstant(oWeapon, oDefender, oAttacker); + + // if special applies only to first attack, the damage should be given directly + iDamage = sAttackVars.iDamageModifier; + + // otherwise (special applies to all attacks) the damage given should be a DAMAGE_BONUS_* const + // motu99: don't know why this is handled so, but it says so in the description of PerformAttack(), so we do it + if (!bFirstAttack) + iDamage = GetDamageByConstant(iDamage, FALSE); + // Bypass damage reduction for effect damage if set + if(GetLocalInt(oAttacker, "MoveIgnoreDR")) + { + struct DamageReducers drReduced = GetTotalReduction(oAttacker, oDefender, oWeapon); + int nRedDR = drReduced.nStaticReductions * 100 / (100 - drReduced.nPercentReductions); + iDamage += nRedDR; + if(DEBUG) DoDebug("Damage increased by " + IntToString(nRedDR) + " to ignore DR with effects"); + } + if(DEBUG) DoDebug("AttackLoopLogic: found special effect (iDamageModifier = "+IntToString(iDamage)+") - now applying damage"); + + // apply the special effect damage + // motu99: maybe we should link this damage to the "normal" weapon damage? + effect eBonusDamage = EffectDamage(iDamage, sAttackVars.iDamageType, iDamagePower); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eBonusDamage, oDefender); + } + + // apply any special effects + // motu99: added check for invalid effect + if(sAttackVars.eSpecialEffect != eInvalid) + { + if (DEBUG) DoDebug("AttackLoopLogic: found special effect (eSpecialEffect) - now applying effect"); + //struct PRCeffect eSpecialEffect = GetLocalPRCEffect(GetModule(), sAttackVars.sEffectLocalName); + ApplyEffectToObject(iDurationType, sAttackVars.eSpecialEffect, oDefender, sAttackVars.eDuration); + } + } + } // end of code for a *hit* (iAttackRoll > 0), excluding instant kills (bInstantKill == FALSE) + + // stuff we have to do after an attack, regardless if we missed or not + // not the first attack any more + sAttackVars.iAttackNumber++; + bFirstAttack = !sAttackVars.iAttackNumber; + + // Code to remove ammo from inventory after an attack is made + if( sAttackVars.bIsRangedWeapon ) + { + if (DEBUG) DoDebug("AttackLoopLogic: reducing ammunition"); + SetItemStackSize(sAttackVars.oAmmo, (GetItemStackSize(sAttackVars.oAmmo) - 1) ); + } + + // code for circle kick + // DoDebug("AttackLoopLogic: check for circle kick"); + if( iAttackRoll // only if we scored a hit + && sAttackVars.iCircleKick == 0 // only if we didn't yet do a circle kick + && GetHasMonkWeaponEquipped(oAttacker) // we must be unarmed (or wield a kama) + && GetHasFeat(FEAT_CIRCLE_KICK, oAttacker) ) // and we need the feat + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: *hit* - now attempting circle kick"); + // Find nearest enemy creature within 10 feet + /* + // motu99: logic is screwed. Mostly we will be taking the second nearest creature, because we discard the nearest before looking whether it is valid and in range + int iVal = 1; + int bHasValidTarget = FALSE; + int bIsWithinRange = TRUE; + object oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iVal, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + while(GetIsObjectValid(oTarget) && !bHasValidTarget && bIsWithinRange ) + { + iVal += 1; + oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oAttacker, iVal, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, -1, -1); + + // will cause the loop to end on a valid target + if(oTarget != oDefender && GetIsInMeleeRange(oDefender, oAttacker) ) + bHasValidTarget = TRUE; + + // will cause the loop to end if there are no valid enemies within range + if(!GetIsInMeleeRange(oDefender, oAttacker) ) + bIsWithinRange = FALSE; + } + */ + object oCircleKickDefender = FindNearestNewEnemyWithinRange(oAttacker, oDefender); + if( GetIsObjectValid(oCircleKickDefender) + && !GetIsDead(oCircleKickDefender) + && GetIsInMeleeRange(oCircleKickDefender, oAttacker) ) + { + // DoDebug("AttackLoopLogic: found valid target for circle kick " + GetName(oCircleKickDefender)); + + // remember that we did a circle kick in the round (so we cannot do any more) + sAttackVars.iCircleKick++; + // not sure, if circle kick comes at full AB, assumed so (if not, comment iMod in again) + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, 0 /*iMod*/, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, iOffhand, 3); + } + else + { + if(DEBUG) DoDebug("AttackLoopLogic: no valid target for circle kick"); + } + } // end code for circle kick + + } // end of code for oDefender with hitpoints HP > 0 + else + { // this stub is for any code possibly required for the case when oDefender had HP <= 0 on entry + } + + // we are now through with the original attack sequence + // now we check HP of enemy to see if they are alive still or not + // note that defender could have been below 1 HP from the beginning - we can see whether we actually killed it in *this* attack, if iAttackRoll > 0 + // motu99: problem in original code was, that damage application was delayed (except for a coup de grace), + // so we couldn't have noticed in the following checks whether they would be dead; changed that + //if (DEBUG) DoDebug("AttackLoopLogic: check for dead enemy"); + if(!GetIsObjectValid(oDefender) || GetCurrentHitPoints(oDefender) <= 0) + { + //if (DEBUG) DoDebug("AttackLoopLogic: enemy dead or invalid after attack"); + + // if enemy is dead find a new target (we are absolutely free in choosing a new one, so we take the closest) + oDefender = FindNearestEnemy(oAttacker); + + // if there is no new valid target, then no more attacks + if( !GetIsObjectValid(oDefender) || GetIsDead(oDefender)) + { + oDefender = OBJECT_INVALID; + if(DEBUG) DoDebug(PRC_TEXT_WHITE + "No new valid targets to attack - Aborting"); + return; + } + if (DEBUG) DoDebug(PRC_TEXT_WHITE+"AttackLoopLogic: old target dead or invalid, found new target - " + GetName(oDefender)); + + if (!bIsRangedAttack) // if it is not a ranged attack, we check if we are in range for a cleave + { + if(!GetIsInMeleeRange(oDefender, oAttacker)) + { + if (GetDistanceBetween(oDefender, oAttacker) <= fMaxAttackDistance) + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE+"AttackLoopLogic: new target not in melee range - move to and attack new target; current action = " + GetActionName(GetCurrentAction(oAttacker))); + // if no enemy is close enough, move to the nearest target and attack + // note that this will initiate a new combat round by the aurora engine + // ClearAllActionsMoveToObjectAndAttack(oDefender, MELEE_RANGE_METERS-1.); + ClearAllActions(); + ActionAttack(oDefender); + } + else + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE+"AttackLoopLogic: new target not in melee range and too far away - do nothing; current action = " + GetActionName(GetCurrentAction(oAttacker))); + } + + // motu99: commented out the return statement, because we still want to continue fighting + // and hope that we are in melee range when the next attack within the current round actually occurs + // returning means abort; so we forfeit any attacks that might still be left in the round! + // However, we might have to check whether the distance is too large to be covered + // according to PnP rules we can at most do a 5 foot step without canceling the full attack round + // return; + } + else if (iAttackRoll) + { // we did an attack that must have killed the original defender, we are within melee range of the new defender + // we are not wielding a ranged weapon, so we are ready to cleave, if we have the feats + if (DEBUG) DoDebug("AttackLoopLogic: old target dead and new target in melee range - check for cleave"); + + int bHasCleave = 0; + if(GetHasFeat(FEAT_GREAT_CLEAVE, oAttacker)) + bHasCleave = 2; + else if(sAttackVars.iCleaveAttacks == 0 && GetHasFeat(FEAT_CLEAVE, oAttacker)) + bHasCleave = 1; + + if(bHasCleave) + { + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "AttackLoopLogic: we can cleave - initiate cleave attack"); + // perform cleave + // recall this function with Cleave = TRUE + sAttackVars.iCleaveAttacks++; // note that due to the recursive calls this does not count any subsequent cleaves in the cleave attack itself! + // cleave attacks come at the AB of the attack that killed the critter, so use current value of iMod + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, iOffhand, bHasCleave); + } + } + } + } + + // we are through + + // if it was a cleave attack, return directly from the recursive call (we don't wan't to decrement iMod, nor do we want to call AttackLoopMain at this point) + // by returning to the instance of AttackLoopLogic that called the cleave, we will eventually land in the instance of AttackLoopLogic() that initiated the cleave(s). + // This (first) instance, however, does have bIsCleaveAttack = FALSE, so when we are eventually through with all recursive cleave attacks, + // we will not return in that (first) instance (which would mean giving up all left over attacks in the round). Rather we check whether there are still + // attacks left in the round and call AttackLoopMain with an appropriate delay in order to do the next one. + if (bIsCleaveAttack) + return; + + // now calculate whether we must decrement the attack bonus (iMod) + if(iBonusAttacks > 0) + { + iBonusAttacks --; + } + // only decrement iMod, if we are not performing a cleave attack and are through with all bonus attacks + //else if(iBonusAttacks < 0) + else if (0 >= iBonusAttacks) + { + if (iOffHandAttacks > 0 && iMainAttacks == iOffHandAttacks) + { + // Has the same number of main and off-hand attacks left + // thus the player has attacked with both main and off-hand + // and should now have -5 to their next attack iterations. + if (DEBUG) DoDebug("AttackLoopLogic: Decrement iMod Offhand"); + if(!sAttackVars.bUseMonkAttackMod) iMod -= 5; + else iMod -= 3; + } + else if(iOffHandAttacks == 0) + { + // if iOffHandAttacks = 0 and through with all bonus attacks + // then the player only has main hand attacks + // thus they should have their attack decremented + + // motu99: off hand attacks should be decremented by -5, even when we wield a monk weapon. Not yet implemented, because we need different iMods for mainhand and offhand + if(!sAttackVars.bUseMonkAttackMod) iMod -= 5; + if (DEBUG) DoDebug("AttackLoopLogic: Decrement iMod Mainhand"); + else iMod -= 3; + } + } + + if (DEBUG) DoDebug("AttackLoopLogic: go back to main attack loop with APR penalty of " + IntToString(iMod)); + // go back to main part of loop, but only if there are still attacks left + if (iBonusAttacks > 0 || iOffHandAttacks + iMainAttacks > 0) + DelayCommand(sAttackVars.fDelay, AttackLoopMain(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage) ); + else if ( sAttackVars.bMode & PRC_COMBATMODE_ACTIONATTACK_ON_LAST) + ActionAttack(oDefender); +} + +void AttackLoopMain(object oDefender, object oAttacker, + int iBonusAttacks, int iMainAttacks, int iOffHandAttacks, + int iMod, struct AttackLoopVars sAttackVars, struct BonusDamage sMainWeaponDamage, + struct BonusDamage sOffHandWeaponDamage, struct BonusDamage sSpellBonusDamage) +{ + if(DEBUG) DoDebug("Entered AttackLoopMain: bonus attacks = " + IntToString(iBonusAttacks)+", main attacks = "+IntToString(iMainAttacks)+", offhand attacks = "+IntToString(iOffHandAttacks)); + + // ugly workaround to make this global available for other functions after a call to DelayCommand or AssignCommand + bUseMonkAttackMod = sAttackVars.bUseMonkAttackMod; + + // turn off touch attack if var says it only applies to first attack + if (sAttackVars.iAttackNumber && !sAttackVars.bApplyTouchToAll) sAttackVars.iTouchAttackType == FALSE; + + // turn off AB-mod if var says it only applies to first attack + if (sAttackVars.iAttackNumber && !sAttackVars.bEffectAllAttacks) iMod = 0; + + // perform all bonus attacks + if(iBonusAttacks > 0) + { // motu99: with perfect two weapon fighting (PTWF) there could be bonus attacks in the offhand as well! + // however, here we are assuming that all bonus attacks are from the main hand alone + // @TODO: find a way to implement PTWF with bonus attacks in main and offhand + if(DEBUG) DoDebug("AttackLoopMain: Calling AttackLoopLogic - bonus"); + iBonusAttacks --; + // note that attacklooplogic is called with attacks that are left + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, 0, FALSE); + } + // perform main attack, if there are at least as many main hand attacks left as off-hand attacks + else if(iMainAttacks > 0 && iMainAttacks >= iOffHandAttacks) + { + if(DEBUG) DoDebug("AttackLoopMain: Calling AttackLoopLogic - main hand"); + iMainAttacks --; + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, 0, FALSE); + } + // if there are more offhand attacks left than mainhand attacks, do those + else if(iOffHandAttacks > 0) + { + if(DEBUG) DoDebug("AttackLoopMain: Calling AttackLoopLogic - offhand"); + iOffHandAttacks --; + AttackLoopLogic(oDefender, oAttacker, iBonusAttacks, iMainAttacks, iOffHandAttacks, iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage, 1, FALSE); + } +// DoDebug("Exiting AttackLoopMain: no attacks left"); +} + +void PerformAttackRound(object oDefender, object oAttacker, + effect eSpecialEffect, float eDuration = 0.0, int iAttackBonusMod = 0, + int iDamageModifier = 0, int iDamageType = 0, int bEffectAllAttacks = FALSE, + string sMessageSuccess = "", string sMessageFailure = "", + int bApplyTouchToAll = FALSE, int iTouchAttackType = FALSE, + int bInstantAttack = FALSE, int bCombatModeFlags = 0) +{ +// if (DEBUG) DoDebug("Entered PerformAttackRound"); + + // create struct for attack loop logic + struct AttackLoopVars sAttackVars; + + // store the combat mode flags + sAttackVars.bMode = bCombatModeFlags; + + // set variables required in attack loop logic + sAttackVars.oWeaponR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oAttacker); + sAttackVars.oWeaponL = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oAttacker); + + // weapon base item type of right hand weapon + int iMainhandWeaponType = GetBaseItemType(sAttackVars.oWeaponR); + + sAttackVars.bIsRangedWeapon = GetIsRangedWeaponType(iMainhandWeaponType); + sAttackVars.iDamageModifier = iDamageModifier; + sAttackVars.iDamageType = iDamageType; + + sAttackVars.eSpecialEffect = eSpecialEffect; + //post prc-effectness + //sAttackVars.sEffectLocalName = "CombatStructEffect_"+ObjectToString(oDefender)+"_"+ObjectToString(oAttacker); + //SetLocalPRCEffect(GetModule(), sAttackVars.sEffectLocalName, eSpecialEffect); + //this says e but is really a float + sAttackVars.eDuration = eDuration; + sAttackVars.bEffectAllAttacks = bEffectAllAttacks; + sAttackVars.bApplyTouchToAll = bApplyTouchToAll; + sAttackVars.iTouchAttackType = iTouchAttackType; + sAttackVars.sMessageSuccess = sMessageSuccess; + sAttackVars.sMessageFailure = sMessageFailure; + + // are they using a two handed weapon? + int bIsTwoHandedMeleeWeapon = GetIsTwoHandedMeleeWeaponType(iMainhandWeaponType); + + // are they unarmed? + int bIsUnarmed = FALSE; + if(iMainhandWeaponType == BASE_ITEM_INVALID) + bIsUnarmed = TRUE; + + // if player is unarmed use gloves as weapon + if(bIsUnarmed) + sAttackVars.oWeaponR = GetItemInSlot(INVENTORY_SLOT_ARMS, oAttacker); + + int bIsUsingTwoWeapons = FALSE; + int iOffhandWeaponType = GetBaseItemType(sAttackVars.oWeaponL); + + // is the player is using two weapons or double sided weapons? + if(GetIsOffhandWeaponType(iOffhandWeaponType)) + bIsUsingTwoWeapons = TRUE; + else if(GetIsDoubleSidedWeaponType(iMainhandWeaponType)) // motu99: included double sided weapons + { + bIsUsingTwoWeapons = TRUE; + iOffhandWeaponType = iMainhandWeaponType; + sAttackVars.oWeaponL = sAttackVars.oWeaponR; + } + + + // determine extra bonus damage from spells (on the attacker) + //Now checks defender for alignment and racial type -ebonfowl + struct BonusDamage sSpellBonusDamage = GetMagicalBonusDamage(oAttacker, oDefender); + + // structs for damage on main and offhand weapons + struct BonusDamage sMainWeaponDamage; + struct BonusDamage sOffHandWeaponDamage; + + // find out the number of bonus attacks from haste and spell like abilities and the penalties associated with them + struct BonusAttacks sBonusAttacks = GetBonusAttacks(oAttacker); + + // find out last attack mode to check whether it gives us bonus attacks (and penalties) + int iLastAttackMode = GetLastAttackMode(oAttacker); + if( iLastAttackMode == COMBAT_MODE_FLURRY_OF_BLOWS || iLastAttackMode == COMBAT_MODE_RAPID_SHOT ) + { + sBonusAttacks.iNumber ++; + sBonusAttacks.iPenalty += 2; + } + + // number of attacks with main hand + int iMainHandAttacks = GetMainHandAttacks(oAttacker); + + // ugly workaround (GetMainHandAttacks calculated this, and returns it in the quasi-global) + sAttackVars.bUseMonkAttackMod = bUseMonkAttackMod; + + // determine main hand attack bonus and damage that remains constant througout a round + if (iMainHandAttacks || sBonusAttacks.iNumber) + { + // determine attack bonus + sAttackVars.iMainAttackBonus = GetAttackBonus(oDefender, oAttacker, sAttackVars.oWeaponR, 0); + + // Determine physical damage per round (cached for multiple use) + sAttackVars.iMainWeaponDamageRound = GetWeaponDamagePerRound(oDefender, oAttacker, sAttackVars.oWeaponR, 0); + + + + // variables that store extra damage dealt + sMainWeaponDamage = GetWeaponBonusDamage(sAttackVars.oWeaponR, oDefender); + + if (!bIsUnarmed) + { + // we are using a weapon: get weapon information + sAttackVars.iMainNumSides = StringToInt(Get2DACache("baseitems", "DieToRoll", iMainhandWeaponType)); + sAttackVars.iMainNumDice = StringToInt(Get2DACache("baseitems", "NumDice", iMainhandWeaponType)); + } + // we are unarmed, now check if we are a monk or have a creature weapon from a PrC class. - Brawler, Shou, IoDM, etc. + else if(GetIsUnarmedFighter(oAttacker)) + { + int iDamage = FindUnarmedDamage(oAttacker); + sAttackVars.iMainNumSides = StringToInt(Get2DACache("iprp_monstcost", "Die", iDamage)); + sAttackVars.iMainNumDice = StringToInt(Get2DACache("iprp_monstcost", "NumDice", iDamage)); + } + // we are unarmed and not a monk or a PrC class with a creature weapon + // so we use normal fists + else + { + sAttackVars.iMainNumSides = 3; + sAttackVars.iMainNumDice = 1; + } + sAttackVars.iMainCritMult = GetWeaponCritcalMultiplier(oAttacker, sAttackVars.oWeaponR); + } + + // off-hand variables + int iOffHandAttacks = 0; + +/* motu99: these are zero anyway + sAttackVars.iOffHandAttackBonus = 0; + sAttackVars.iOffHandWeaponDamageRound = 0; + sAttackVars.iOffHandNumSides = 0; + sAttackVars.iOffHandNumDice = 0; + sAttackVars.iOffHandCritMult = 0; +*/ + + // only run offhand code if using two weapons + if(bIsUsingTwoWeapons) + { + iOffHandAttacks = GetOffHandAttacks(oAttacker); + + // check if double sided weapon + if (iMainHandAttacks && sAttackVars.oWeaponL == sAttackVars.oWeaponR) + { + // double sided weapon: Just copy from main hand (but beware that AB -4 if no ambidex feat!) + sAttackVars.iOffHandAttackBonus = sAttackVars.iMainAttackBonus; + if (!GetHasFeat(FEAT_AMBIDEXTERITY, oAttacker)) + sAttackVars.iOffHandAttackBonus -= 4; + sOffHandWeaponDamage = sMainWeaponDamage; + sAttackVars.iOffHandWeaponDamageRound = sAttackVars.iMainWeaponDamageRound; + + sAttackVars.iOffHandNumSides = sAttackVars.iMainNumSides; + sAttackVars.iOffHandNumDice = sAttackVars.iMainNumDice; + sAttackVars.iOffHandCritMult = sAttackVars.iMainCritMult; + } + else // any other (non double sided) weapon: calculate + { + sAttackVars.iOffHandAttackBonus = GetAttackBonus(oDefender, oAttacker, sAttackVars.oWeaponL, 1); + sOffHandWeaponDamage = GetWeaponBonusDamage(sAttackVars.oWeaponL, oDefender); + sAttackVars.iOffHandWeaponDamageRound = GetWeaponDamagePerRound(oDefender, oAttacker, sAttackVars.oWeaponL, 1); + + sAttackVars.iOffHandNumSides = StringToInt(Get2DACache("baseitems", "DieToRoll", iOffhandWeaponType)); + sAttackVars.iOffHandNumDice = StringToInt(Get2DACache("baseitems", "NumDice", iOffhandWeaponType)); + sAttackVars.iOffHandCritMult = GetWeaponCritcalMultiplier(oAttacker, sAttackVars.oWeaponL); + } + } + + // Code to equip new ammo + // Equips new ammo if they don't have enough ammo for the whole attack round + // or if they have no ammo equipped. + if(!sAttackVars.bIsRangedWeapon) + { + sAttackVars.oAmmo = OBJECT_INVALID; + } + else + { + sAttackVars.oAmmo = GetAmmunitionFromWeapon(sAttackVars.oWeaponR, oAttacker); + + // if there is no ammunition search inventory for ammo + if( sAttackVars.oAmmo == OBJECT_INVALID + || GetItemStackSize(sAttackVars.oAmmo) <= (iMainHandAttacks + sBonusAttacks.iNumber +1) ) + { + sAttackVars.oAmmo = EquipAmmunition(oAttacker); + } + + struct BonusDamage sAmmoDamage = GetWeaponBonusDamage(sAttackVars.oAmmo, oDefender); + + // if these values are better than the weapon, then use these. + if(sAmmoDamage.dam_Acid > sMainWeaponDamage.dam_Acid) sMainWeaponDamage.dam_Acid = sAmmoDamage.dam_Acid; + if(sAmmoDamage.dam_Cold > sMainWeaponDamage.dam_Cold) sMainWeaponDamage.dam_Cold = sAmmoDamage.dam_Cold; + if(sAmmoDamage.dam_Fire > sMainWeaponDamage.dam_Fire) sMainWeaponDamage.dam_Fire = sAmmoDamage.dam_Fire; + if(sAmmoDamage.dam_Elec > sMainWeaponDamage.dam_Elec) sMainWeaponDamage.dam_Elec = sAmmoDamage.dam_Elec; + if(sAmmoDamage.dam_Son > sMainWeaponDamage.dam_Son) sMainWeaponDamage.dam_Son = sAmmoDamage.dam_Son; + + if(sAmmoDamage.dam_Div > sMainWeaponDamage.dam_Div) sMainWeaponDamage.dam_Div = sAmmoDamage.dam_Div; + if(sAmmoDamage.dam_Neg > sMainWeaponDamage.dam_Neg) sMainWeaponDamage.dam_Neg = sAmmoDamage.dam_Neg; + if(sAmmoDamage.dam_Pos > sMainWeaponDamage.dam_Pos) sMainWeaponDamage.dam_Pos = sAmmoDamage.dam_Pos; + + if(sAmmoDamage.dam_Mag > sMainWeaponDamage.dam_Mag) sMainWeaponDamage.dam_Mag = sAmmoDamage.dam_Mag; + + if(sAmmoDamage.dam_Blud > sMainWeaponDamage.dam_Blud) sMainWeaponDamage.dam_Blud = sAmmoDamage.dam_Blud; + if(sAmmoDamage.dam_Pier > sMainWeaponDamage.dam_Pier) sMainWeaponDamage.dam_Pier = sAmmoDamage.dam_Pier; + if(sAmmoDamage.dam_Slash > sMainWeaponDamage.dam_Slash) sMainWeaponDamage.dam_Slash = sAmmoDamage.dam_Slash; + + if(sAmmoDamage.dice_Acid > sMainWeaponDamage.dice_Acid) sMainWeaponDamage.dice_Acid = sAmmoDamage.dice_Acid; + if(sAmmoDamage.dice_Cold > sMainWeaponDamage.dice_Cold) sMainWeaponDamage.dice_Cold = sAmmoDamage.dice_Cold; + if(sAmmoDamage.dice_Fire > sMainWeaponDamage.dice_Fire) sMainWeaponDamage.dice_Fire = sAmmoDamage.dice_Fire; + if(sAmmoDamage.dice_Elec > sMainWeaponDamage.dice_Elec) sMainWeaponDamage.dice_Elec = sAmmoDamage.dice_Elec; + if(sAmmoDamage.dice_Son > sMainWeaponDamage.dice_Son) sMainWeaponDamage.dice_Son = sAmmoDamage.dice_Son; + + if(sAmmoDamage.dice_Div > sMainWeaponDamage.dice_Div) sMainWeaponDamage.dice_Div = sAmmoDamage.dice_Div; + if(sAmmoDamage.dice_Neg > sMainWeaponDamage.dice_Neg) sMainWeaponDamage.dice_Neg = sAmmoDamage.dice_Neg; + if(sAmmoDamage.dice_Pos > sMainWeaponDamage.dice_Pos) sMainWeaponDamage.dice_Pos = sAmmoDamage.dice_Pos; + + if(sAmmoDamage.dice_Mag > sMainWeaponDamage.dice_Mag) sMainWeaponDamage.dice_Mag = sAmmoDamage.dice_Mag; + + if(sAmmoDamage.dice_Blud > sMainWeaponDamage.dice_Blud) sMainWeaponDamage.dice_Blud = sAmmoDamage.dice_Blud; + if(sAmmoDamage.dice_Pier > sMainWeaponDamage.dice_Pier) sMainWeaponDamage.dice_Pier = sAmmoDamage.dice_Pier; + if(sAmmoDamage.dice_Slash > sMainWeaponDamage.dice_Slash) sMainWeaponDamage.dice_Slash = sAmmoDamage.dice_Slash; + } + + sAttackVars.iMainAttackBonus -= sBonusAttacks.iPenalty; + sAttackVars.iOffHandAttackBonus -= sBonusAttacks.iPenalty; + + // determines the delay between effect application + // to make the system run like the normal combat system. + if(bInstantAttack)// If the full attack is to happen at once + sAttackVars.fDelay = 0.075; // Have some delay in order to avoid being a total resource hog + else + sAttackVars.fDelay = (5.5 / (iMainHandAttacks + sBonusAttacks.iNumber + iOffHandAttacks)); + + // sets iMods to iAttackBonusMod + // used in AttackLoopLogic to decrement attack bonus for attacks. + int iMod = 0; + iMod += iAttackBonusMod; + + // motu99: where do we set the global bFirstAttack and the other global variables ? Shouldn't we set them here? + // Or are they initialized whenever the main() function that calls PerformAttack() or PerformAttackRound() is entered? + // (In this case they would be like local ints attached to the PC or the module, whoever is the caller if the main() function) + AttackLoopMain(oDefender, oAttacker, sBonusAttacks.iNumber, iMainHandAttacks, iOffHandAttacks, iMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage); +} + +// changed Default of nHandednessOverride = 0 (was -1), which means that on default we *now* do a *mainhand* attack; +// only with nHandednessOverride = TRUE (explicitly set) we *now* do an offhand attack +// in the old version, defaulting this variable to -1 was highly confusing (and caused more incorrect calls to PerformAttack than correct ones) +// therefore it seemed justified to change the calling logic of this function +// @TODO: check if all calls to PerformAttack are correct with respect to mainhand/offhand attacks +void PerformAttack(object oDefender, object oAttacker, + effect eSpecialEffect, float eDuration = 0.0, int iAttackBonusMod = 0, + int iDamageModifier = 0, int iDamageType = 0, + string sMessageSuccess = "", string sMessageFailure = "", + int iTouchAttackType = FALSE, + object oRightHandOverride = OBJECT_INVALID, object oLeftHandOverride = OBJECT_INVALID, + int nHandednessOverride = 0, int bCombatModeFlags = 0) +{ +// if (DEBUG) DoDebug("Entered PerformAttack"); + + // create struct for attack loop logic + struct AttackLoopVars sAttackVars; + + // store the combat mode flags + sAttackVars.bMode = bCombatModeFlags; + + // set variables required in attack loop logic + // first for right hand + if (oRightHandOverride == OBJECT_INVALID) + sAttackVars.oWeaponR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oAttacker); + else + sAttackVars.oWeaponR = oRightHandOverride; + + // now for left hand + if (oLeftHandOverride == OBJECT_INVALID) + sAttackVars.oWeaponL = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oAttacker); + else + sAttackVars.oWeaponL = oLeftHandOverride; + + // weapon base item type of right hand weapon + int iMainhandWeaponType = GetBaseItemType(sAttackVars.oWeaponR); + sAttackVars.bIsRangedWeapon = GetIsRangedWeaponType(iMainhandWeaponType); + sAttackVars.iDamageModifier = iDamageModifier; + sAttackVars.iDamageType = iDamageType; + + sAttackVars.eSpecialEffect = eSpecialEffect; + //post prc-effectness + //sAttackVars.sEffectLocalName = "CombatStructEffect_"+ObjectToString(oDefender)+"_"+ObjectToString(oAttacker); + //SetLocalPRCEffect(GetModule(), sAttackVars.sEffectLocalName, eSpecialEffect); + //this says e but is really a float + sAttackVars.eDuration = eDuration; + sAttackVars.bEffectAllAttacks = FALSE; // not really necessary, because default value of int in struct is false + sAttackVars.bApplyTouchToAll = FALSE; // not really necessary, because default value of int in struct is false + sAttackVars.iTouchAttackType = iTouchAttackType; + sAttackVars.sMessageSuccess = sMessageSuccess; + sAttackVars.sMessageFailure = sMessageFailure; + + // are they using a two handed weapon? + int bIsTwoHandedMeleeWeapon = GetIsTwoHandedMeleeWeaponType(iMainhandWeaponType); + + // are they unarmed? + int bIsUnarmed = FALSE; + if(iMainhandWeaponType == BASE_ITEM_INVALID) + { + bIsUnarmed = TRUE; + // if player is unarmed use gloves as weapon + sAttackVars.oWeaponR = GetItemInSlot(INVENTORY_SLOT_ARMS, oAttacker); + } + + int bIsUsingTwoWeapons = FALSE; + int iOffhandWeaponType = GetBaseItemType(sAttackVars.oWeaponL); + + // is the player is using two weapons or double sided weapons? + if(GetIsOffhandWeaponType(iOffhandWeaponType)) // motu99: this could also be a (secondary) creature weapon + bIsUsingTwoWeapons = TRUE; + else if(GetIsDoubleSidedWeaponType(iMainhandWeaponType)) // motu99: included double sided weapons + { + bIsUsingTwoWeapons = TRUE; + iOffhandWeaponType = iMainhandWeaponType; + sAttackVars.oWeaponL = sAttackVars.oWeaponR; + } + + int iMainHandAttacks = 0; + int iOffHandAttacks = 0; + + // number of attacks with main hand + if(nHandednessOverride) + { + // sanity checks + if (!bIsUsingTwoWeapons) + { + DoDebug("PerformAttack: offhand attack, but no offhand weapon wielded - aborting"); + return; + } + iOffHandAttacks = 1; + } + else + iMainHandAttacks = 1; + + // determine extra bonus damage from spells (on the attacker) + //Now checks the defender for racial type and alignment bonus damage -ebonfowl + struct BonusDamage sSpellBonusDamage = GetMagicalBonusDamage(oAttacker, oDefender); + + // structs for damage on main and offhand weapons + struct BonusDamage sMainWeaponDamage; + struct BonusDamage sOffHandWeaponDamage; + + // find out the number of bonus attacks from haste and spell like abilities and the penalties associated with them + // we only need the penalties here! + struct BonusAttacks sBonusAttacks = GetBonusAttacks(oAttacker); + +// DoDebug("PerformAttack() found bonus attacks = " + IntToString(sBonusAttacks.iNumber) + " with penalty " + IntToString(sBonusAttacks.iPenalty)); + + // find out last attack mode to check whether it gives us bonus attacks (and penalties) + int iLastAttackMode = GetLastAttackMode(oAttacker); + if( iLastAttackMode == COMBAT_MODE_FLURRY_OF_BLOWS || iLastAttackMode == COMBAT_MODE_RAPID_SHOT ) + { + sBonusAttacks.iNumber ++; + sBonusAttacks.iPenalty += 2; + } + + + // determine main hand attack bonus and damage that remains constant througout a round + if (iMainHandAttacks) + { + // motu99: all the following three variables do *not* remain constant during the round, because they depend on the defender, and the defender can change + + // determine attack bonus + sAttackVars.iMainAttackBonus = GetAttackBonus(oDefender, oAttacker, sAttackVars.oWeaponR, 0); + + // Determine physical damage per round (cached for multiple use) + sAttackVars.iMainWeaponDamageRound = GetWeaponDamagePerRound(oDefender, oAttacker, sAttackVars.oWeaponR, 0); + + // variables that store extra damage dealt + sMainWeaponDamage = GetWeaponBonusDamage(sAttackVars.oWeaponR, oDefender); + + if (!bIsUnarmed) + { + // we are using a weapon: get weapon information + + // through the overrides we could have been passed a creature weapon as main hand weapon + // (creature weapons usually are equipped in the "special" CWEAPON_R/L/B inventory slots) + if (GetIsCreatureWeaponType(iMainhandWeaponType)) + { + struct Dice sDice; + sDice = GetWeaponMonsterDamage(sAttackVars.oWeaponR); + sAttackVars.iMainNumSides = sDice.iSides; + sAttackVars.iMainNumDice = sDice.iNum; + } + else + { // we are wielding a "normal" weapon + sAttackVars.iMainNumSides = StringToInt(Get2DACache("baseitems", "DieToRoll", iMainhandWeaponType)); + sAttackVars.iMainNumDice = StringToInt(Get2DACache("baseitems", "NumDice", iMainhandWeaponType)); + } + } + // we are unarmed, now check if we are a monk or have a creature weapon from a PrC class. - Brawler, Shou, IoDM, etc. + else if(GetIsUnarmedFighter(oAttacker)) + { + int iDamage = FindUnarmedDamage(oAttacker); + sAttackVars.iMainNumSides = StringToInt(Get2DACache("iprp_monstcost", "Die", iDamage)); + sAttackVars.iMainNumDice = StringToInt(Get2DACache("iprp_monstcost", "NumDice", iDamage)); + } + // we are unarmed and not a monk or a PrC class with a creature weapon + // so we use normal fists + else + { + sAttackVars.iMainNumSides = 3; + sAttackVars.iMainNumDice = 1; + } + sAttackVars.iMainCritMult = GetWeaponCritcalMultiplier(oAttacker, sAttackVars.oWeaponR); + + } + + // only run if off hand attack + if(iOffHandAttacks) + { + sAttackVars.iOffHandAttackBonus = GetAttackBonus(oDefender, oAttacker, sAttackVars.oWeaponL, 1); + sOffHandWeaponDamage = GetWeaponBonusDamage(sAttackVars.oWeaponL, oDefender); + sAttackVars.iOffHandWeaponDamageRound = GetWeaponDamagePerRound(oDefender, oAttacker, sAttackVars.oWeaponL, 1); + + // we are using a weapon: get weapon information + if (GetIsCreatureWeaponType(iOffhandWeaponType)) + { + // if we were passed a creature weapon, we need to do a special calculation + struct Dice sDice; + sDice = GetWeaponMonsterDamage(sAttackVars.oWeaponL); + sAttackVars.iOffHandNumSides = sDice.iSides; + sAttackVars.iOffHandNumDice = sDice.iNum; + } + else + { + // we are wielding a "normal" offhand weapon + sAttackVars.iOffHandNumSides = StringToInt(Get2DACache("baseitems", "DieToRoll", iOffhandWeaponType)); + sAttackVars.iOffHandNumDice = StringToInt(Get2DACache("baseitems", "NumDice", iOffhandWeaponType)); + } + + sAttackVars.iOffHandCritMult = GetWeaponCritcalMultiplier(oAttacker, sAttackVars.oWeaponL); +// DoDebug("PerformAttack() found offhand weapon damage " + IntToString(sAttackVars.iOffHandNumDice) + "d" + IntToString(sAttackVars.iOffHandNumSides) + " with crit mult: " + IntToString(sAttackVars.iOffHandCritMult)); + } + + // Code to equip new ammo + // Equips new ammo if they don't have enough ammo for the whole attack round + // or if they have no ammo equipped. + if(!sAttackVars.bIsRangedWeapon) + { + sAttackVars.oAmmo = OBJECT_INVALID; + } + else // we have a ranged weapon: this can only be a main hand attack + { + if (iOffHandAttacks) + { + DoDebug("PerformAttack: offhand attack while using a ranged weapon - aborting"); + return; + } + sAttackVars.oAmmo = GetAmmunitionFromWeapon(sAttackVars.oWeaponR, oAttacker); + + // if there is no ammunition search inventory for ammo + if( sAttackVars.oAmmo == OBJECT_INVALID + || GetItemStackSize(sAttackVars.oAmmo) <= 2) + { + sAttackVars.oAmmo = EquipAmmunition(oAttacker); + } + + // note that this does not include any on hit properties of the ammunition + // we take care of these in AttackLoopLogic + struct BonusDamage sAmmoDamage = GetWeaponBonusDamage(sAttackVars.oAmmo, oDefender); + + // if these values are better than the weapon, then use these. + if(sAmmoDamage.dam_Acid > sMainWeaponDamage.dam_Acid) sMainWeaponDamage.dam_Acid = sAmmoDamage.dam_Acid; + if(sAmmoDamage.dam_Cold > sMainWeaponDamage.dam_Cold) sMainWeaponDamage.dam_Cold = sAmmoDamage.dam_Cold; + if(sAmmoDamage.dam_Fire > sMainWeaponDamage.dam_Fire) sMainWeaponDamage.dam_Fire = sAmmoDamage.dam_Fire; + if(sAmmoDamage.dam_Elec > sMainWeaponDamage.dam_Elec) sMainWeaponDamage.dam_Elec = sAmmoDamage.dam_Elec; + if(sAmmoDamage.dam_Son > sMainWeaponDamage.dam_Son) sMainWeaponDamage.dam_Son = sAmmoDamage.dam_Son; + + if(sAmmoDamage.dam_Div > sMainWeaponDamage.dam_Div) sMainWeaponDamage.dam_Div = sAmmoDamage.dam_Div; + if(sAmmoDamage.dam_Neg > sMainWeaponDamage.dam_Neg) sMainWeaponDamage.dam_Neg = sAmmoDamage.dam_Neg; + if(sAmmoDamage.dam_Pos > sMainWeaponDamage.dam_Pos) sMainWeaponDamage.dam_Pos = sAmmoDamage.dam_Pos; + + if(sAmmoDamage.dam_Mag > sMainWeaponDamage.dam_Mag) sMainWeaponDamage.dam_Mag = sAmmoDamage.dam_Mag; + + if(sAmmoDamage.dam_Blud > sMainWeaponDamage.dam_Blud) sMainWeaponDamage.dam_Blud = sAmmoDamage.dam_Blud; + if(sAmmoDamage.dam_Pier > sMainWeaponDamage.dam_Pier) sMainWeaponDamage.dam_Pier = sAmmoDamage.dam_Pier; + if(sAmmoDamage.dam_Slash > sMainWeaponDamage.dam_Slash) sMainWeaponDamage.dam_Slash = sAmmoDamage.dam_Slash; + + if(sAmmoDamage.dice_Acid > sMainWeaponDamage.dice_Acid) sMainWeaponDamage.dice_Acid = sAmmoDamage.dice_Acid; + if(sAmmoDamage.dice_Cold > sMainWeaponDamage.dice_Cold) sMainWeaponDamage.dice_Cold = sAmmoDamage.dice_Cold; + if(sAmmoDamage.dice_Fire > sMainWeaponDamage.dice_Fire) sMainWeaponDamage.dice_Fire = sAmmoDamage.dice_Fire; + if(sAmmoDamage.dice_Elec > sMainWeaponDamage.dice_Elec) sMainWeaponDamage.dice_Elec = sAmmoDamage.dice_Elec; + if(sAmmoDamage.dice_Son > sMainWeaponDamage.dice_Son) sMainWeaponDamage.dice_Son = sAmmoDamage.dice_Son; + + if(sAmmoDamage.dice_Div > sMainWeaponDamage.dice_Div) sMainWeaponDamage.dice_Div = sAmmoDamage.dice_Div; + if(sAmmoDamage.dice_Neg > sMainWeaponDamage.dice_Neg) sMainWeaponDamage.dice_Neg = sAmmoDamage.dice_Neg; + if(sAmmoDamage.dice_Pos > sMainWeaponDamage.dice_Pos) sMainWeaponDamage.dice_Pos = sAmmoDamage.dice_Pos; + + if(sAmmoDamage.dice_Mag > sMainWeaponDamage.dice_Mag) sMainWeaponDamage.dice_Mag = sAmmoDamage.dice_Mag; + + if(sAmmoDamage.dice_Blud > sMainWeaponDamage.dice_Blud) sMainWeaponDamage.dice_Blud = sAmmoDamage.dice_Blud; + if(sAmmoDamage.dice_Pier > sMainWeaponDamage.dice_Pier) sMainWeaponDamage.dice_Pier = sAmmoDamage.dice_Pier; + if(sAmmoDamage.dice_Slash > sMainWeaponDamage.dice_Slash) sMainWeaponDamage.dice_Slash = sAmmoDamage.dice_Slash; + } + + // determines the delay between effect application + // to make the system run like the normal combat system. + sAttackVars.fDelay = 0.1; + sAttackVars.iMainAttackBonus -= sBonusAttacks.iPenalty; + sAttackVars.iOffHandAttackBonus -= sBonusAttacks.iPenalty; + + if(nHandednessOverride) + AttackLoopMain(oDefender, oAttacker, 0, 0, 1, iAttackBonusMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage); + else + AttackLoopMain(oDefender, oAttacker, 0, 1, 0, iAttackBonusMod, sAttackVars, sMainWeaponDamage, sOffHandWeaponDamage, sSpellBonusDamage); +} + +//:: void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_combmove.nss b/src/include/prc_inc_combmove.nss new file mode 100644 index 0000000..b81aab4 --- /dev/null +++ b/src/include/prc_inc_combmove.nss @@ -0,0 +1,2338 @@ +//:://///////////////////////////////////////////// +//:: Combat Maneuver include: +//:: prc_inc_combmove +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the combat maneuvers + + Things: + Grapple + Trip + Bullrush + Charge + Overrun + Disarm + + @author Stratovarius + @date Created - 2008.4.20 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int GRAPPLE_ATTACK = 1; +const int GRAPPLE_OPPONENT_WEAPON = 2; +const int GRAPPLE_ESCAPE = 3; +const int GRAPPLE_TOB_CRUSHING = 4; +const int GRAPPLE_DAMAGE = 5; +const int GRAPPLE_PIN = 6; + +const int DEBUG_COMBAT_MOVE = FALSE; + +const int COMBAT_MOVE_GRAPPLE = 1; +const int COMBAT_MOVE_BULLRUSH = 2; +const int COMBAT_MOVE_OVERRUN = 3; +const int COMBAT_MOVE_TRIP = 4; +const int COMBAT_MOVE_DISARM = 5; +const int COMBAT_MOVE_SHIELD_BASH = 6; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the total bonus to checks for chosen combat move + * + * @param oPC The PC + * @param CombatMove The combat move to check + * @param nDefender Do the benefits apply when defending only? + * @param nAttacker Do the benefits apply when attacking only? + * @return Total bonus + */ +int GetCombatMoveCheckBonus(object oPC, int nCombatMove, int nDefender = FALSE, int nAttacker = FALSE); + +/** + * Marks a PC is charging for a round + * + * @param oPC The PC + */ +void SetIsCharging(object oPC); + +/** + * Get whether a PC is charging for a round + * + * @param oPC The PC + * @return TRUE or FALSE + */ +int GetIsCharging(object oPC); + +/** + * This will do a complete PnP charge attack + * Only call EITHER Attack OR Bull Rush + * + * @param oPC The PC + * @param oTarget The Target + * @param nDoAttack Do an attack at the end of a charge or not + * @param nGenerateAoO Does the movement generate an AoO + * @param nDamage A damage bonus on the charge + * @param nDamageType Damage type of the bonus. + * @param nBullRush Do a Bull Rush at the end of a charge + * @param nExtraBonus An extra bonus to grant the PC on the Bull rush + * @param nBullAoO Does the bull rush attempt generate an AoO + * @param nMustFollow Does the Bull rush require the pushing PC to follow the target + * @param nAttack Bonus to the attack roll // I forgot to add it before, I'm an idiot ok? + * @param nPounce FALSE for normal behaviour, TRUE to do a full attack at the end of a charge // Same comment as above + * + * @return TRUE if the attack or Bull rush hits, else FALSE + */ +void DoCharge(object oPC, object oTarget, int nDoAttack = TRUE, int nGenerateAoO = TRUE, int nDamage = 0, int nDamageType = -1, int nBullRush = FALSE, int nExtraBonus = 0, int nBullAoO = TRUE, int nMustFollow = TRUE, int nAttack = 0, int nPounce = FALSE); + +/** + * This will do a complete PnP Bull rush + * + * @param oPC The PC + * @param oTarget The Target + * @param nExtraBonus An extra bonus to grant the PC + * @param nGenerateAoO Does the Bull rush attempt generate an AoO + * @param nMustFollow Does the Bull rush require the pushing PC to follow the target + * @param nNoMove PC does not need to move to the target, used for spells + * @param nAbility Override the PC's Strength score with something else + * + * @return TRUE if the Bull rush succeeds, else FALSE + */ +void DoBullRush(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, int nMustFollow = TRUE, int nNoMove = FALSE, int nAbility = 0); + +/** + * This will do a complete PnP Trip + * + * @param oPC The PC + * @param oTarget The Target + * @param nExtraBonus An extra bonus to grant the PC + * @param nGenerateAoO Does the Trip attempt generate an AoO + * @param nCounterTrip Can the target attempt a counter trip if you fail + * @param nSkipTouch Skip the melee touch attack or not + * @param nAbi This overrides the PC's ability modifier with the input value + * + * @return TRUE if the Trip succeeds, else FALSE + * It sets a local int known as TripDifference that is the amount you succeeded or failed by. + */ +int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, int nCounterTrip = TRUE, int nSkipTouch = FALSE, int nAbi = 0); + +/** + * Will take an int and transform it into one of the DAMAGE_BONUS constants (From 1 to 50). + * + * @param nCheck Int to convert + * @return DAMAGE_BONUS_1 to DAMAGE_BONUS_50 + */ +int GetIntToDamage(int nCheck); + +/** + * This will do a complete PnP Grapple + * + * @param oPC The PC + * @param oTarget The Target + * @param nExtraBonus An extra bonus to grant the PC + * @param nGenerateAoO Does the Grapple attempt generate an AoO + * @param nSkipTouch Skip the melee touch attack or not + * + * @return TRUE if the Grapple succeeds, else FALSE + */ +int DoGrapple(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, int nSkipTouch = FALSE); + +/** + * Marks a target as grappled. + * + * @param oTarget The Target + */ +void SetGrapple(object oTarget); + +/** + * Returns true or false if the creature is grappled. + * + * @param oTarget Person to check + * + * @return TRUE or FALSE + */ +int GetGrapple(object oTarget); + +/** + * Saves the grapple target for the PC + * + * @param oPC The PC + * @param oTarget The Target + */ +void SetGrappleTarget(object oPC, object oTarget); + +/** + * Returns the grapple target for the PC + * + * @param oPC The PC + */ +object GetGrappleTarget(object oPC); + +/** + * Marks a target as pinned. + * + * @param oTarget The Target + */ +void SetIsPinned(object oTarget); + +/** + * Returns true or false if the creature is pinned. + * + * @param oTarget Person to check + * + * @return TRUE or FALSE + */ +int GetIsPinned(object oTarget); + +/** + * Removes the Pinned condition + * + * @param oTarget The Target + */ +void BreakPin(object oTarget); + +/** + * Ends a grapple between the two creatures + * + * @param oPC The PC + * @param oTarget The Target + */ +void EndGrapple(object oPC, object oTarget); + +/** + * The options that can be performed during a grapple. Returns success or fail of the grapple check. + * + * @param oPC The PC + * @param oTarget The Target + * @param nExtraBonus An extra bonus to grant the PC + * @param nSwitch The options to use. One of: GRAPPLE_ATTACK, GRAPPLE_OPPONENT_WEAPON, GRAPPLE_ESCAPE, GRAPPLE_TOB_CRUSHING + * + * To-Do - Add Grapple to Move, add Pin Target, add Pin options + */ +int DoGrappleOptions(object oPC, object oTarget, int nExtraBonus, int nSwitch = -1); + +/** + * Returns true or false if the creature's right hand weapon is light + * + * @param oPC Person to check + * + * @return TRUE or FALSE + */ +int GetIsLightWeapon(object oPC); + +/** + * This will do a complete PnP Overrun. See tob_stdr_bldrrll for an example of how to use. + * Overrun is part of a move action. + * + * @param oPC The PC + * @param oTarget The Target + * @param nExtraBonus An extra bonus to grant the PC + * @param nGenerateAoO Does the Overrun attempt generate an AoO + * @param nAvoid Can the target avoid you + * @param nCounterTrip Can the target attempt a counter if you fail + * + * @return TRUE if the Overrun succeeds, else FALSE + * It sets a local int known as OverrunDifference that is the amount you succeeded or failed by. + */ +void DoOverrun(object oPC, object oTarget, location lTarget, int nGenerateAoO = TRUE, int nExtraBonus = 0, int nAvoid = TRUE, int nCounter = TRUE); + +/** + * This will do a complete PnP Disarm + * + * @param oPC The PC + * @param oTarget The Target + * @param nExtraBonus An extra bonus to grant the PC + * @param nGenerateAoO Does the attempt generate an AoO + * @param nCounter Can the target attempt a counter disarm if you fail + * + * @return TRUE if the Disarm succeeds, else FALSE +*/ +int DoDisarm(object oPC, object oTarget, int nExtraBonus = 0, int nGenerateAoO = TRUE, int nCounter = TRUE); + +// * returns the size modifier for grappling +int PRCGetSizeModifier(object oCreature); + +/** + * Does the knockback for the Tiger Blooded Tome of Battle feat. + * Is here because it uses Bull Rush code. + * + * @param oInitiator Hitter + * @param oTarget Guess what + */ +void TigerBlooded(object oInitiator, object oTarget); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_combat" +#include "prc_inc_sp_tch" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _DoBullRushKnockBack(object oTarget, object oPC, float fFeet) +{ + // Calculate how far the creature gets pushed + float fDistance = FeetToMeters(fFeet); + // Determine if they hit a wall on the way + location lPC = GetLocation(oPC); + location lTargetOrigin = GetLocation(oTarget); + vector vAngle = AngleToVector(GetRelativeAngleBetweenLocations(lPC, lTargetOrigin)); + vector vTargetOrigin = GetPosition(oTarget); + vector vTarget = vTargetOrigin + (vAngle * fDistance); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("Initialized _DoBullRushKnockBack", oPC, FALSE); + if(!LineOfSightVector(vTargetOrigin, vTarget)) + { + // Hit a wall, binary search for the wall + float fEpsilon = 1.0f; // Search precision + float fLowerBound = 0.0f; // The lower search bound, initialise to 0 + float fUpperBound = fDistance; // The upper search bound, initialise to the initial distance + fDistance = fDistance / 2; // The search position, set to middle of the range + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoBullRushKnockBack: If Statement", oPC, FALSE); + + do{ + // Create test vector for this iteration + vTarget = vTargetOrigin + (vAngle * fDistance); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoBullRushKnockBack: DO Loop", oPC, FALSE); + // Determine which bound to move. + if(LineOfSightVector(vTargetOrigin, vTarget)) + fLowerBound = fDistance; + else + fUpperBound = fDistance; + + // Get the new middle point + fDistance = (fUpperBound + fLowerBound) / 2; + }while(fabs(fUpperBound - fLowerBound) > fEpsilon); + } + + // Create the final target vector + vTarget = vTargetOrigin + (vAngle * fDistance); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoBullRushKnockBack: Final Vector", oPC, FALSE); + // Move the target + location lTargetDestination = Location(GetArea(oTarget), vTarget, GetFacing(oTarget)); + AssignCommand(oTarget, ClearAllActions(TRUE)); + AssignCommand(oTarget, JumpToLocation(lTargetDestination)); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoBullRushKnockBack: Jumped", oPC, FALSE); +} + +int _DoGrappleCheck(object oPC, object oTarget, int nExtraBonus, int nSwitch = -1) +{ + // The basic modifiers + int nPCBAB = GetBaseAttackBonus(oPC); + int nTargetBAB = GetBaseAttackBonus(oTarget); + int nPCStr = GetAbilityModifier(ABILITY_STRENGTH, oPC); + int nTargetStr = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + int nPCBonus = PRCGetSizeModifier(oPC); + int nTargetBonus = PRCGetSizeModifier(oTarget); + // Other ability bonuses + nPCBonus += GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_GRAPPLE, FALSE, TRUE); + nTargetBonus += GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_GRAPPLE, TRUE); + // Extra bonus + nPCBonus += nExtraBonus; + + if (GetHasFeat(FEAT_IMPROVED_GRAPPLE, oPC)) nPCBonus += 4; + if (GetHasFeat(FEAT_IMPROVED_GRAPPLE, oTarget)) nTargetBonus += 4; + nTargetBonus += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oTarget); //Only applies on defense + if (nSwitch == GRAPPLE_ESCAPE) + nPCBonus += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oPC); // And when escaping + + //cant grapple incorporeal or ethereal things + if((GetIsEthereal(oTarget) && !GetIsEthereal(oPC)) + || GetIsIncorporeal(oTarget)) + { + FloatingTextStringOnCreature("You cannot grapple an Ethereal or Incorporeal creature",oPC, FALSE); + return FALSE; + } + + int nPCCheck = nPCBAB + nPCStr + nPCBonus + d20(); + int nTargetCheck = nTargetBAB + nTargetStr + nTargetBonus + d20(); + // Now roll the ability check + SendMessageToPC(oPC, "PC Grapple Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + if (GetIsPC(oTarget)) + SendMessageToPC(oTarget, "Enemy Grapple Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + if (nPCCheck >= nTargetCheck) + { + return TRUE; + } + + // Didn't grapple successfully + return FALSE; +} + +void _DoCurlingWaveStrike(object oPC, object oTarget) +{ + if (GetLocalInt(oPC, "CurlingWaveStrike")) return; // Escape if this has already happened + location lTarget = GetLocation(oPC); + int nContinue = TRUE; + // Use the function to get the closest creature as a target + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget) && nContinue) + { + // All enemies in range get a free AoO shot + if(oAreaTarget != oPC && // Don't hit yourself + GetIsInMeleeRange(oPC, oAreaTarget) && // They must be in melee range + GetIsEnemy(oAreaTarget, oPC) && // Only enemies are valid targets + oAreaTarget != oTarget) // Can't hit the same guy twice + { + // Once we're here, perform the second trip + DoTrip(oPC, oAreaTarget, 0); + nContinue = FALSE; // Break the loop + } + + //Select the next target within the spell shape. + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + + // Stop this being an eternal loop + SetLocalInt(oPC, "CurlingWaveStrikeLoop", TRUE); + DelayCommand(0.5, DeleteLocalInt(oPC, "CurlingWaveStrikeLoop")); + + // Once a round + SetLocalInt(oPC, "CurlingWaveStrikeRound", TRUE); + DelayCommand(6.0, DeleteLocalInt(oPC, "CurlingWaveStrikeRound")); +} + +// Returns 0 on a fail +// Returns 1 on a successful overrun +// Returns 2 on an avoid +void _DoOverrunCheck(object oPC, object oTarget, location lTarget, int nGenerateAoO = TRUE, int nExtraBonus = 0, int nAvoid = TRUE, int nCounter = TRUE) +{ + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoOverrunCheck: Initialized", oPC, FALSE); + // Stops loops + if (GetLocalInt(oPC, "Overrun") != 2 && !GetLocalInt(oPC, "BoulderRoll")) return; + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoOverrunCheck: Loop Protect", oPC, FALSE); + + if(!nGenerateAoO) + { + // Huge bonus to tumble to prevent AoOs from movement + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSkillIncrease(SKILL_TUMBLE, 50), oPC, 6.0); + } + // The basic modifiers + int nSucceed = FALSE; + int nPCStat, nTargetStat; + // Use the higher of the two mods + if (GetAbilityModifier(ABILITY_STRENGTH, oPC) > GetAbilityModifier(ABILITY_DEXTERITY, oPC)) + nPCStat = GetAbilityModifier(ABILITY_STRENGTH, oPC) + GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_OVERRUN, FALSE, TRUE); + else + nPCStat = GetAbilityModifier(ABILITY_DEXTERITY, oPC) + GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_OVERRUN, FALSE, TRUE); + // Use the higher of the two mods + if (GetAbilityModifier(ABILITY_STRENGTH, oTarget) > GetAbilityModifier(ABILITY_DEXTERITY, oTarget)) + nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_OVERRUN, TRUE); + else + nTargetStat = GetAbilityModifier(ABILITY_DEXTERITY, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_OVERRUN, TRUE); + // Get mods for size + int nPCBonus = PRCGetSizeModifier(oPC); + int nTargetBonus = PRCGetSizeModifier(oTarget); + //Warblade Battle Skill bonus + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oPC) >= 11) + { + nPCBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Overrun bonus (attacker)"); + } + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oTarget) >= 11) + { + nTargetBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oTarget); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Overrun bonus (defender)"); + } + if (GetHasFeat(FEAT_IMPROVED_OVERRUN, oPC)) //Can't avoid an overrun now + { + nPCBonus += 4; + nAvoid = FALSE; + } + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoOverrunCheck: Modifiers Complete", oPC, FALSE); + // Do the AoO for an overrun attempt + if (nGenerateAoO) + { + // Perform the Attack + effect eNone; + DelayCommand(0.0, PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, 0, "Attack of Opportunity Hit", "Attack of Opportunity Miss")); + FloatingTextStringOnCreature(GetName(oTarget)+" Overrun Attack of Opportunity", oPC, FALSE); + } + int nPCCheck = nPCStat + nPCBonus + nExtraBonus + d20(); + int nTargetCheck = nTargetStat + nTargetBonus + d20(); + + // The target has the option to avoid. Smaller targets will avoid if allowed. + if (nPCBonus > nTargetBonus && nAvoid) + { + FloatingTextStringOnCreature(GetName(oTarget) + " has successfully avoided you", oPC, FALSE); + // You didn't knock down the target, but neither did it stop you. Keep on chugging. + SetLocalInt(oPC, "Overrun", 2); + AssignCommand(oPC, ActionForceMoveToLocation(lTarget, TRUE)); + return; + } + // Now roll the ability check + SendMessageToPC(oPC, "Overrun Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + if (nPCCheck >= nTargetCheck) + { + FloatingTextStringOnCreature("You have succeeded on your Overrun attempt",oPC, FALSE); + // Knock em down + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oTarget, 6.0); + nSucceed = TRUE; + SetLocalInt(oPC, "OverrunDifference", nPCCheck - nTargetCheck); + DeleteLocalInt(oPC, "OverrunDifference"); + effect eNone; + if (GetHasFeat(FEAT_CENTAUR_TRAMPLE, oPC)) PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, 0, "Centaur Trample Hit", "Centaur Trample Miss", FALSE, GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC)); + AssignCommand(oPC, ActionForceMoveToLocation(lTarget, TRUE)); + DelayCommand(7.05, AssignCommand(oTarget, ClearAllActions(TRUE))); + DelayCommand(7.05, AssignCommand(oTarget, ActionAttack(oPC))); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoOverrunCheck: oTarget Assign Command "+GetName(oTarget), oPC, FALSE); + } + else // If you fail, enemy gets a counter Overrun attempt, using Strength + { + nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_OVERRUN, FALSE, TRUE); + FloatingTextStringOnCreature("You have failed on your Overrun attempt",oPC, FALSE); + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, JumpToLocation(GetLocation(oTarget))); + // Roll counter Overrun attempt + nTargetCheck = nTargetStat + nTargetBonus + d20(); + nPCCheck = nPCStat + nPCBonus + d20(); + // If counters aren't allowed, don't knock em down + // Its down here to allow the text message to go through + SendMessageToPC(oPC, "Enemy Overrun Counter Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + if (nTargetCheck >= nPCCheck && nCounter) + { + // Knock em down + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oPC, 6.0); + } + SetLocalInt(oPC, "OverrunDifference", nTargetCheck - nPCCheck); + DelayCommand(2.0, DeleteLocalInt(oPC, "OverrunDifference")); + } + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("_DoOverrunCheck: Ending, nSucceed "+IntToString(nSucceed), oPC, FALSE); + SetLocalInt(oPC, "Overrun", nSucceed); +} + +int _ChargeDamage(object oPC) +{ + int nDam; + int nSize = PRCGetSizeModifier(oPC); + + if (GetHasFeat(FEAT_GREATER_POWERFUL_CHARGE, oPC)) nSize += 4; + + if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 0) nDam += d8(); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 4) nDam += d6(2); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 8) nDam += d6(3); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 12) nDam += d6(4); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 16) nDam += d6(6); + + if (GetHasFeat(FEAT_RHINO_TRIBE_CHARGE, oPC)) nDam += d6(2); + // Using a natural attack only + if (GetHasSpellEffect(VESTIGE_AMON, oPC) && GetLocalInt(oPC, "AmonRam") && !GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC))) nDam += d8(); + + nDam += GetEssentiaInvestedFeat(oPC, FEAT_COBALT_CHARGE); + + // Doesn't appear to be added to charging normally, so brute forcing it here + nDam += GetEssentiaInvested(oPC, MELD_INCANDESCENT_STRIKE); + + return nDam; +} + +int _ChargeAttack(object oPC, object oTarget, int nAtk) +{ + nAtk += 2; // Add to whatever the bonus was before + int nAC = 2; + + if(GetHasFeat(FEAT_FURIOUS_CHARGE, oPC)) nAtk += 2; + if(GetHasFeat(FEAT_RECKLESS_CHARGE, oPC)) + { + nAtk += 2; + nAC += 2; + } + if(GetHasFeat(FEAT_GREAT_STAG_BERSERKER, oPC)) // Yes, it's the same feat as Reckless Charge + { + nAtk += 2; + nAC += 2; + } + if(GetHasFeat(FEAT_SIDESTEP_CHARGE, oTarget)) nAtk -= 4; + if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC) >= 2) nAtk += 1; + if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC) >= 4) nAtk += 1; + + effect eCharge = EffectLinkEffects(EffectACDecrease(nAC), EffectMovementSpeedIncrease(99)); + eCharge = ExtraordinaryEffect(eCharge); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCharge, oPC, 6.0); + + nAtk += GetEssentiaInvestedFeat(oPC, FEAT_COBALT_CHARGE); + + // Done this way because otherwise the attack bonus isn't used properly + return nAtk; +} + +void _SuperiorBullRush(object oPC, object oTarget) +{ + int nDam = d6(); + if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC) >= 4) nDam = d8(); + nDam += GetAbilityModifier(ABILITY_STRENGTH, oPC); + + if (GetIsCharging(oPC)) + { + int nSize = PRCGetSizeModifier(oPC); + + if (GetHasFeat(FEAT_GREATER_POWERFUL_CHARGE, oPC)) nSize += 4; + + if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 0) nDam += d8(); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 4) nDam += d6(2); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 8) nDam += d6(3); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 12) nDam += d6(4); + else if(GetHasFeat(FEAT_POWERFUL_CHARGE, oPC) && nSize == 16) nDam += d6(6); + } + + effect eDeath = EffectDamage(nDam, DAMAGE_TYPE_PIERCING); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oTarget); + FloatingTextStringOnCreature("Superior Bull Rush Hit", oPC, FALSE); +} + +void _DoBullrushCheck(object oPC, object oTarget, int nExtraBonus, int nMustFollow = TRUE, int nAbility = 0) +{ + // The basic modifiers + int nPCStr = GetAbilityModifier(ABILITY_STRENGTH, oPC); + int nTargetStr = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + if (nAbility > 0) nPCStr = nAbility; // Use the override if it exists + int nPCBonus = PRCGetSizeModifier(oPC); + int nTargetBonus = PRCGetSizeModifier(oTarget); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: Initialized", oPC, FALSE); + //Warblade Battle Skill bonus + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oPC) >= 11) + { + nPCBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Bull Rush bonus (attacker)"); + } + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oTarget) >= 11) + { + nTargetBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oTarget); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Bull Rush bonus (defender)"); + } + if (GetHasFeat(FEAT_IMPROVED_BULLRUSH, oPC)) nPCBonus += 4; + effect eNone; + // Get a +2 bonus for charging during a bullrush + if (GetIsCharging(oPC)) nPCBonus += 2; + // Other ability bonuses + nPCBonus += GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_BULLRUSH, FALSE, TRUE); + nTargetBonus += GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_BULLRUSH, TRUE); + // Extra bonus + nPCBonus += nExtraBonus; + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: End of Bonuses", oPC, FALSE); + + if (GetLocalInt(oPC, "SlingDireWind_BullRush")) //Special roll + { + nPCStr = 15; + nPCBonus = 0; + nMustFollow = FALSE; + } + else if (GetLocalInt(oPC, "RonoveBullRush")) //Special roll + { + nPCStr = GetLocalInt(oPC, "RonoveBullRush"); + nPCBonus = 0; + nMustFollow = FALSE; + } + + // Ability check + int nPCCheck = nPCStr + nPCBonus + d20(); + int nTargetCheck = nTargetStr + nTargetBonus + d20(); + SendMessageToPC(oPC, "Bull Rush Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + + // Now roll the ability check + if (nPCCheck >= nTargetCheck) + { + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: Successful Hit", oPC, FALSE); + // Knock them back 5 feet + float fFeet = 5.0; + // For every 5 points greater, knock back an additional 5 feet. + fFeet += 5.0 * ((nPCCheck - nTargetCheck) / 5); + // This weapon of legacy adds 5 feet to a successful bull rush + if(GetLocalInt(oPC, "Caladbolg_Bullrush")) fFeet += 5.0; + // Max pushback from this one is 5.0 + if (GetLocalInt(oPC, "SlingDireWind_BullRush")) fFeet = 5.0; + SetLocalInt(oPC, "Bullrush", TRUE); + DelayCommand(3.0, DeleteLocalInt(oPC, "Bullrush")); + // Shedu Crown negates the knockback + if (GetHasSpellEffect(18767, oTarget)) fFeet = 0.0; + _DoBullRushKnockBack(oTarget, oPC, fFeet); + if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC)) _SuperiorBullRush(oPC, oTarget); + // If the PC has to keep pushing to knock back, move the PC along + if (nMustFollow) + { + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: Following", oPC, FALSE); + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, JumpToLocation(GetLocation(oTarget))); + } + if(GetHasFeat(FEAT_RAMPAGING_BULL_RUSH, oPC) && GetHasSpellEffect(SPELLABILITY_BARBARIAN_RAGE, oPC)) + DelayCommand(0.1, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, RoundsToSeconds(1))); + } + else + FloatingTextStringOnCreature("You have failed your Bull Rush Attempt",oPC, FALSE); + + // Let people know if we made the hit or not + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: Exit", oPC, FALSE); +} + +int _CountPinRounds(object oTarget) +{ + int nPin = GetLocalInt(oTarget, "PinnedRounds"); + SetLocalInt(oTarget, "PinnedRounds", nPin+1); + + return nPin+1; +} + +void _DoReapingMauler(object oPC, object oTarget, int nRounds) +{ + int nClass = GetLevelByClass(CLASS_TYPE_REAPING_MAULER, oPC); + if (3 > nClass) return; + + if (!GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) + { + if (nRounds >= 3 && nClass >= 5) // Devastating Grapple + { + int nDC = 15 + GetAbilityModifier(ABILITY_WISDOM, oPC); + if(!FortitudeSave(oTarget, nDC, SAVING_THROW_TYPE_NONE, oPC)) + { + FloatingTextStringOnCreature("Devastating Grapple Success", oPC, FALSE); + effect eDeath = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oTarget); + } + } + else + { + int nDC = 10 + nClass + GetAbilityModifier(ABILITY_WISDOM, oPC); + if(!FortitudeSave(oTarget, nDC, SAVING_THROW_TYPE_NONE, oPC)) + { + SetLocalInt(oTarget, "UnconsciousGrapple", TRUE); + // Unconscious effects + effect eUncon = EffectLinkEffects(EffectStunned(), EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DISABLED)); + eUncon = EffectLinkEffects(eUncon, EffectKnockdown()); + float fDur = d3() * 6.0; + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eUncon), oTarget, fDur); + FloatingTextStringOnCreature("Sleeper Lock Success", oPC, FALSE); + DelayCommand(fDur, DeleteLocalInt(oTarget, "UnconsciousGrapple")); + } + } + } +} + +void _PostCharge(object oPC, object oTarget) +{ + int nSpellId = PRCGetSpellId(); + if (DEBUG) DoDebug("_PostCharge nSpellId: "+IntToString(nSpellId)); + effect eNone; + int nSucceed = GetLocalInt(oTarget, "PRCCombat_StruckByAttack"); + if(GetHasFeat(FEAT_SIDESTEP_CHARGE, oTarget) && nSucceed == FALSE) + DelayCommand(0.0, PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, -1, "Sidestep Charge Hit", "Sidestep Charge Miss")); + + //Gorebrute Elite Knockdown + if(GetLocalInt(oPC, "ShifterGore") && GetHasFeat(FEAT_GOREBRUTE_ELITE, oPC) && nSucceed) + { + //Knockdown check + int nEnemyStr = d20() + GetAbilityModifier(ABILITY_STRENGTH, oTarget); + int nYourStr = d20() + GetAbilityModifier(ABILITY_STRENGTH, oPC); + SendMessageToPC(oPC, "Opposed Knockdown Check: Rolled " + IntToString(nYourStr) + " vs " + IntToString(nEnemyStr)); + SendMessageToPC(oTarget, "Opposed Knockdown Check: Rolled " + IntToString(nEnemyStr) + " vs " + IntToString(nYourStr)); + if(nYourStr > nEnemyStr) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 6.0); + } + + if (nSpellId == MOVE_DS_DOOM_CHARGE && nSucceed) + { + effect eLink = EffectVisualEffect(VFX_DUR_ROOTED_TO_SPOT); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_BLUDGEONING, 10)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_PIERCING, 10)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_SLASHING, 10)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oPC, 6.0); + } + else if (nSpellId == MOVE_DS_LAW_BEARER && nSucceed) + { + effect eLink = EffectVisualEffect(VFX_DUR_ROOTED_TO_SPOT); + eLink = EffectLinkEffects(eLink, EffectACIncrease(5)); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_ALL, 5)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oPC, 6.0); + } + else if (nSpellId == MOVE_DS_RADIANT_CHARGE && nSucceed) + { + effect eLink = EffectVisualEffect(VFX_DUR_ROOTED_TO_SPOT); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_BLUDGEONING, 10)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_PIERCING, 10)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_SLASHING, 10)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oPC, 6.0); + } + else if (nSpellId == MOVE_DS_TIDE_CHAOS && nSucceed) + { + effect eLink = EffectLinkEffects(EffectVisualEffect(VFX_DUR_BLUR), EffectConcealment(50)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oPC, 6.0); + } + else if (nSpellId == MOVE_WR_WAR_MASTERS_CHARGE && nSucceed) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectStunned()), oTarget, 6.0); + } + // Applies to all charges, Thunderstep Boots meld + if (GetHasSpellEffect(MELD_THUNDERSTEP_BOOTS, oPC) && nSucceed) + { + int nDice = GetEssentiaInvested(oPC, MELD_STRONGHEART_VEST) + 1; + ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDamage(d4(nDice), DAMAGE_TYPE_SONIC)), oTarget); + if (GetIsMeldBound(oTarget, MELD_THUNDERSTEP_BOOTS) == CHAKRA_FEET) + { + int nDC = GetMeldshaperDC(oPC, CLASS_TYPE_SOULBORN, MELD_STRONGHEART_VEST); + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectStunned(), oTarget, 6.0); + } + } + // Applies to all charges, Urskan Greaves meld + if (GetHasSpellEffect(MELD_URSKAN_GREAVES, oPC) && nSucceed) + { + int nDice = GetEssentiaInvested(oPC, MELD_URSKAN_GREAVES); + if (GetIsMeldBound(oTarget, MELD_URSKAN_GREAVES) == CHAKRA_FEET) + ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDamage(d4(nDice), DAMAGE_TYPE_BLUDGEONING)), oTarget); + } +} + +void _DoTrampleDamage(object oPC, object oTarget) +{ + int nPCSize = PRCGetSizeModifier(oPC); + int nTargetSize = PRCGetSizeModifier(oTarget); + + // Have to be equal to your size or smaller + if (nPCSize >= nTargetSize) + { + int nDamage = d8(); + if (nPCSize == 4) nDamage = d6(2); // Large + else if (nPCSize == -4) nDamage = d6(); // Small + int nDC = GetMeldshaperDC(oPC, CLASS_TYPE_SOULBORN, PRCGetSpellId()); + + nDamage += FloatToInt(GetAbilityModifier(ABILITY_STRENGTH, oPC) * 1.5); + nDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nDC, SAVING_THROW_TYPE_NONE); + if (nDamage > 0) + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage, DAMAGE_TYPE_BLUDGEONING), oTarget); + } +} + +void _HeartOfFireGrapple(object oPC, object oTarget) +{ + if (GetIsMeldBound(oTarget, MELD_HEART_OF_FIRE) == CHAKRA_WAIST) + { + int nEssentia = GetEssentiaInvested(oPC, MELD_HEART_OF_FIRE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(nEssentia), DAMAGE_TYPE_FIRE), oTarget); + } +} + +int _TotemAvatar(object oPC, int nCombatMove) +{ + int nReturn; + + if (GetIsMeldBound(oPC, MELD_HEART_OF_FIRE) == CHAKRA_FEET && nCombatMove != COMBAT_MOVE_GRAPPLE) + nReturn = 4; + + if (nCombatMove == COMBAT_MOVE_GRAPPLE || + nCombatMove == COMBAT_MOVE_BULLRUSH || + nCombatMove == COMBAT_MOVE_OVERRUN || + nCombatMove == COMBAT_MOVE_TRIP) + nReturn += 4; + + return nReturn; +} + +int _UrskanGreaves(object oPC) +{ + int nReturn; + + if (GetIsMeldBound(oPC, MELD_URSKAN_GREAVES) == CHAKRA_TOTEM) + nReturn = 2 + GetEssentiaInvested(oPC, MELD_URSKAN_GREAVES); + + return nReturn; +} + +void _ShieldBashDamage(object oPC, object oTarget, int nRoll, int nHand, int nDamage, int nDamageType, int nType) +{ + int nDam = d3(); + if (nType == BASE_ITEM_LARGESHIELD) nDam = d4(); + int nStr = GetAbilityModifier(ABILITY_STRENGTH, oPC)/2; // Default to offhand + if (nHand) nStr = GetAbilityModifier(ABILITY_STRENGTH, oPC); // Onhand attack + int nBash = GetLocalInt(oPC, "BashingEnchant"); + if (nBash && nType == BASE_ITEM_LARGESHIELD) nDam = d8(); + else if (nBash && nType == BASE_ITEM_SMALLSHIELD) nDam = d6(); + + nDam += nStr; + nDam += nBash; + // Critical hit + if (nRoll == 2) + { + if (nType == BASE_ITEM_LARGESHIELD) nDam += d4(); + else nDam += d3(); + nDam += nStr; + nDam += nBash; + nDamage *= 2; + } + effect eLink = EffectDamage(nDam, DAMAGE_TYPE_BLUDGEONING); + if (nDamage) eLink = EffectLinkEffects(eLink, EffectDamage(nDamage, nDamageType)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, ExtraordinaryEffect(eLink), oTarget); + + //Extra damage from Energized Shield and Lesser Energized Shield + int nType = GetLocalInt(oPC, "EnShieldType"); + int nD6 = GetLocalInt(oPC, "EnShieldD6"); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nType, d6(nD6)), oTarget); +} + +void _BariaurChargeDamage(object oPC, object oTarget, int nRoll, int nDamBonus) +{ + int nDam = d6(2) + GetAbilityModifier(ABILITY_STRENGTH, oPC) + nDamBonus; // Onhand attack + if (nRoll == 2) nDam += d6(2) + GetAbilityModifier(ABILITY_STRENGTH, oPC) + nDamBonus; + + ApplyEffectToObject(DURATION_TYPE_INSTANT, ExtraordinaryEffect(EffectDamage(nDam, DAMAGE_TYPE_BLUDGEONING)), oTarget); +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetCombatMoveCheckBonus(object oPC, int nCombatMove, int nDefender = FALSE, int nAttacker = FALSE) +{ + int nBonus = 0; + if(nCombatMove == COMBAT_MOVE_GRAPPLE) + { + if(GetHasSpellEffect(MOVE_SD_STONEFOOT_STANCE, oPC)) nBonus += 2; + if(GetLevelByClass(CLASS_TYPE_REAPING_MAULER, oPC) >= 2) nBonus += 1; + if(GetLevelByClass(CLASS_TYPE_REAPING_MAULER, oPC) >= 4) nBonus += 1; + if(GetHasFeat(FEAT_LEGENDARY_WRESTLER, oPC)) nBonus += 10; + if(GetRacialType(oPC) == RACIAL_TYPE_CHITINE) nBonus += 4; + if(GetHasSpellEffect(MELD_GIRALLON_ARMS, oPC)) nBonus += (2 + GetEssentiaInvested(oPC, MELD_GIRALLON_ARMS)); // MELD_GIRALLON_ARMS + if(GetLocalInt(oPC, "BullybashersGrapple")) nBonus += 4; + if(GetLocalInt(oPC, "BullybashersGiant")) nBonus += 4; + if(GetHasFeat(FEAT_OPEN_LESSER_CHAKRA_ARMS, oPC)) nBonus += 2; + if(GetHasFeat(FEAT_ABERRANT_DEEPSPAWN, oPC)) nBonus += 2; + if(GetHasFeat(FEAT_ABERRANT_LIMBS, oPC)) nBonus += 2; + if(GetHasSpellEffect(POWER_GRIP_IRON, oPC)) nBonus += GetLocalInt(oPC, "Psi_GripOfIron"); + } + else if(nCombatMove == COMBAT_MOVE_BULLRUSH) + { + if(GetHasSpellEffect(MOVE_SD_STONEFOOT_STANCE, oPC)) nBonus += 2; + if(GetHasSpellEffect(MOVE_SS_STEP_WIND, oPC)) nBonus += 4; + if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC)) nBonus += GetAbilityModifier(ABILITY_STRENGTH, oPC); + if(GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC) >= 3) nBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if(GetLocalInt(oPC, "LuckyDiceAbility")) nBonus += 1; + if(GetHasSpellEffect(MELD_MAULING_GAUNTLETS, oPC)) nBonus += (2 + (2 * GetEssentiaInvested(oPC, MELD_MAULING_GAUNTLETS))); // MELD_MAULING_GAUNTLETS + if(GetHasSpellEffect(MELD_SPHINX_CLAWS, oPC)) nBonus += (1 + GetEssentiaInvested(oPC, MELD_SPHINX_CLAWS)); // MELD_SPHINX_CLAWS + if(GetLocalInt(oPC, "EventideCrux")) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_OVERRUN) + { + if(GetHasSpellEffect(MOVE_SD_STONEFOOT_STANCE, oPC)) nBonus += 2; + if(GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC) >= 3) nBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if(GetLocalInt(oPC, "LuckyDiceAbility")) nBonus += 1; + if(GetHasSpellEffect(MELD_MAULING_GAUNTLETS, oPC)) nBonus += (2 + (2 * GetEssentiaInvested(oPC, MELD_MAULING_GAUNTLETS))); // MELD_MAULING_GAUNTLETS + if(GetHasSpellEffect(MELD_SPHINX_CLAWS, oPC)) nBonus += (1 + GetEssentiaInvested(oPC, MELD_SPHINX_CLAWS)); // MELD_SPHINX_CLAWS + if(GetLocalInt(oPC, "EventideCrux")) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_TRIP) + { + if(GetHasSpellEffect(MOVE_SD_STONEFOOT_STANCE, oPC)) nBonus += 2; + if(GetHasSpellEffect(MOVE_SS_STEP_WIND, oPC)) nBonus += 4; + if(GetHasFeat(FEAT_WOLF_BERSERKER, oPC)) nBonus += 4; + if(GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC) >= 3) nBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if(GetLocalInt(oPC, "LuckyDiceAbility")) nBonus += 1; + if(GetHasSpellEffect(MELD_MAULING_GAUNTLETS, oPC)) nBonus += (2 + (2 * GetEssentiaInvested(oPC, MELD_MAULING_GAUNTLETS))); // MELD_MAULING_GAUNTLETS + if(GetHasSpellEffect(MELD_SPHINX_CLAWS, oPC)) nBonus += (1 + GetEssentiaInvested(oPC, MELD_SPHINX_CLAWS)); // MELD_SPHINX_CLAWS + if(GetLocalInt(oPC, "EventideCrux")) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_DISARM) + { + if(GetHasSpellEffect(MELD_BLADEMELD_CROWN, oPC)) nBonus += 4; + } + + if (nAttacker) + { + if(nCombatMove == COMBAT_MOVE_GRAPPLE) + { + if(GetLocalInt(oPC, "Flay_Grapple")) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_BULLRUSH) + { + if(GetLocalInt(oPC, "Caladbolg_Bullrush")) nBonus += 4; + if (GetLocalInt(oPC, "Marshal_ArtWar")) nBonus += GetLocalInt(oPC, "Marshal_ArtWar"); + if (GetEssentiaInvestedFeat(oPC, FEAT_COBALT_POWER)) nBonus += GetEssentiaInvestedFeat(oPC, FEAT_COBALT_POWER); + if(GetHasFeat(FEAT_RAMPAGING_BULL_RUSH, oPC) && GetHasSpellEffect(SPELLABILITY_BARBARIAN_RAGE, oPC)) nBonus -= 4; // Yes, minus is correct + } + else if(nCombatMove == COMBAT_MOVE_OVERRUN) + { + nBonus += _UrskanGreaves(oPC); + if (GetEssentiaInvestedFeat(oPC, FEAT_COBALT_POWER)) nBonus += GetEssentiaInvestedFeat(oPC, FEAT_COBALT_POWER); + } + else if(nCombatMove == COMBAT_MOVE_TRIP) + { + if (GetEssentiaInvestedFeat(oPC, FEAT_COBALT_EXPERTISE)) nBonus += GetEssentiaInvestedFeat(oPC, FEAT_COBALT_EXPERTISE); + if (GetLocalInt(oPC, "Marshal_ArtWar")) nBonus += GetLocalInt(oPC, "Marshal_ArtWar"); + } + else if(nCombatMove == COMBAT_MOVE_DISARM) + { + if(GetLocalInt(oPC, "Caladbolg_Disarm")) nBonus += 4; + if (GetLocalInt(oPC, "Marshal_ArtWar")) nBonus += GetLocalInt(oPC, "Marshal_ArtWar"); + if (GetEssentiaInvestedFeat(oPC, FEAT_COBALT_EXPERTISE)) nBonus += GetEssentiaInvestedFeat(oPC, FEAT_COBALT_EXPERTISE); + int IsDisarmWeap = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)); + if (IsDisarmWeap == BASE_ITEM_HEAVYFLAIL || IsDisarmWeap == BASE_ITEM_LIGHTFLAIL || IsDisarmWeap == BASE_ITEM_DIREMACE || IsDisarmWeap == BASE_ITEM_WHIP || IsDisarmWeap == BASE_ITEM_NUNCHAKU) nBonus += 2; + if (GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)) == BASE_ITEM_SAI) nBonus += 4; + } + } + else if (nDefender) + { + if(GetHasFeat(FEAT_MOUNTAIN_STANCE, oPC)) nBonus += 2; + if(GetHasSpellEffect(SPELL_UNMOVABLE, oPC)) nBonus += 20; + if(GetHasFeat(FEAT_SHIELD_WARD, oPC)) + { + int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC)); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD) + nBonus += GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC)); + } + + if(nCombatMove == COMBAT_MOVE_GRAPPLE) + { + if(GetHasSpellEffect(MOVE_SD_ROOT_MOUNTAIN, oPC)) nBonus += 10; + nBonus += _TotemAvatar(oPC, nCombatMove); + if(GetHasSpellEffect(MELD_BLADEMELD_WAIST, oPC)) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_BULLRUSH) + { + if(GetLocalInt(oPC, "Steadfast_Rooted")) nBonus += 4; + if(GetHasSpellEffect(MOVE_SD_ROOT_MOUNTAIN, oPC)) nBonus += 10; + if(GetHasSpellEffect(MELD_BEHIR_GORGET, oPC)) nBonus += 4; // MELD_BEHIR_GORGET + if(GetHasSpellEffect(MELD_GORGON_MASK, oPC)) nBonus += (2 + GetEssentiaInvested(oPC, MELD_GORGON_MASK)); // MELD_GORGON_MASK + nBonus += _TotemAvatar(oPC, nCombatMove); + if(GetHasSpellEffect(MELD_BLADEMELD_WAIST, oPC)) nBonus += 4; + if(GetRacialType(oPC) == RACIAL_TYPE_BARIAUR) nBonus += 4; + if(GetRacialType(oPC) == RACIAL_TYPE_WILDREN) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_OVERRUN) + { + if(GetHasSpellEffect(MOVE_SD_ROOT_MOUNTAIN, oPC)) nBonus += 10; + if(GetHasSpellEffect(MELD_GORGON_MASK, oPC)) nBonus += (2 + GetEssentiaInvested(oPC, MELD_GORGON_MASK)); // MELD_GORGON_MASK + nBonus += _TotemAvatar(oPC, nCombatMove); + if(GetHasSpellEffect(MELD_BLADEMELD_WAIST, oPC)) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_TRIP) + { + if(GetLocalInt(oPC, "Steadfast_Rooted")) nBonus += 4; + if(GetHasSpellEffect(MOVE_SD_ROOT_MOUNTAIN, oPC)) nBonus += 10; + if(GetHasSpellEffect(MELD_BEHIR_GORGET, oPC)) nBonus += 4; // MELD_BEHIR_GORGET + if(GetHasSpellEffect(MELD_GORGON_MASK, oPC)) nBonus += (2 + GetEssentiaInvested(oPC, MELD_GORGON_MASK)); // MELD_GORGON_MASK + nBonus += _TotemAvatar(oPC, nCombatMove); + if(GetHasSpellEffect(MELD_BLADEMELD_WAIST, oPC)) nBonus += 4; + if(GetRacialType(oPC) == RACIAL_TYPE_BARIAUR) nBonus += 4; + if(GetRacialType(oPC) == RACIAL_TYPE_WILDREN) nBonus += 4; + } + else if(nCombatMove == COMBAT_MOVE_DISARM) + { + if(GetRacialType(oPC) == RACIAL_TYPE_CHITINE) nBonus += 4; + } + } + if(DEBUG) DoDebug("GetCombatMoveCheckBonus: nBonus " + IntToString(nBonus)); + return nBonus; +} + +void SetIsCharging(object oPC) +{ + SetLocalInt(oPC, "PCIsCharging", TRUE); + // You count as having charged for the entire round + DelayCommand(6.0, DeleteLocalInt(oPC, "PCIsCharging")); +} + +int GetIsCharging(object oPC) +{ + return GetLocalInt(oPC, "PCIsCharging"); +} + +/** + * @brief Initiates a charge action by the player character (PC) toward a target. + * + * This function handles movement, potential attack resolution, damage calculations, + * bull rush attempts, and special feat/ability conditions such as Pounce or Flying Kick. + * + * @param oPC The creature performing the charge (usually the player character). + + * @param oTarget The target of the charge. + + * @param nDoAttack If TRUE (default), the PC will perform an attack after charging. + + * @param nGenerateAoO If TRUE (default), movement may provoke attacks of opportunity (AoOs). + * If FALSE, a high temporary Tumble bonus is applied to prevent AoOs. + + * @param nDamage Initial base damage to apply on a successful hit. Modified by feats or abilities. + + * @param nDamageType The type of damage to apply. Set to -1 (default) to use weapon damage type. + + * @param nBullRush If TRUE, attempt to initiate a Bull Rush after the attack. + + * @param nExtraBonus Additional bonus damage or attack modifiers. Defaults to 0. + + * @param nBullAoO If TRUE (default), allows AoOs triggered during Bull Rush resolution. + + * @param nMustFollow If TRUE (default), the PC will always move toward the target even if it's invalid later. + + * @param nAttack Attack bonus override for the charge attack. 0 = use default or calculate. + + * @param nPounce If TRUE, the PC can perform a full attack on the charge. Determined automatically if not set. + */ +void DoCharge(object oPC, object oTarget, int nDoAttack = TRUE, int nGenerateAoO = TRUE, int nDamage = 0, int nDamageType = -1, int nBullRush = FALSE, int nExtraBonus = 0, int nBullAoO = TRUE, int nMustFollow = TRUE, int nAttack = 0, int nPounce = FALSE) +{ + if(!nGenerateAoO) + { + // Huge bonus to tumble to prevent AoOs from movement + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSkillIncrease(SKILL_TUMBLE, 50), oPC, 6.0); + } + // Return value + int nSucceed = FALSE; + // PnP rules use feet, might as well convert it now. + float fDistance = MetersToFeet(GetDistanceBetweenLocations(GetLocation(oPC), GetLocation(oTarget))); + if(fDistance >= 10.0) + { + // Mark the PC as charging + SetIsCharging(oPC); + + nDamage += _ChargeDamage(oPC); + nAttack = _ChargeAttack(oPC, oTarget, nAttack); // This now takes into account charge feats + effect eNone; + + // Move to the target + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionMoveToObject(oTarget, TRUE)); + if(nDoAttack) // Perform the Attack + { + // Dread Carapace Totem Bind + if(GetIsIncarnumUser(oPC)) + { + if (GetIsMeldBound(oPC, MELD_DREAD_CARAPACE) == CHAKRA_TOTEM) // CHAKRA_TOTEM + { + location lTargetLocation = GetLocation(oPC); + int nSaveDC = GetMeldshaperDC(oPC, CLASS_TYPE_TOTEMIST, MELD_DREAD_CARAPACE); // MELD_DREAD_CARAPACE + + object oDreadTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(60.0), lTargetLocation, FALSE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oDreadTarget)) + { + if(GetIsEnemy(oPC, oDreadTarget)) + { + if(!PRCMySavingThrow(SAVING_THROW_WILL, oDreadTarget, nSaveDC, SAVING_THROW_TYPE_FEAR)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectShaken(), oDreadTarget, 6.0); + + } + //Select the next target within the spell shape. + oDreadTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(60.0), lTargetLocation, FALSE, OBJECT_TYPE_CREATURE); + } + } + + if (GetIsMeldBound(oPC, MELD_SPHINX_CLAWS) == CHAKRA_HANDS && !GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC))) // CHAKRA_HANDS, empty hand + nPounce = TRUE; + } + + // Flying Kick feat + if(GetHasFeat(FEAT_FLYING_KICK, oPC) && (GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) == OBJECT_INVALID)) + nDamage += d12(); + if(GetHasFeat(FEAT_LION_TRIBE_WARRIOR, oPC) && GetIsLightWeapon(oPC) && !IPGetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC))) // No melee weapon in your off hand + nPounce = TRUE; + if(GetHasFeat(FEAT_SNOW_TIGER_BERSERKER, oPC) && GetIsLightWeapon(oPC)) + nPounce = TRUE; + if(GetHasFeat(FEAT_CATFOLK_POUNCE, oPC) && GetIsDeniedDexBonusToAC(oTarget, oPC, TRUE)) + nPounce = TRUE; + if(GetItemPossessedBy(oPC, "WOL_Shishio") == GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) && GetLocalInt(oPC, "Shishio_Pounce")) + nPounce = TRUE; + if(GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oPC) >= 5) + nPounce = TRUE; + if(GetRacialType(oPC) == RACIAL_TYPE_MARRUSAULT) + nPounce = TRUE; + if (GetHasSpellEffect(VESTIGE_CHUPOCLOPS, oPC) && GetLocalInt(oPC, "ExploitVestige") != VESTIGE_CHUPOCLOPS_POUNCE) + nPounce = TRUE; + + // Checks for a White Raven Stance + // If it exists, +1 damage/initiator level + if(GetLocalInt(oPC, "LeadingTheCharge")) + nDamage += GetLocalInt(oPC, "LeadingTheCharge"); + if(nDamageType == -1) // If the damage type isn't set + { + object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + nDamageType = GetWeaponDamageType(oWeap); + } + + float fDelay = FeetToMeters(fDistance)/10; + + if(nPounce) // Uses instant attack in order to make sure they all go off in the alloted period of time. + DelayCommand(fDelay, PerformAttackRound(oTarget, oPC, eNone, 0.0, nAttack, nDamage, nDamageType, FALSE, "Charge Hit", "Charge Miss", FALSE, FALSE, TRUE)); + else if(GetLocalInt(oPC, "BariaurCharge")) // Bariaur Charge + { + int nRoll = GetAttackRoll(oTarget, oPC, OBJECT_INVALID, 0, 0, nAttack); + _BariaurChargeDamage(oPC, oTarget, nRoll, nDamage); + DeleteLocalInt(oPC, "BariaurCharge"); + } + else if(GetLocalInt(oPC, "ShifterGore")) //Gorebrute shifter option + { + string sResRef = "prc_shftr_gore_"; + int nSize = PRCGetCreatureSize(oPC); + if(GetHasFeat(FEAT_SHIFTER_SAVAGERY) && GetHasFeatEffect(FEAT_FRENZY, oTarget)) + nSize += 2; + if(nSize > CREATURE_SIZE_COLOSSAL) + nSize = CREATURE_SIZE_COLOSSAL; + sResRef += GetAffixForSize(nSize); + object oHorns = CreateItemOnObject(sResRef, oPC); + DelayCommand(fDelay, PerformAttack(oTarget, oPC, eNone, 0.0, nAttack, nDamage + (GetHitDice(oPC) / 4), DAMAGE_TYPE_PIERCING, "Horns Hit", "Horns Miss", FALSE, oHorns)); + DestroyObject(oHorns); + } + else if(GetLocalInt(oPC, "ShifterClaw")) //Razorclaw Elite shifted option + { + object oWeaponL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC); + object oWeaponR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + DelayCommand(fDelay, PerformAttack(oTarget, oPC, eNone, 0.0, nAttack, nDamage, DAMAGE_TYPE_SLASHING, "Claw Hit", "Claw Miss", FALSE, oWeaponR, oWeaponL)); + DelayCommand(fDelay, PerformAttack(oTarget, oPC, eNone, 0.0, nAttack, nDamage, DAMAGE_TYPE_SLASHING, "Claw Hit", "Claw Miss", FALSE, oWeaponR, oWeaponL, 1)); + } + else + { + if(GetHasFeat(FEAT_TWO_WEAPON_POUNCE, oPC) && IPGetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC))) + { + nAttack -= 2; + DelayCommand(fDelay, PerformAttack(oTarget, oPC, eNone, 0.0, nAttack, nDamage, nDamageType, "Two Weapon Pounce Hit", "Two Weapon Pounce Miss", FALSE, OBJECT_INVALID, OBJECT_INVALID, TRUE)); + } + else if (GetLocalInt(oPC, "TigerFangCharge")) // Grants one extra attack at the end of a charge + DelayCommand(fDelay, PerformAttack(oTarget, oPC, eNone, 0.0, nAttack, nDamage, nDamageType, "Frenzied Charge Hit", "Frenzied Charge Miss", FALSE, OBJECT_INVALID, OBJECT_INVALID, TRUE)); + DelayCommand(fDelay, PerformAttack(oTarget, oPC, eNone, 0.0, nAttack, nDamage, nDamageType, "Charge Hit", "Charge Miss")); + } + DelayCommand(fDelay, _PostCharge(oPC, oTarget)); + } + if(nBullRush) + { + DoBullRush(oPC, oTarget, nExtraBonus, nBullAoO, nMustFollow); + FloatingTextStringOnCreature("You are bull rushing " + GetName(oTarget), oPC); + } + } + else + { + FloatingTextStringOnCreature("You are too close to charge " + GetName(oTarget), oPC); + } +} + +void DoBullRush(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, int nMustFollow = TRUE, int nNoMove = FALSE, int nAbility = 0) +{ + if (!nNoMove) + { + // Move to the target + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionMoveToObject(oTarget, TRUE)); + } + effect eNone; + + // Do the AoO for moving into the enemy square + if (nGenerateAoO) + { + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: AoO Start", oPC, FALSE); + location lTarget = GetLocation(oPC); + // Use the function to get the closest creature as a target + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget)) + { + // All enemies in range get a free AoO shot + if(oAreaTarget != oPC && // Don't hit yourself + GetIsInMeleeRange(oPC, oAreaTarget) && // They must be in melee range + GetIsEnemy(oAreaTarget, oPC)) // Only enemies are going to take AoOs + { + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: AoO Part 1", oPC, FALSE); + if (!GetHasFeat(FEAT_IMPROVED_BULLRUSH, oPC) || oAreaTarget != oTarget) //Improved Bullrush means the defender can't take the AoO + { + // Perform the Attack + DelayCommand(0.0, PerformAttack(oPC, oAreaTarget, eNone, 0.0, 0, 0, 0, "Attack of Opportunity Hit", "Attack of Opportunity Miss")); + FloatingTextStringOnCreature(GetName(oAreaTarget)+" Bull Rush Attack of Opportunity", oPC, FALSE); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullRush: AoO Part 2", oPC, FALSE); + } + } + + //Select the next target within the spell shape. + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + } + float fDelay = PRCGetSpellEffectDelay(GetLocation(oPC), oTarget) * 2.5; + if (nNoMove) fDelay = 0.1; // No need for a delay if it's a spell + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoBullrush: Delay: "+FloatToString(fDelay), oPC, FALSE); + DelayCommand(fDelay,_DoBullrushCheck(oPC, oTarget, nExtraBonus, nMustFollow, nAbility)); +} + +int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, int nCounterTrip = TRUE, int nSkipTouch = FALSE, int nAbi = 0) +{ + // The basic modifiers + int nSucceed = FALSE; + effect eNone; + int nPCStat, nTargetStat; + // Use the higher of the two mods + if (GetAbilityModifier(ABILITY_STRENGTH, oPC) > GetAbilityModifier(ABILITY_DEXTERITY, oPC)) + nPCStat = GetAbilityModifier(ABILITY_STRENGTH, oPC); + else + nPCStat = GetAbilityModifier(ABILITY_DEXTERITY, oPC); + // Use the higher of the two mods + if (GetAbilityModifier(ABILITY_STRENGTH, oTarget) > GetAbilityModifier(ABILITY_DEXTERITY, oTarget)) + nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + else + nTargetStat = GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + // Override + if (nAbi > 0) nPCStat = nAbi; + // Get mods for size + int nPCBonus = PRCGetSizeModifier(oPC) + GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_TRIP, FALSE, TRUE); + int nTargetBonus = PRCGetSizeModifier(oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, TRUE); + //Warblade Battle Skill bonus + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oPC) >= 11) + { + nPCBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Trip bonus (attacker)"); + } + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oTarget) >= 11) + { + nTargetBonus += GetAbilityModifier(ABILITY_INTELLIGENCE, oTarget); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Trip bonus (defender)"); + } + if (GetHasFeat(FEAT_IMPROVED_TRIP, oPC)) nPCBonus += 4; + // Do the AoO for a trip attempt + if (nGenerateAoO && !GetHasFeat(FEAT_IMPROVED_TRIP, oPC)) + { + // Perform the Attack + effect eNone; + DelayCommand(0.0, PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, 0, "Attack of Opportunity Hit", "Attack of Opportunity Miss")); + } + int nPCCheck = nPCStat + nPCBonus + nExtraBonus + d20(); + int nTargetCheck = nTargetStat + nTargetBonus + d20(); + + int nTouch; + if (nSkipTouch == TRUE) nTouch = TRUE; + else nTouch = PRCDoMeleeTouchAttack(oTarget, TRUE, oPC); + + // Gotta touch them + if (nTouch) + { + SendMessageToPC(oPC, "Trip Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + // Now roll the ability check + if (nPCCheck >= nTargetCheck) + { + // Knock em down + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oTarget, 6.0); + nSucceed = TRUE; + SetLocalInt(oPC, "TripDifference", nPCCheck - nTargetCheck); + DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference")); + + if (!GetLocalInt(oPC, "CurlingWaveStrikeLoop")) // Neither of these trigger when Curling Wave Strike Loop protection is active + { + if (GetHasFeat(FEAT_CURLING_WAVE_STRIKE, oPC) && !GetLocalInt(oPC, "CurlingWaveStrikeRound")) // Once a round + _DoCurlingWaveStrike(oPC, oTarget); + else if (GetHasFeat(FEAT_IMPROVED_TRIP, oPC)) // Do the free Improved Trip attack + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, 0, 0, "Improved Trip Free Attack Hit", "Improved Trip Free Attack Miss")); + } + } + else // If you fail, enemy gets a counter trip attempt, using Strength + { + nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE); + FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); + // Roll counter trip attempt + nTargetCheck = nTargetStat + nTargetBonus + d20(); + nPCCheck = nPCStat + nPCBonus + d20(); + // If counters aren't allowed, don't knock em down + // Its down here to allow the text message to go through + SendMessageToPC(oPC, "Enemy Counter Trip Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + if (nTargetCheck >= nPCCheck && nCounterTrip) + { + // Knock em down + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oPC, 6.0); + } + SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck); + DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference")); + } + } + else + FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); + + // Now we do the rest of the attacks in the round, regardless, remembering to bump down the iteratives + SetLocalInt(oPC, "CombatMoveAttack", TRUE); + DelayCommand(5.5, DeleteLocalInt(oPC, "CombatMoveAttack")); + if (GetBaseAttackBonus(oPC) >= 6) PerformAttackRound(oTarget, oPC, eNone, 0.0, -5); + DelayCommand(0.25, AssignCommand(oPC, ActionAttack(oTarget))); + + // Let people know if we made the hit or not + return nSucceed; +} + +int GetIntToDamage(int nCheck) +{ + switch(nCheck) + { + case 1: return DAMAGE_BONUS_1; + case 2: return DAMAGE_BONUS_2; + case 3: return DAMAGE_BONUS_3; + case 4: return DAMAGE_BONUS_4; + case 5: return DAMAGE_BONUS_5; + case 6: return DAMAGE_BONUS_6; + case 7: return DAMAGE_BONUS_7; + case 8: return DAMAGE_BONUS_8; + case 9: return DAMAGE_BONUS_9; + case 10: return DAMAGE_BONUS_10; + case 11: return DAMAGE_BONUS_11; + case 12: return DAMAGE_BONUS_12; + case 13: return DAMAGE_BONUS_13; + case 14: return DAMAGE_BONUS_14; + case 15: return DAMAGE_BONUS_15; + case 16: return DAMAGE_BONUS_16; + case 17: return DAMAGE_BONUS_17; + case 18: return DAMAGE_BONUS_18; + case 19: return DAMAGE_BONUS_19; + case 20: return DAMAGE_BONUS_20; + case 21: return DAMAGE_BONUS_21; + case 22: return DAMAGE_BONUS_22; + case 23: return DAMAGE_BONUS_23; + case 24: return DAMAGE_BONUS_24; + case 25: return DAMAGE_BONUS_25; + case 26: return DAMAGE_BONUS_26; + case 27: return DAMAGE_BONUS_27; + case 28: return DAMAGE_BONUS_28; + case 29: return DAMAGE_BONUS_29; + case 30: return DAMAGE_BONUS_30; + case 31: return DAMAGE_BONUS_31; + case 32: return DAMAGE_BONUS_32; + case 33: return DAMAGE_BONUS_33; + case 34: return DAMAGE_BONUS_34; + case 35: return DAMAGE_BONUS_35; + case 36: return DAMAGE_BONUS_36; + case 37: return DAMAGE_BONUS_37; + case 38: return DAMAGE_BONUS_38; + case 39: return DAMAGE_BONUS_39; + case 40: return DAMAGE_BONUS_40; + case 41: return DAMAGE_BONUS_41; + case 42: return DAMAGE_BONUS_42; + case 43: return DAMAGE_BONUS_43; + case 44: return DAMAGE_BONUS_44; + case 45: return DAMAGE_BONUS_45; + case 46: return DAMAGE_BONUS_46; + case 47: return DAMAGE_BONUS_47; + case 48: return DAMAGE_BONUS_48; + case 49: return DAMAGE_BONUS_49; + case 50: return DAMAGE_BONUS_50; + } + return -1; +} + +/** + * @brief Attempts to initiate a grapple between the PC and the target creature. + * + * @param oPC The player character initiating the grapple attempt. + * @param oTarget The target creature to be grappled. + * @param nExtraBonus Additional bonus added to the grapple check (e.g., from feats, spells, items). + * @param nGenerateAoO If TRUE, the target is allowed an Attack of Opportunity unless the PC has Improved Grapple. + * Defaults to TRUE. + * @param nSkipTouch If TRUE, the melee touch attack step is skipped (assumes auto-hit). + * Useful when a touch attack was already resolved elsewhere. + * + * @return TRUE if the grapple attempt succeeds, FALSE otherwise. + */ +int DoGrapple(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, int nSkipTouch = FALSE) +{ + if (GetGrapple(oTarget)) + { + FloatingTextStringOnCreature("You cannot grapple a creature that is already grappled.", oPC, FALSE); + return FALSE; + } + if(GetHasSpellEffect(SPELL_FREEDOM_OF_MOVEMENT, oTarget)) + { + FloatingTextStringOnCreature("You cannot grapple a creature under the effect of Freedom of Movement", oPC, FALSE); + return FALSE; + } + + if (IPGetHasItemPropertyOnCharacter(oTarget, ITEM_PROPERTY_FREEDOM_OF_MOVEMENT)) + { + FloatingTextStringOnCreature("You cannot grapple a creature under the effect of Freedom of Movement.", oPC, FALSE); + return FALSE; + } + if (GetIsImmune(oTarget, IMMUNITY_TYPE_ENTANGLE)) + { + FloatingTextStringOnCreature("This creature is immune to grappling.", oPC, FALSE); + return FALSE; + } + + object oGlaive = GetItemPossessedBy(oPC, "prc_eldrtch_glv"); + if(GetIsObjectValid(oGlaive)) + DestroyObject(oGlaive); + + int nSucceed = FALSE; + effect eNone; + // Do the AoO for trying a grapple + if (nGenerateAoO && !GetHasFeat(FEAT_IMPROVED_GRAPPLE, oPC)) + { + // Perform the Attack + DelayCommand(0.0, PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, 0, "Attack of Opportunity Hit", "Attack of Opportunity Miss")); + + if (GetLocalInt(oPC, "PRCCombat_StruckByAttack")) + { + FloatingTextStringOnCreature("You have failed at your Grapple Attempt.", oPC, FALSE); + return FALSE; + } + } + + int nTouch; + if (nSkipTouch == TRUE) nTouch = TRUE; + else nTouch = PRCDoMeleeTouchAttack(oTarget, TRUE, oPC); + + // Gotta touch them + if (nTouch) + { + // Now roll the ability check + if (_DoGrappleCheck(oPC, oTarget, nExtraBonus)) + { + FloatingTextStringOnCreature("You have successfully grappled " + GetName(oTarget), oPC, FALSE); + int nBearFang = GetLocalInt(oPC, "BearFangGrapple"); + SetGrapple(oPC); + SetGrapple(oTarget); + SetLocalInt(oPC, "GrappleOriginator", TRUE); + effect eHold = EffectCutsceneParalyze(); + effect eEntangle = EffectVisualEffect(VFX_DUR_SPELLTURNING_R); + effect eLink = EffectLinkEffects(eHold, eEntangle); + //apply the effect + if (!GetLocalInt(oPC, "Flay_Grapple")) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eLink), oPC, 9999.0); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eLink), oTarget, 9999.0); + nSucceed = TRUE; + + if (nBearFang) // Grants a bonus attack on hit + { + // Attack with a -4 penalty + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, -4, 0, 0, "Bear Fang Attack Hit", "Bear Fang Attack Miss")); + DeleteLocalInt(oPC, "BearFangGrapple"); + } + else + { + // Now hit them with an unarmed strike + object oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + effect eDam = GetAttackDamage(oTarget, oPC, oWeap, GetWeaponBonusDamage(oWeap, oTarget), GetMagicalBonusDamage(oPC, oTarget)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + } + _HeartOfFireGrapple(oPC, oTarget); + + // If you kill them with this, best to clean up right away + if (GetIsDead(oTarget) || !GetIsObjectValid(oTarget)) + { + EndGrapple(oPC, oTarget); + // Remove the hooks + if(DEBUG) DoDebug("prc_grapple: Removing eventhooks"); + RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_grapple", TRUE, FALSE); + FloatingTextStringOnCreature("Your target is dead, ending grapple", oPC, FALSE); + } + else + { + // Hook in the events and save the target for an ongoing grapple + if(DEBUG) DoDebug("prc_grapple: Adding eventhooks"); + SetGrappleTarget(oPC, oTarget); + AddEventScript(oPC, EVENT_ONHEARTBEAT, "prc_grapple", TRUE, FALSE); + } + } + else + FloatingTextStringOnCreature("You have failed your Grapple Attempt", oPC, FALSE); + } + + // Let people know if we made the hit or not + return nSucceed; +} + +void SetGrapple(object oTarget) +{ + SetLocalInt(oTarget, "IsGrappled", TRUE); +} + +int GetGrapple(object oTarget) +{ + return GetLocalInt(oTarget, "IsGrappled"); +} + +void SetGrappleTarget(object oPC, object oTarget) +{ + SetLocalObject(oPC, "GrappleTarget", oTarget); +} + +object GetGrappleTarget(object oPC) +{ + return GetLocalObject(oPC, "GrappleTarget"); +} + +void SetIsPinned(object oTarget) +{ + SetLocalInt(oTarget, "IsPinned", TRUE); +} + +int GetIsPinned(object oTarget) +{ + return GetLocalInt(oTarget, "IsPinned"); +} + +void BreakPin(object oTarget) +{ + DeleteLocalInt(oTarget, "IsPinned"); + DeleteLocalInt(oTarget, "PinnedRounds"); +} + +void EndGrapple(object oPC, object oTarget) +{ + DeleteLocalInt(oPC, "IsGrappled"); + DeleteLocalInt(oTarget, "IsGrappled"); + DeleteLocalInt(oTarget, "PinnedRounds"); + DeleteLocalObject(oPC, "GrappleTarget"); + DeleteLocalInt(oTarget, "UnconsciousGrapple"); + DeleteLocalInt(oPC, "GrappleOriginator"); + DeleteLocalInt(oPC, "ScorpionLight"); + DeleteLocalInt(oPC, "Flay_Grapple"); + RemoveEventScript(oPC, EVENT_ITEM_ONHIT, "wol_m_flay", TRUE, FALSE); + BreakPin(oTarget); + effect eBad = GetFirstEffect(oTarget); + //Search for negative effects + while(GetIsEffectValid(eBad)) + { + int nInt = GetEffectSpellId(eBad); + if (GetEffectType(eBad) == EFFECT_TYPE_CUTSCENE_PARALYZE) + { + RemoveEffect(oTarget, eBad); + } + eBad = GetNextEffect(oTarget); + } + eBad = GetFirstEffect(oPC); + //Search for negative effects + while(GetIsEffectValid(eBad)) + { + int nInt = GetEffectSpellId(eBad); + if (GetEffectType(eBad) == EFFECT_TYPE_CUTSCENE_PARALYZE) + { + RemoveEffect(oPC, eBad); + } + eBad = GetNextEffect(oPC); + } +} + +int DoGrappleOptions(object oPC, object oTarget, int nExtraBonus, int nSwitch = -1) +{ + effect eNone; + + int nSuccess = _DoGrappleCheck(oPC, oTarget, nExtraBonus, nSwitch); + // This applies on all grapples regardless of success or failure + _HeartOfFireGrapple(oPC, oTarget); + if(GetHasSpellEffect(SPELL_FREEDOM_OF_MOVEMENT, oTarget)) + { + nSuccess = TRUE; + nSwitch = GRAPPLE_ESCAPE; + } + + if (nSwitch == GRAPPLE_ATTACK) + { + // Must be a light weapon, and succeed at the grapple check + if (nSuccess && (GetIsLightWeapon(oPC) || GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC) || GetHasFeat(FEAT_SPIKED_BODY, oPC)||GetHasFeat(FEAT_NATURAL_SPIKES))) + { + if (GetIsMeldBound(oTarget, MELD_RAGECLAWS) == CHAKRA_TOTEM && !GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC))) // CHAKRA_TOTEM + { + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, 0, 0, "Rageclaws Hit", "Rageclas Miss")); + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, 0, 0, "Rageclaws Hit", "Rageclas Miss")); + } + else if (GetHasSpellEffect(MOVE_TC_WOLVERINE_STANCE, oPC)) + { + int nDam = 0; + if (PRCGetCreatureSize(oTarget) > PRCGetCreatureSize(oPC)) nDam = 4; + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, nDam, 0, "Wolverine Stance Hit", "Wolverine Stance Miss")); + } + else if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC)) + { + int nDam = d6(); + if(GetLevelByClass(CLASS_TYPE_WARFORGED_JUGGERNAUT, oPC) >= 4) nDam = d8(); + + effect eDeath = EffectDamage(nDam, DAMAGE_TYPE_PIERCING); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oTarget); + FloatingTextStringOnCreature("Warforged Juggernaut Armor Spikes Hit", oPC, FALSE); + } + else if(GetHasFeat(FEAT_SPIKED_BODY, oPC)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(), DAMAGE_TYPE_PIERCING), oTarget); + FloatingTextStringOnCreature("Spiked Body Hit", oPC, FALSE); + } + else if(GetHasFeat(FEAT_NATURAL_SPIKES, oPC)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(), DAMAGE_TYPE_PIERCING), oTarget); + FloatingTextStringOnCreature("Natural Spikes Hit", oPC, FALSE); + } + else + { + // Attack with a -4 penalty + int nPen = -4; + if (GetLocalInt(oPC, "ScorpionLight")) nPen = 0; + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, nPen, 0, 0, "Grapple Light Weapon Attack Hit", "Grapple Light Weapon Attack Miss")); + } + } + else + FloatingTextStringOnCreature("You have failed your Grapple Light Weapon Attack Attempt",oPC, FALSE); + } + else if (nSwitch == GRAPPLE_OPPONENT_WEAPON) + { + // Must be a light weapon, and succeed at the grapple check + if (nSuccess && GetIsLightWeapon(oTarget)) + { + // Attack with a -4 penalty + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, -4, 0, 0, "Grapple Opponent's Weapon Hit", "Grapple Opponent's Weapon Miss", FALSE, oWeapon)); + } + else + FloatingTextStringOnCreature("You have failed your Grapple Opponent's Weapon Attempt",oPC, FALSE); + } + else if (nSwitch == GRAPPLE_DAMAGE) + { + // Must be a light weapon, and succeed at the grapple check + if (GetLocalInt(oPC, "Flay_Grapple")) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d3()+5), oTarget); + FloatingTextStringOnCreature("Flay Constrict", oPC, FALSE); + } + else if (GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oPC) >= 8) + { + // Now hit them with a lot of unarmed strikes + object oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + effect eDam = GetAttackDamage(oTarget, oPC, oWeap, GetWeaponBonusDamage(oWeap, oTarget), GetMagicalBonusDamage(oPC, oTarget)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC); + eDam = GetAttackDamage(oTarget, oPC, oWeap, GetWeaponBonusDamage(oWeap, oTarget), GetMagicalBonusDamage(oPC, oTarget)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oPC); + eDam = GetAttackDamage(oTarget, oPC, oWeap, GetWeaponBonusDamage(oWeap, oTarget), GetMagicalBonusDamage(oPC, oTarget)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + // Rend damage is double claw damage + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d8(2) + (2 * GetAbilityModifier(ABILITY_STRENGTH, oPC)), DAMAGE_TYPE_SLASHING), oTarget); + FloatingTextStringOnCreature("Savage Grapple Hit",oPC, FALSE); + } + else if (GetHasFeat(FEAT_OWLBEAR_BERSERKER, oPC)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6()), oTarget); + FloatingTextStringOnCreature("Owlbear Berserker Hit",oPC, FALSE); + } + else if (nSuccess) + { + // Now hit them with an unarmed strike + object oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + effect eDam = GetAttackDamage(oTarget, oPC, oWeap, GetWeaponBonusDamage(oWeap, oTarget), GetMagicalBonusDamage(oPC, oTarget)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + FloatingTextStringOnCreature("Grapple Unarmed Damage Hit",oPC, FALSE); + } + else + FloatingTextStringOnCreature("You have failed your Grapple Unarmed Damage Attempt",oPC, FALSE); + + // This is bonus damage that applies to successful grappling of any of the above + if (GetHasSpellEffect(VESTIGE_ZAGAN, oPC) && GetLocalInt(oPC, "ExploitVestige") != VESTIGE_ZAGAN_CONSTRICT && nSuccess) + { + int nDam = d8() + GetAbilityModifier(ABILITY_STRENGTH, oPC) + GetAbilityModifier(ABILITY_STRENGTH, oPC)/2; + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam), oTarget); + FloatingTextStringOnCreature("Zagan Constrict Hit",oPC, FALSE); + } + } + else if (nSwitch == GRAPPLE_ESCAPE) + { + // Must be a light weapon, and succeed at the grapple check + if (nSuccess) + { + if (GetIsPinned(oPC)) + { + BreakPin(oPC); + if (GetIsPC(oPC)) + FloatingTextStringOnCreature("You have escaped the pin", oPC, FALSE); + else + FloatingTextStringOnCreature("Your target has escaped your pin", oTarget, FALSE); + } + else + { + EndGrapple(oPC, oTarget); + + if (GetIsPC(oPC)) + { + // Remove the hooks + if(DEBUG) DoDebug("prc_grapple: Removing eventhooks"); + RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_grapple", TRUE, FALSE); + FloatingTextStringOnCreature("You have escaped the grapple", oPC, FALSE); + } + else + { + // Remove the hooks + if(DEBUG) DoDebug("prc_grapple: Removing eventhooks"); + RemoveEventScript(oTarget, EVENT_ONHEARTBEAT, "prc_grapple", TRUE, FALSE); + FloatingTextStringOnCreature("Your target has escaped your grapple", oTarget, FALSE); + // Target is valid and we know it's an enemy and we're in combat + DelayCommand(0.25, AssignCommand(oTarget, ActionAttack(oPC))); + } + } + } + else + FloatingTextStringOnCreature("You have failed your Grapple Escape Attempt",oPC, FALSE); + } + else if (nSwitch == GRAPPLE_TOB_CRUSHING && GetHasSpellEffect(MOVE_SD_CRUSHING_WEIGHT, oPC)) + { + // Constrict for 2d6 + 1.5 Strength + if (nSuccess) + { + int nDam = FloatToInt(d6(2) + (GetAbilityModifier(ABILITY_STRENGTH, oPC) * 1.5)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam), oTarget); + FloatingTextStringOnCreature("Crushing Weight Success",oPC, FALSE); + } + else + FloatingTextStringOnCreature("You have failed your Crushing Weight Attempt",oPC, FALSE); + } + else if (nSwitch == GRAPPLE_PIN) + { + // Pins the target + if (nSuccess) + { + FloatingTextStringOnCreature("You have pinned your target", oPC, FALSE); + SetIsPinned(oTarget); + int nClass = GetLevelByClass(CLASS_TYPE_REAPING_MAULER, oPC); + if (nClass) + { + int nRounds = _CountPinRounds(oTarget); + _DoReapingMauler(oPC, oTarget, nRounds); + } + if (GetHasFeat(FEAT_EARTHS_EMBRACE, oPC)) + { + // Add in unarmed damage + int nDamageSize = FindUnarmedDamage(oPC); + + int nDie = StringToInt(Get2DACache("iprp_monstcost", "Die", nDamageSize)); + int nNum = StringToInt(Get2DACache("iprp_monstcost", "NumDice", nDamageSize)); + int nRoll; + + //Potential die options + if(nDie == 4) nRoll = d4(nNum); + else if(nDie == 6) nRoll = d6(nNum); + else if(nDie == 8) nRoll = d8(nNum); + else if(nDie == 10) nRoll = d10(nNum); + else if(nDie == 12) nRoll = d12(nNum); + else if(nDie == 20) nRoll = d20(nNum); + + FloatingTextStringOnCreature("Earth's Embrace chokes the life from your foe", oPC, FALSE); + + int nDam = d12() + nRoll; + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, DAMAGE_TYPE_BLUDGEONING), oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACDecrease(4)), oPC, 6.0); + } + } + else + FloatingTextStringOnCreature("You have failed your Grapple Pin Attempt",oPC, FALSE); + } + else + { + FloatingTextStringOnCreature("DoGrappleOptions: Error, invalid option "+IntToString(nSwitch)+" passed to function by "+GetName(oPC),oPC, FALSE); + FloatingTextStringOnCreature("DoGrappleOptions: Error, GetGrapple(oPC) "+IntToString(GetGrapple(oPC))+" GetGrapple(oTarget) "+IntToString(GetGrapple(oTarget)),oPC, FALSE); + return FALSE; + } + + return nSuccess; +} + +int GetIsLightWeapon(object oPC) +{ + // You may use any weapon in a grapple with this stance. + if (GetHasSpellEffect(MOVE_TC_WOLVERINE_STANCE, oPC)) return TRUE; + int nSize = PRCGetCreatureSize(oPC); + object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + int nWeaponSize = GetWeaponSize(oItem); + // is the size appropriate for a light weapon? + return (nWeaponSize < nSize); +} + +void DoOverrun(object oPC, object oTarget, location lTarget, int nGenerateAoO = TRUE, int nExtraBonus = 0, int nAvoid = TRUE, int nCounter = TRUE) +{ + effect eCharge = SupernaturalEffect(EffectMovementSpeedIncrease(99)); // Just to speed things up a bit + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCharge, oPC, 5.0); + SetLocalInt(oPC, "Overrun", 2); // This tells _DoOverrunCheck it's a valid call + float fLength = GetDistanceBetweenLocations(GetLocation(oPC), lTarget); + vector vOrigin = GetPosition(oPC); + + if (GetIsMeldBound(oPC, MELD_URSKAN_GREAVES) == CHAKRA_TOTEM) + nAvoid = FALSE; + if (GetHasFeat(FEAT_CENTAUR_TRAMPLE, oPC)) + nAvoid = FALSE; + + // Step one is check to see if we're using oTarget or not + if (GetIsObjectValid(oTarget)) + { + lTarget = GetLocation(oTarget); + fLength = GetDistanceBetweenLocations(GetLocation(oPC), lTarget); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoOverrun: Valid Target", oPC, FALSE); + } + else + oTarget = MyFirstObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE, vOrigin); // Only change targets if invalid + + // Move the PC to the location + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, ActionForceMoveToLocation(lTarget, TRUE)); + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoOverrun: Distance: "+FloatToString(fLength), oPC, FALSE); + + // Loop over targets in the line shape + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoOverrun: First Target: "+GetName(oTarget), oPC, FALSE); + while(GetIsObjectValid(oTarget)) // Find the targets + { + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoOverrun: Loop 1, Target: "+GetName(oTarget), oPC, FALSE); + if(oTarget != oPC && GetIsEnemy(oTarget, oPC)) // Don't overrun friends or yourself + { + float fDelay = PRCGetSpellEffectDelay(GetLocation(oPC), oTarget) * 2.5; + if (DEBUG_COMBAT_MOVE) FloatingTextStringOnCreature("DoOverrun: Loop 2, Delay: "+FloatToString(fDelay), oPC, FALSE); + DelayCommand(fDelay,_DoOverrunCheck(oPC, oTarget, lTarget, nGenerateAoO, nExtraBonus, nAvoid, nCounter)); + }// end if - Target validity check + // Get next target + oTarget = MyNextObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE, vOrigin); + }// end while - Target loop + + // Clean up + DelayCommand(3.0,DeleteLocalInt(oPC, "Overrun")); +} + +int PRCGetSizeModifier(object oCreature) +{ + int nSize = PRCGetCreatureSize(oCreature); + + //Powerful Build bonus + if(GetHasFeat(FEAT_RACE_POWERFUL_BUILD, oCreature)) + nSize++; + //Make sure it doesn't overflow + if(nSize > CREATURE_SIZE_COLOSSAL) nSize = CREATURE_SIZE_COLOSSAL; + int nModifier = 0; + + // Jotunbrud + if(GetHasFeat(FEAT_JOTUNBRUD, oCreature) && nSize == CREATURE_SIZE_MEDIUM) + nSize++; + + // Oddly, this overrides everything else and just sets your size as large + if (GetHasSpellEffect(VESTIGE_ZAGAN, oCreature) && GetLocalInt(oCreature, "ExploitVestige") != VESTIGE_ZAGAN_GRAPPLE) + nSize = CREATURE_SIZE_LARGE; + + switch (nSize) + { + case CREATURE_SIZE_TINY: nModifier = -8; break; + case CREATURE_SIZE_SMALL: nModifier = -4; break; + case CREATURE_SIZE_MEDIUM: nModifier = 0; break; + case CREATURE_SIZE_LARGE: nModifier = 4; break; + case CREATURE_SIZE_HUGE: nModifier = 8; break; + case CREATURE_SIZE_GARGANTUAN: nModifier = 12; break; + case CREATURE_SIZE_COLOSSAL: nModifier = 16; break; + } + return nModifier; +} + +void TigerBlooded(object oInitiator, object oTarget) +{ + // Got to have the feat first and hit the opponent + if (!GetHasFeat(FEAT_TIGER_BLOODED, oInitiator)) return; + if (!GetLocalInt(oTarget, "PRCCombat_StruckByAttack")) return; + + if(GetHasSpellEffect(SPELLABILITY_BARBARIAN_RAGE, oInitiator) || + GetIsPolyMorphedOrShifted(oInitiator) || + GetHasSpellEffect(SPELL_SPELL_RAGE, oInitiator) || + GetHasSpellEffect(SPELL_BLOOD_FRENZY, oInitiator) || + GetHasSpellEffect(SPELL_FRENZY, oInitiator) || + GetHasSpellEffect(SPELL_INSPIRE_FRENZY, oInitiator) || + GetHasSpellEffect(SPELL_TRIBAL_FRENZY , oInitiator) || + GetHasSpellEffect(INVOKE_WILD_FRENZY, oInitiator)) + { + int nHD = GetHitDice(oInitiator)/2; + int nStr = GetAbilityModifier(ABILITY_STRENGTH, oInitiator); + int nDC = 10 + nHD + nStr; + + if (PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC) && PRCGetSizeModifier(oInitiator) >= PRCGetSizeModifier(oTarget)) + { + _DoBullRushKnockBack(oTarget, oInitiator, 5.0); + FloatingTextStringOnCreature("Tiger Blooded Knockback", oInitiator, FALSE); + } + } +} + +int DoDisarm(object oPC, object oTarget, int nExtraBonus = 0, int nGenerateAoO = TRUE, int nCounter = TRUE) +{ + object oTargetWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + + if (!GetIsObjectValid(oTargetWep) || GetPlotFlag(oTargetWep) || (!GetIsCreatureDisarmable(oTarget) && !GetPRCSwitch(PRC_PNP_DISARM)) || GetLocalInt(oTarget, "TigerFangDisarm")) + { + FloatingTextStringOnCreature("Target is not a legal target", oPC, FALSE); + AssignCommand(oPC, ActionAttack(oTarget)); + return FALSE; + } + + // The basic modifiers + int nSucceed = FALSE; + object oPCWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + effect eNone; + GetAttackRoll(oTarget, oPC, oPCWep, 0, 0, 0, FALSE); + GetAttackRoll(oPC, oTarget, oTargetWep, 0, 0, 0, FALSE); + int nPCAttack = GetLocalInt(oPC, "PRCAttackBonus"); + int nTargetAttack = GetLocalInt(oTarget, "PRCAttackBonus"); + + nPCAttack += GetCombatMoveCheckBonus(oPC, COMBAT_MOVE_DISARM, FALSE, TRUE); + nTargetAttack += GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_DISARM, TRUE); + + int nPCSize = PRCGetCreatureSize(oPC); + int nPCWeaponSize = GetWeaponSize(oPCWep); + int nTGSize = PRCGetCreatureSize(oTarget); + int nTGWeaponSize = GetWeaponSize(oTargetWep); + + // Two-handed weapon + if (nPCWeaponSize >= nPCSize + 1) + nPCAttack += 4; + if (nTGWeaponSize >= nTGSize + 1) + nTargetAttack += 4; + // Light weapon or unarmed + if ((nPCSize > nPCWeaponSize) || !GetIsObjectValid(oPCWep)) + nPCAttack -= 4; + if (nTGSize > nTGWeaponSize) + nTargetAttack -= 4; + + //Larger combatant gets a +4 bonus per size difference, applied as a bonus or penalty to the PCAttack + nPCAttack += (nPCSize - nTGSize) * 4; + + //Warblade Battle Skill bonus + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oPC) >= 11) + { + nPCAttack += GetAbilityModifier(ABILITY_INTELLIGENCE, oPC); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Disarm bonus (attacker)"); + } + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oTarget) >= 11) + { + nTargetAttack += GetAbilityModifier(ABILITY_INTELLIGENCE, oTarget); + if (DEBUG_COMBAT_MOVE) DoDebug("Warblade Battle Skill Disarm bonus (defender)"); + } + if (GetHasFeat(FEAT_PRC_IMP_DISARM, oPC)) + // Check if oPC is the same as oTarget and return immediately + if (oPC == oTarget) + { + FloatingTextStringOnCreature("You can't Disarm yourself.", oPC, FALSE); + return FALSE; + } + + if (GetHasFeat(FEAT_PRC_IMP_DISARM, oPC)) + { + nPCAttack += 4; + nGenerateAoO = FALSE; + nCounter = FALSE; + } + + // Do the AoO for a trip attempt + if (nGenerateAoO) + { + // Perform the Attack + effect eNone; + DelayCommand(0.0, PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, 0, "Attack of Opportunity Hit", "Attack of Opportunity Miss")); + if (GetLocalInt(oPC, "PRCCombat_StruckByAttack")) + { + FloatingTextStringOnCreature("You have failed at your Disarm Attempt.", oPC, FALSE); + return FALSE; + } + } + + + SendMessageToPC(oPC, "Disarm Check: "+IntToString(nPCAttack)+" vs "+IntToString(nTargetAttack)); + // Now the outcome + if (nPCAttack >= nTargetAttack) + { + // Disarm em + nSucceed = TRUE; + + // Unarmed + if (!GetIsObjectValid(oPCWep)) + { + object oCopy = CopyObject(oTargetWep, GetLocation(oPC), oPC); + DestroyObject(oTargetWep); + SetDroppableFlag(oCopy, TRUE); + SetStolenFlag(oCopy, FALSE); + } + else + { + AssignCommand(oTarget, ClearAllActions(TRUE)); + SetDroppableFlag(oTargetWep, TRUE); + SetStolenFlag(oTargetWep, FALSE); + //AssignCommand(oTarget, ActionPutDownItem(oTargetWep)); + ForcePutDown(oTarget, oTargetWep, INVENTORY_SLOT_RIGHTHAND); + } + if (GetHasFeat(FEAT_STEAL_AND_STRIKE, oPC)) + { + int nRight = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)); + int nLeft = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC)); + if (nLeft == BASE_ITEM_KUKRI && nRight == BASE_ITEM_RAPIER) + PerformAttack(oPC, oTarget, eNone, 0.0, 0, 0, 0, "Steal and Strike Hit", "Steal and Strike Miss", FALSE, OBJECT_INVALID, GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC), TRUE); + } + } + else if (nCounter) // If you fail, enemy gets a counter attempt + { + FloatingTextStringOnCreature("You have failed on your disarm attempt",oPC, FALSE); + DoDisarm(oTarget, oPC, 0, FALSE, FALSE); + } + else + FloatingTextStringOnCreature("You have failed on your disarm attempt",oPC, FALSE); + + // Keep Attacking + //AssignCommand(oPC, ActionAttack(oTarget)); + // Now we do the rest of the attacks in the round, regardless, remembering to bump down the iteratives + SetLocalInt(oPC, "CombatMoveAttack", TRUE); + DelayCommand(5.5, DeleteLocalInt(oPC, "CombatMoveAttack")); + if (GetBaseAttackBonus(oPC) >= 6) PerformAttackRound(oTarget, oPC, eNone, 0.0, -5); + DelayCommand(0.25, AssignCommand(oPC, ActionAttack(oTarget))); + + // Let people know if we made the hit or not + return nSucceed; +} + +void DoTrample(object oPC, object oTarget, location lTarget) +{ + effect eCharge = SupernaturalEffect(EffectMovementSpeedIncrease(99)); // Just to speed things up a bit + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCharge, oPC, 5.5); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSkillIncrease(SKILL_TUMBLE, 50), oPC, 5.5); + float fLength = GetDistanceBetweenLocations(GetLocation(oPC), lTarget); + vector vOrigin = GetPosition(oPC); + + // Step one is check to see if we're using oTarget or not + if (GetIsObjectValid(oTarget)) + { + lTarget = GetLocation(oTarget); + fLength = GetDistanceBetweenLocations(GetLocation(oPC), lTarget); + } + else + oTarget = MyFirstObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE, vOrigin); // Only change targets if invalid + + // Move the PC to the location + AssignCommand(oPC, ClearAllActions(TRUE)); + AssignCommand(oPC, ActionForceMoveToLocation(lTarget, TRUE)); + + // Loop over targets in the line shape + while(GetIsObjectValid(oTarget)) // Find the targets + { + if(oTarget != oPC && GetIsEnemy(oTarget, oPC)) // Don't overrun friends or yourself + { + float fDelay = PRCGetSpellEffectDelay(GetLocation(oPC), oTarget) * 2.5; + DelayCommand(fDelay, _DoTrampleDamage(oPC, oTarget)); + }// end if - Target validity check + // Get next target + oTarget = MyNextObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE, vOrigin); + }// end while - Target loop +} + +void DoShieldBash(object oPC, object oTarget, int nAtk = 0, int nDamage = 0, int nDamageType = 0, int nCharge = FALSE, int nPounce = FALSE, int nSlam = FALSE, int nInstant = FALSE) +{ + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + int nType = GetBaseItemType(oShield); + effect eNone; + // Can only bash with small or large shields, not tower + if (nType != BASE_ITEM_LARGESHIELD && nType != BASE_ITEM_SMALLSHIELD) + { + FloatingTextStringOnCreature("You do not have a legal shield equipped!", oPC, FALSE); + PerformAttackRound(oTarget, oPC, eNone); + return; + } + + int nTWFeat, nPenOn, nPenOff; + // Check for TWF feats that matter for this + if(GetHasFeat(FEAT_SUPREME_TWO_WEAPON_FIGHTING, oPC)) nTWFeat = 4; + else if(GetHasFeat(FEAT_GREATER_TWO_WEAPON_FIGHTING, oPC)) nTWFeat = 3; + else if(GetHasFeat(FEAT_IMPROVED_TWO_WEAPON_FIGHTING, oPC)) nTWFeat = 2; + else if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oPC)) nTWFeat = 1; + + // Number of attacks with the shield. You always get at least one. + int nAttacks = 1; + if (nTWFeat == 4) nAttacks = 4; + else if (nTWFeat == 3) nAttacks = 3; + else if (nTWFeat == 2) nAttacks = 2; + + // Attack penalty for two weapon fighting. Small shields count as light weapons. Large shields as one-handed weapons. + if(GetHasFeat(FEAT_AGILE_SHIELD_FIGHTER, oPC)) + { + nPenOn = -2; + nPenOff = -2; + } + else if (nTWFeat && (nType == BASE_ITEM_SMALLSHIELD || (nType == BASE_ITEM_LARGESHIELD && GetHasFeat(FEAT_OTWF, oPC)))) + { + nPenOn = -2; + nPenOff = -2; + } + else if (nTWFeat && nType == BASE_ITEM_LARGESHIELD) // TWF with a One-handed weapon + { + nPenOn = -4; + nPenOff = -4; + } + else if (nType == BASE_ITEM_SMALLSHIELD) // Light weapon, no TWF + { + nPenOn = -4; + nPenOff = -8; + } + else // One-handed weapon, no TWF + { + nPenOn = -6; + nPenOff = -10; + } + // Add bashing to attack rolls. _ShieldBashDamage gets it for damage + nAtk += GetLocalInt(oPC, "BashingEnchant"); + + // Add in any passed along bonuses + nPenOn += nAtk; + nPenOff += nAtk; + + // Do the attacks, including main hand + //Pouncing Charge + if (nCharge && nPounce) + { + PerformAttackRound(oTarget, oPC, eNone, 0.0, nPenOn, nDamage, nDamageType, FALSE, "Pouncing Charge Hit", "Pouncing Charge Miss", FALSE, FALSE, TRUE); + int i; + for(i = 1; i <= nAttacks; i++) + { + // Account for iteratives on TWF + int nIter = 5-(5*i); + int nRoll = GetAttackRoll(oTarget, oPC, oShield, 1, 0, nPenOff-nIter); + if (nRoll) + { + _ShieldBashDamage(oPC, oTarget, nRoll, FALSE, nDamage, nDamageType, nType); + + // The only way to get here is Shield Charge, so we know they have this effect + DoTrip(oPC, oTarget, 0, FALSE, FALSE); + } + } + } + else if (nCharge && !nPounce) // Charging without pounce, so only one attack + { + PerformAttack(oTarget, oPC, eNone, 0.0, nPenOn, nDamage, nDamageType, "Charge Hit", "Charge Miss"); + + int nRoll = GetAttackRoll(oTarget, oPC, oShield, 1, 0, nPenOff); + if (nRoll) + { + _ShieldBashDamage(oPC, oTarget, nRoll, FALSE, nDamage, nDamageType, nType); + // The only way to get here is Shield Charge, so we know they have this effect + DoTrip(oPC, oTarget, 0, FALSE, FALSE); + } + } + else if (nSlam) // Shield Slam only + { + int nRoll = GetAttackRoll(oTarget, oPC, oShield, 1, 0, nPenOff); + if (nRoll) + { + // GetAttackRoll doesn't set this, so we set it manually + SetLocalInt(oTarget, "PRCCombat_StruckByAttack", TRUE); + DelayCommand(1.0, DeleteLocalInt(oTarget, "PRCCombat_StruckByAttack")); + _ShieldBashDamage(oPC, oTarget, nRoll, TRUE, nDamage, nDamageType, nType); + // Shield slam stun + int nDC = 10 + GetHitDice(oPC)/2 + GetAbilityModifier(ABILITY_STRENGTH, oPC); + if (!GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) + { + if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE)) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oTarget, 6.0); + } + } + FloatingTextStringOnCreature("Shield Slam Hit!", oPC, FALSE); + // If it's a charge and we're here, we've hit with the attack + if (nCharge) DoTrip(oPC, oTarget, 0, FALSE, FALSE); + } + else + FloatingTextStringOnCreature("Shield Slam Missed!", oPC, FALSE); + } + else if (nInstant) // Shield Counter only, currently + { + int nRoll = GetAttackRoll(oTarget, oPC, oShield, 1, 0, nPenOff); + if (nRoll) + { + // GetAttackRoll doesn't set this, so we set it manually + SetLocalInt(oTarget, "PRCCombat_StruckByAttack", TRUE); + DelayCommand(1.0, DeleteLocalInt(oTarget, "PRCCombat_StruckByAttack")); + _ShieldBashDamage(oPC, oTarget, nRoll, TRUE, nDamage, nDamageType, nType); + // Enemy misses their next attack + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(PSI_IMP_CONCUSSION_BLAST), oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectAttackDecrease(20)), oTarget, 3.0); + FloatingTextStringOnCreature("Shield Counter Hit!", oPC, FALSE); + } + else + FloatingTextStringOnCreature("Shield Counter Missed!", oPC, FALSE); + } + else // No charge, no pounce, so regular combat + { + PerformAttackRound(oTarget, oPC, eNone, 0.0, nPenOn, nDamage, nDamageType); + int i; + for(i = 1; i <= nAttacks; i++) + { + // Account for iteratives on TWF + int nIter = 5-(5*i); + int nRoll = GetAttackRoll(oTarget, oPC, oShield, 1, 0, nPenOff-nIter); + if (nRoll) + { + _ShieldBashDamage(oPC, oTarget, nRoll, FALSE, nDamage, nDamageType, nType); + } + } + } + + // No shield benefit for the next round, unless you have Improved Shield Bash + if(!GetHasFeat(FEAT_IMPROVED_SHIELD_BASH, oPC)) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectACDecrease(GetItemACValue(oShield)), oPC, 6.0); + + // Target is valid and we know it's an enemy and we're in combat + DelayCommand(0.25, AssignCommand(oPC, ActionAttack(oTarget))); +} + +void DoShieldCharge(object oPC, object oTarget, int nSlam = FALSE) +{ + // PnP rules use feet, might as well convert it now. + float fDistance = MetersToFeet(GetDistanceBetweenLocations(GetLocation(oPC), GetLocation(oTarget))); + if(fDistance >= 10.0) + { + // Mark the PC as charging + SetIsCharging(oPC); + + int nPounce = FALSE; + int nDamageType = DAMAGE_TYPE_BASE_WEAPON; + int nDamage = _ChargeDamage(oPC); + int nAttack = _ChargeAttack(oPC, oTarget, nAttack); // This now takes into account charge feats + effect eNone; + + // Move to the target + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionMoveToObject(oTarget, TRUE)); + + // Dread Carapace Totem Bind + if(GetIsIncarnumUser(oPC)) + { + if (GetIsMeldBound(oPC, MELD_DREAD_CARAPACE) == CHAKRA_TOTEM) // CHAKRA_TOTEM + { + location lTargetLocation = GetLocation(oPC); + int nSaveDC = GetMeldshaperDC(oPC, CLASS_TYPE_TOTEMIST, MELD_DREAD_CARAPACE); // MELD_DREAD_CARAPACE + + object oDreadTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(60.0), lTargetLocation, FALSE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oDreadTarget)) + { + if(GetIsEnemy(oPC, oDreadTarget)) + { + if(!PRCMySavingThrow(SAVING_THROW_WILL, oDreadTarget, nSaveDC, SAVING_THROW_TYPE_FEAR)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectShaken(), oDreadTarget, 6.0); + + } + //Select the next target within the spell shape. + oDreadTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(60.0), lTargetLocation, FALSE, OBJECT_TYPE_CREATURE); + } + } + } + + // Flying Kick feat + if(GetHasFeat(FEAT_FLYING_KICK, oPC) && (GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) == OBJECT_INVALID)) + nDamage += d12(); + if(GetHasFeat(FEAT_SNOW_TIGER_BERSERKER, oPC) && GetIsLightWeapon(oPC)) + nPounce = TRUE; + if(GetHasFeat(FEAT_CATFOLK_POUNCE, oPC) && GetIsDeniedDexBonusToAC(oTarget, oPC, TRUE)) + nPounce = TRUE; + if(GetItemPossessedBy(oPC, "WOL_Shishio") == GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) && GetLocalInt(oPC, "Shishio_Pounce")) + nPounce = TRUE; + if(GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oPC) >= 5) + nPounce = TRUE; + if(GetRacialType(oPC) == RACIAL_TYPE_MARRUSAULT) + nPounce = TRUE; + + // Checks for a White Raven Stance + // If it exists, +1 damage/initiator level + if(GetLocalInt(oPC, "LeadingTheCharge")) + nDamage += GetLocalInt(oPC, "LeadingTheCharge"); + if(nDamageType == -1) // If the damage type isn't set + { + object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + nDamageType = GetWeaponDamageType(oWeap); + } + + float fDelay = FeetToMeters(fDistance)/10; + DelayCommand(fDelay, DoShieldBash(oPC, oTarget, nAttack, nDamage, nDamageType, TRUE, nPounce, nSlam)); + DelayCommand(fDelay, _PostCharge(oPC, oTarget)); + } + else + { + FloatingTextStringOnCreature("You are too close to charge " + GetName(oTarget), oPC); + } +} + +//:: Test void +//:: void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_core.nss b/src/include/prc_inc_core.nss new file mode 100644 index 0000000..30af518 --- /dev/null +++ b/src/include/prc_inc_core.nss @@ -0,0 +1,757 @@ +/* Core functions taken from high up the branch + which are needed lower. */ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Function Prototypes */ +////////////////////////////////////////////////// + +//:: Returns true if oCaster's race can naturally cast sorcerer spells. +int GetIsRHDSorcerer(object oCaster = OBJECT_SELF); + +//:: Returns true if oCaster's race can naturally cast bard spells. +int GetIsRHDBard(object oCaster = OBJECT_SELF); + +// wrapper for getspelltargetlocation +location PRCGetSpellTargetLocation(object oCaster = OBJECT_SELF); + +// Avoids adding passive spellcasting to the character's action queue by +// creating an object specifically to cast the spell on the character. +// +// NOTE: The spell script must refer to the PC as PRCGetSpellTargetObject() +// otherwise this function WILL NOT WORK. Do not make any assumptions +// about the PC being OBJECT_SELF. +void ActionCastSpellOnSelf(int iSpell, int nMetaMagic = METAMAGIC_NONE, object oTarget = OBJECT_SELF); + +// This is a wrapper function that causes OBJECT_SELF to fire the defined spell +// at the defined level. The target is automatically the object or location +// that the user selects. Useful for SLA's to perform the casting of a true +// spell. This is useful because: +// +// 1) If the original's spell script is updated, so is this one. +// 2) The spells are identified as the true spell. That is, they ARE the true spell. +// 3) Spellhooks (such as item crafting) that can only identify true spells +// will easily work. +// +// This function should only be used when SLA's are meant to simulate true +// spellcasting abilities, such as those seen when using feats with subradials +// to simulate spellbooks. +void ActionCastSpell(int iSpell, int iCasterLev = 0, int iBaseDC = 0, int iTotalDC = 0, + int nMetaMagic = METAMAGIC_NONE, int nClass = CLASS_TYPE_INVALID, + int bUseOverrideTargetLocation=FALSE, int bUseOverrideTargetObject=FALSE, + object oOverrideTarget=OBJECT_INVALID, int bInstantCast=TRUE, int bUseOverrideMetaMagic=FALSE); + +/** + * Checks whether the given creature is committing an action, or + * under such effects that cause a breach of concentration. + * + * @param oConcentrator The creature to test + * @return TRUE if concentration is broken, FALSE otherwise + */ +int GetBreakConcentrationCheck(object oConcentrator); + +/** + * Checks for breaks in concentration for an ongoing effect, and removes + * the effect if concentration is broken. + * + * @param oCaster The creature who cast the effect + * @param SpellID The id of the spell the effect belongs to + * @param oTarget The creature or object that is the target of the effect + * @param nDuration The duration the effect lasts in seconds. + */ +void CheckConcentrationOnEffect(object oCaster, int SpellID, object oTarget, int nDuration); + +// gets the spell level adjustment to the nMetaMagic, including boni from the Improved Metamagic (epic) feat +int GetMetaMagicSpellLevelAdjustment(int nMetaMagic); + +// Returns true if a spellcaster +int GetIsBioSpellCastClass(int nClass); + +// Returns true for spell casters with spellbooks +int GetIsNSBClass(int nClass); + +// returns the spelllevel of nSpell as it can be cast by oCreature +int PRCGetSpellLevel(object oCreature, int nSpell); + +// returns if a character should be using the newspellbook when casting +int UseNewSpellBook(object oCreature); + +// wrapper for GetHasSpell, works for newspellbook 'fake' spells too +// should return 0 if called with a normal spell when a character should be using the newspellbook +int PRCGetHasSpell(int nRealSpellID, object oCreature = OBJECT_SELF); + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +int PRCGetIsRealSpellKnown(int nRealSpellID, object oPC = OBJECT_SELF); + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +// this will only check the spellbook of the class specified +int PRCGetIsRealSpellKnownByClass(int nRealSpellID, int nClass, object oPC = OBJECT_SELF); + +//routes to action cast spell, but puts a wrapper around to tell other functions its a +//SLA, so dont craft etc +//also defaults the totalDC to 10+spellevel+chamod +// moved from prc_inc_racial +void DoRacialSLA(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0, int bInstantCast = FALSE); + +/** + * Deletes a stored manifestation structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalManifestation(object oObject, string sName); + +/** + * Deletes a stored mystery structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalMystery(object oObject, string sName); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// metamagic spell level adjustments for Bioware provided metamagic feats +const int METAMAGIC_EXTEND_LEVEL = 1; +const int METAMAGIC_SILENT_LEVEL = 1; +const int METAMAGIC_STILL_LEVEL = 1; +const int METAMAGIC_EMPOWER_LEVEL = 2; +const int METAMAGIC_MAXIMIZE_LEVEL = 3; +const int METAMAGIC_QUICKEN_LEVEL = 4; + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "lookup_2da_spell" +#include "inc_lookups" +#include "prc_inc_damage" +#include "prc_inc_sb_const" // Spell Book Constants +#include "x0_i0_position" + +/* + access to prc_inc_nwscript via prc_inc_damage + access to PRCGetSpell* via prc_inc_damage +*/ + + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +//:: Returns true if oCaster's race can naturally cast sorcerer spells. +int GetIsRHDSorcerer(object oCaster = OBJECT_SELF) +{ + int nRace = GetRacialType(oCaster); + + return (nRace == RACIAL_TYPE_ARANEA || + nRace == RACIAL_TYPE_ARKAMOI || + nRace == RACIAL_TYPE_DRIDER || + nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL || + nRace == RACIAL_TYPE_MARRUTACT || + nRace == RACIAL_TYPE_RAKSHASA || + nRace == RACIAL_TYPE_REDSPAWN_ARCANISS); +} + +//:: Returns true if oCaster's race can naturally cast bard spells. +int GetIsRHDBard(object oCaster = OBJECT_SELF) +{ + int nRace = GetRacialType(oCaster); + + return (nRace == RACIAL_TYPE_GLOURA); +} + +//wrapper for GetSpellTargetLocation() +location PRCGetSpellTargetLocation(object oCaster = OBJECT_SELF) +{ + // check if there is an override location on the module, and return that + // bioware did not define a LOCATION_INVALID const, so we must signal a valid override location by setting a local int on the module + if(GetLocalInt(GetModule(), PRC_SPELL_TARGET_LOCATION_OVERRIDE)) + { + if (DEBUG) DoDebug("PRCGetSpellTargetLocation: found override target location on module"); + return GetLocalLocation(GetModule(), PRC_SPELL_TARGET_LOCATION_OVERRIDE); + } + + + // check if there is an override location on the caster, and return that + // bioware did not define a LOCATION_INVALID const, so we signal a valid override location by setting a local int on oCaster + if (GetLocalInt(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE)) + { + if (DEBUG) DoDebug("PRCGetSpellTargetLocation: found override target location on caster "+GetName(oCaster)); + return GetLocalLocation(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE); + } + + + // The rune/gem/skull always targets the one who activates it. + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && (GetResRef(oItem) == "prc_rune_1" || + GetResRef(oItem) == "prc_skulltalis" || GetTag(oItem) == "prc_attunegem")) + return GetLocation(GetItemPossessor(oItem)); + + if (GetLocalInt(oCaster, "BlackLabyrinth") && d10() < 3) + return GenerateNewLocationFromLocation(GetSpellTargetLocation(), FeetToMeters(5.0*d4()), IntToFloat(Random(360)), IntToFloat(Random(360))); + + // if we made it here, we must use Bioware's function + return GetSpellTargetLocation(); +} + +void ActionCastSpellOnSelf(int iSpell, int nMetaMagic = METAMAGIC_NONE, object oTarget = OBJECT_SELF) +{ + if(!GetIsObjectValid(oTarget)) oTarget = OBJECT_SELF; + object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oTarget)); + + AssignCommand(oCastingObject, ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + if (DEBUG) DoDebug("ActionCastSpellOnSelf: Casting Spell "+IntToString(iSpell)+" on "+GetName(oTarget)); + + DestroyObject(oCastingObject, 6.0); +} + +void ActionCastSpell(int iSpell, int iCasterLev = 0, int iBaseDC = 0, int iTotalDC = 0, + int nMetaMagic = METAMAGIC_NONE, int nClass = CLASS_TYPE_INVALID, + int bUseOverrideTargetLocation=FALSE, int bUseOverrideTargetObject=FALSE, + object oOverrideTarget=OBJECT_INVALID, int bInstantCast=TRUE, int bUseOverrideMetaMagic=FALSE) +{ + + //if its a hostile spell, clear the action queue + //this stops people stacking hostile spells to be instacast + //at the end, for example when coming out of invisibility + // X - hope this is not needed if spells are cast normally + //if(Get2DACache("spells", "HostileSetting", iSpell) == "1" && bInstantCast) + // ClearAllActions(); + + object oTarget = PRCGetSpellTargetObject(); + location lLoc = PRCGetSpellTargetLocation(); + + //set the overriding values + if (iCasterLev != 0) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE, iCasterLev)); + if (iTotalDC != 0) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE, iTotalDC)); + if (iBaseDC != 0) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE, iBaseDC)); + if (nClass != CLASS_TYPE_INVALID) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE, nClass)); + if (bUseOverrideMetaMagic) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE, nMetaMagic)); + else if (nMetaMagic != METAMAGIC_NONE) + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_METAMAGIC_ADJUSTMENT, nMetaMagic)); + if (bUseOverrideTargetLocation) + { + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE, TRUE)); + //location must be set outside of this function at the moment + //cant pass a location into a function as an optional parameter + //go bioware for not defining an invalid location constant + } + if (bUseOverrideTargetObject) + { + ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE, TRUE)); + ActionDoCommand(SetLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE, oOverrideTarget)); + } + ActionDoCommand(SetLocalInt(OBJECT_SELF, "UsingActionCastSpell", TRUE)); + + if(DEBUG) DoDebug("ActionCastSpell SpellId: " + IntToString(iSpell)); + if(DEBUG) DoDebug("ActionCastSpell Caster Level: " + IntToString(iCasterLev)); + if(DEBUG) DoDebug("ActionCastSpell Base DC: " + IntToString(iBaseDC)); + if(DEBUG) DoDebug("ActionCastSpell Total DC: " + IntToString(iTotalDC)); + if(DEBUG) DoDebug("ActionCastSpell Metamagic: " + IntToString(nMetaMagic)); + if(DEBUG) DoDebug("ActionCastSpell Caster Class: " + IntToString(nClass)); + if(DEBUG) DoDebug("ActionCastSpell Target: " + GetName(oTarget)); + if(DEBUG) DoDebug("ActionCastSpell Override Target: " + GetName(oOverrideTarget)); + + //cast the spell + if (GetIsObjectValid(oOverrideTarget)) + ActionCastSpellAtObject(iSpell, oOverrideTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast); + else if (GetIsObjectValid(oTarget)) + ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast); + else + ActionCastSpellAtLocation(iSpell, lLoc, nMetaMagic, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast); + + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "UsingActionCastSpell")); + + //clean up afterwards + if(bInstantCast)//give scripts time to read the variables + { + if (iCasterLev != 0) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE))); + if (iTotalDC != 0) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE))); + if (iBaseDC != 0) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE))); + if (nClass != CLASS_TYPE_INVALID) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE))); + if (nMetaMagic != METAMAGIC_NONE) + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE))); + if (bUseOverrideTargetLocation) + { + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE))); + //location must be set outside of this function at the moment + //cant pass a location into a function as an optional parameter + //go bioware for not defining an invalid location constant + } + if (bUseOverrideTargetObject) + { + ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE))); + ActionDoCommand(DelayCommand(1.0, DeleteLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE))); + } + } + else + { + if (iCasterLev != 0) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE)); + if (iTotalDC != 0) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE)); + if (iBaseDC != 0) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE)); + if (nClass != CLASS_TYPE_INVALID) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE)); + if (bUseOverrideMetaMagic) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE)); + else if (nMetaMagic != METAMAGIC_NONE) + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_ADJUSTMENT)); + if (bUseOverrideTargetLocation) + { + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE)); + //location must be set outside of this function at the moment + //cant pass a location into a function as an optional parameter + //go bioware for not defining an invalid location constant + } + if (bUseOverrideTargetObject) + { + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE)); + ActionDoCommand(DeleteLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE)); + } + } + + +/* +//The problem with this approace is that the effects are then applies by the original spell, which could go wrong. What to do? + SetLocalInt(OBJECT_SELF, PRC_SPELLID_OVERRIDE, GetSpellId()); + DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELLID_OVERRIDE)); + string sScript = Get2DACache("spells", "ImpactScript", iSpell); + ExecuteScript(sScript, OBJECT_SELF); +*/ +} + +int GetBreakConcentrationCheck(object oConcentrator) +{ + if (GetHasSpellEffect(VESTIGE_DAHLVERNAR, oConcentrator) && !GetLocalInt(oConcentrator, "PactQuality"+IntToString(VESTIGE_DAHLVERNAR))) return TRUE; + + int nAction = GetCurrentAction(oConcentrator); + // creature 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) + { + return TRUE; + } + //suffering a mental effect + effect e1 = GetFirstEffect(oConcentrator); + int nType; + while (GetIsEffectValid(e1)) + { + nType = GetEffectType(e1); + 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; + } + e1 = GetNextEffect(oConcentrator); + } + // add to on damage event + AddEventScript(oConcentrator, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc", FALSE, FALSE); + if(GetLocalInt(oConcentrator, "CONC_BROKEN")) // won't be set first time around regardless + { + DeleteLocalInt(oConcentrator, "CONC_BROKEN"); // reset for next spell + return TRUE; + } + return FALSE; +} + +void CheckConcentrationOnEffect(object oCaster, int SpellID, object oTarget, int nDuration) +{ + int nDur = GetLocalInt(oCaster, "Conc" + IntToString(SpellID)); + if(GetBreakConcentrationCheck(oCaster) == TRUE && nDur < nDuration) + { + FloatingTextStringOnCreature("*Concentration Broken*", oCaster); + DeleteLocalInt(oCaster, "Conc" + IntToString(SpellID)); + PRCRemoveSpellEffects(SpellID, oCaster, oTarget); + } + else if(nDur < nDuration) + { + SetLocalInt(oCaster, "Conc" + IntToString(SpellID), nDur + 3); + DelayCommand(3.0, CheckConcentrationOnEffect(oCaster, SpellID, oTarget, nDuration)); + } + else + { + DeleteLocalInt(oCaster, "Conc" + IntToString(SpellID)); + } +} + +int PRCGetSpellLevelForClass(int nSpell, int nClass) +{ + string sSpellLevel = ""; + if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER) + sSpellLevel = Get2DACache("spells", "Wiz_Sorc", nSpell); + else if (nClass == CLASS_TYPE_RANGER) + sSpellLevel = Get2DACache("spells", "Ranger", nSpell); + else if (nClass == CLASS_TYPE_PALADIN) + sSpellLevel = Get2DACache("spells", "Paladin", nSpell); + else if (nClass == CLASS_TYPE_DRUID) + sSpellLevel = Get2DACache("spells", "Druid", nSpell); + else if (nClass == CLASS_TYPE_CLERIC || nClass == CLASS_TYPE_UR_PRIEST || nClass == CLASS_TYPE_OCULAR) + sSpellLevel = Get2DACache("spells", "Cleric", nSpell); + else if (nClass == CLASS_TYPE_BARD) + sSpellLevel = Get2DACache("spells", "Bard", nSpell); + else if (nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK) + sSpellLevel = Get2DACache("spells", "Cultist", nSpell); + else if (nClass == CLASS_TYPE_NENTYAR_HUNTER) + sSpellLevel = Get2DACache("spells", "Nentyar", nSpell); + else if (nClass == CLASS_TYPE_SHADOWLORD) + sSpellLevel = Get2DACache("spells", "Telflammar", nSpell); + else if (nClass == CLASS_TYPE_SLAYER_OF_DOMIEL) + sSpellLevel = Get2DACache("spells", "Domiel", nSpell); + else if (nClass == CLASS_TYPE_SOHEI) + sSpellLevel = Get2DACache("spells", "Sohei", nSpell); + else if (nClass == CLASS_TYPE_VASSAL) + sSpellLevel = Get2DACache("spells", "Bahamut", nSpell); + else if (nClass == CLASS_TYPE_BLACKGUARD) + sSpellLevel = Get2DACache("spells", "Blackguard", nSpell); + else if (nClass == CLASS_TYPE_KNIGHT_CHALICE) + sSpellLevel = Get2DACache("spells", "Chalice", nSpell); + else if (nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE) + sSpellLevel = Get2DACache("spells", "MiddleCircle", nSpell); + else if (nClass == CLASS_TYPE_SOLDIER_OF_LIGHT) + sSpellLevel = Get2DACache("spells", "SoLight", nSpell); + else if (nClass == CLASS_TYPE_BLIGHTER) + sSpellLevel = Get2DACache("spells", "Blighter", nSpell); + else if (nClass == CLASS_TYPE_HEALER) + sSpellLevel = Get2DACache("spells", "Healer", nSpell); + else if (nClass == CLASS_TYPE_SHAMAN) + sSpellLevel = Get2DACache("spells", "Shaman", nSpell); + else if (nClass == CLASS_TYPE_INVALID) + sSpellLevel = Get2DACache("spells", "Innate", nSpell); + + if (sSpellLevel != "") + return StringToInt(sSpellLevel); + + // 2009-9-21: Support real spell ID's. -N-S + // PRCGetSpellLevel() is called several times in the Bioware spellhooking script. + // That means it will always pass a "real" spell ID to this function, but new-spellbook users won't have the real spell! + // GetSpellLevel() takes the fake spell ID, so this function was always failing. + //int nSpellLevel = GetSpellLevel(nSpell, nClass); + int nSpellLevel = -1; + int nSpellbookID = RealSpellToSpellbookID(nClass, nSpell); + if (nSpellbookID == -1) + nSpellLevel = GetSpellLevel(nSpell, nClass); + else + { + string sFile = GetFileForClass(nClass); + string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookID); + if (sSpellLevel != "") + nSpellLevel = StringToInt(sSpellLevel); + } + + return nSpellLevel; +} + +// returns the spelllevel of nSpell as it can be cast by oCreature +int PRCGetSpellLevel(object oCreature, int nSpell) +{ + /*if (!PRCGetHasSpell(nSpell, oCreature)) + return -1;*/ + + int nClass = PRCGetLastSpellCastClass(); + int nSpellLevel = PRCGetSpellLevelForClass(nSpell, nClass); + if (nSpellLevel != -1) + return nSpellLevel; + + int i; + for (i=1;i<=8;i++) + { + nClass = GetClassByPosition(i, oCreature); + int nCharLevel = GetLevelByClass(nClass, oCreature); + if (nCharLevel) + { + nSpellLevel = PRCGetSpellLevelForClass(nSpell, nClass); + if (nSpellLevel != -1) + return nSpellLevel; + } + } + + //return innate level + return StringToInt(Get2DACache("spells", "Innate", nSpell)); +} + +// gets the spell level adjustment to the nMetaMagic, including boni from the Improved Metamagic (epic) feat +int GetMetaMagicSpellLevelAdjustment(int nMetaMagic) +{ + int nAdj; + if (nMetaMagic == 0) return nAdj; + + if (nMetaMagic & METAMAGIC_EXTEND) nAdj += METAMAGIC_EXTEND_LEVEL; + if (nMetaMagic & METAMAGIC_SILENT) nAdj += METAMAGIC_SILENT_LEVEL; + if (nMetaMagic & METAMAGIC_STILL) nAdj += METAMAGIC_STILL_LEVEL; + if (nMetaMagic & METAMAGIC_EMPOWER) nAdj += METAMAGIC_EMPOWER_LEVEL; + if (nMetaMagic & METAMAGIC_MAXIMIZE) nAdj += METAMAGIC_MAXIMIZE_LEVEL; + if (nMetaMagic & METAMAGIC_QUICKEN) nAdj += METAMAGIC_QUICKEN_LEVEL; + + return nAdj; +} + +int GetIsBioSpellCastClass(int nClass) +{ + return nClass == CLASS_TYPE_WIZARD + || nClass == CLASS_TYPE_SORCERER && !GetIsRHDSorcerer() + || nClass == CLASS_TYPE_BARD && !GetIsRHDBard() + || nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_HEALER + || nClass == CLASS_TYPE_BLIGHTER + || nClass == CLASS_TYPE_BLACKGUARD + || nClass == CLASS_TYPE_UR_PRIEST + || nClass == CLASS_TYPE_OCULAR + || nClass == CLASS_TYPE_SLAYER_OF_DOMIEL + || nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK + || nClass == CLASS_TYPE_NENTYAR_HUNTER + || nClass == CLASS_TYPE_SHADOWLORD + || nClass == CLASS_TYPE_SOHEI + || nClass == CLASS_TYPE_SOLDIER_OF_LIGHT + || nClass == CLASS_TYPE_VASSAL + || nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE + || nClass == CLASS_TYPE_KNIGHT_CHALICE + || nClass == CLASS_TYPE_SHAMAN + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_RANGER; +} + +int GetIsNSBClass(int nClass) +{ + return !GetIsBioSpellCastClass(nClass) + && GetSpellbookTypeForClass(nClass) != SPELLBOOK_TYPE_INVALID; +} + +// returns if a character should be using the newspellbook when casting +int UseNewSpellBook(object oCreature) +{ + int i; + for (i = 1; i <= 8; i++) + { + int nClass = GetClassByPosition(i, oCreature); + if(GetIsNSBClass(nClass)) + return TRUE; + } + + // Special case + if(GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCreature)) + return TRUE; + + int nPrimaryArcane = GetPrimaryArcaneClass(oCreature); + + //check they have bard/sorc in first arcane slot + if(nPrimaryArcane != CLASS_TYPE_BARD && nPrimaryArcane != CLASS_TYPE_SORCERER) + return FALSE; + //check they have arcane PrC or Draconic Breath/Arcane Grace + if(!GetArcanePRCLevels(oCreature, nPrimaryArcane) + && !(GetHasFeat(FEAT_DRACONIC_GRACE, oCreature) || GetHasFeat(FEAT_DRACONIC_BREATH, oCreature))) + return FALSE; + //check if the newspellbooks are disabled + if((GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK) && nPrimaryArcane == CLASS_TYPE_SORCERER) || + (GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK) && nPrimaryArcane == CLASS_TYPE_BARD)) + return FALSE; + //check they have bard/sorc levels + if(!GetLevelByClass(CLASS_TYPE_BARD) && !GetLevelByClass(CLASS_TYPE_SORCERER)) + return FALSE; + + //at this point, they should be using the new spellbook + return TRUE; +} + +// wrapper for GetHasSpell, works for newspellbook 'fake' spells too (and metamagic) +// should return 0 if called with a normal spell when a character should be using the newspellbook +int PRCGetHasSpell(int nRealSpellID, object oCreature = OBJECT_SELF) +{ + if(!PRCGetIsRealSpellKnown(nRealSpellID, oCreature)) + return 0; + int nUses = GetHasSpell(nRealSpellID, oCreature); + + int nClass, nSpellbookID, nCount, nMeta, i, j; + int nSpellbookType, nSpellLevel; + string sFile, sFeat; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oCreature); + sFile = GetFileForClass(nClass); + nSpellbookType = GetSpellbookTypeForClass(nClass); + nSpellbookID = RealSpellToSpellbookID(nClass, nRealSpellID); + nMeta = RealSpellToSpellbookIDCount(nClass, nRealSpellID); + if (nSpellbookID != -1) + { //non-spellbook classes should return -1 + for(j = nSpellbookID; j <= nSpellbookID + nMeta; j++) + { + sFeat = Get2DACache(sFile, "ReqFeat", j); + if(sFeat != "") + { + if(!GetHasFeat(StringToInt(sFeat), oCreature)) + continue; + } + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j); + if(DEBUG) DoDebug("PRCGetHasSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j)); + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(DEBUG) DoDebug("PRCGetHasSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + } + } + } + + if(DEBUG) DoDebug("PRCGetHasSpell: RealSpellID = " + IntToString(nRealSpellID) + ", Uses = " + IntToString(nUses)); + return nUses; +} + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +int PRCGetIsRealSpellKnown(int nRealSpellID, object oPC = OBJECT_SELF) +{ + if(GetHasSpell(nRealSpellID, oPC)) //FUGLY HACK: bioware class having uses of the spell + return TRUE; // means they know the spell (close enough) + int nClass; + int nClassSlot = 1; + while(nClassSlot <= 8) + { + nClass = GetClassByPosition(nClassSlot, oPC); + if(GetIsDivineClass(nClass) || GetIsArcaneClass(nClass)) + if(PRCGetIsRealSpellKnownByClass(nRealSpellID, nClass, oPC)) + return TRUE; + nClassSlot++; + } + // got here means no match + return FALSE; +} + +// checks if oPC knows the specified spell +// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters +// this will only check the spellbook of the class specified +int PRCGetIsRealSpellKnownByClass(int nRealSpellID, int nClass, object oPC = OBJECT_SELF) +{ + // check for whether bard and sorc are using the prc spellbooks + if (nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_SORCERER) + { + if(!UseNewSpellBook(oPC)) + return FALSE; + } + + // get the cls_spell_***.2da index for the real spell + int nSpellbookSpell = RealSpellToSpellbookID(nClass, nRealSpellID); + // if the spell does not exist in the spellbook, return FALSE + if (nSpellbookSpell == -1) + return FALSE; + // next check if the PC is high enough level to know the spell + string sFile = GetFileForClass(nClass); + int nSpellLevel = -1; + string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookSpell); + if (sSpellLevel != "") + nSpellLevel = StringToInt(sSpellLevel); + if ((GetLevelByClass(nClass) < nSpellLevel) || nSpellLevel == -1) + return FALSE; // not high enough level + // at this stage, prepared casters know the spell and only spontaneous classes need checking + // there are exceptions and these need hardcoding: + + if((GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED) && nClass != CLASS_TYPE_ARCHIVIST) + return TRUE; + + // spontaneous casters have all their known spells as hide feats + // get the featID of the spell + int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookSpell)); + if (GetHasFeat(nFeatID, oPC)) + return TRUE; + + return FALSE; +} + +//routes to action cast spell, but puts a wrapper around to tell other functions its a +//SLA, so dont craft etc +//also defaults th totalDC to 10+spellevel+chamod +//this is Base DC, not total DC. SLAs are still spells, so spell focus should still apply. +void DoRacialSLA(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0, int bInstantCast = FALSE) +{ + if(DEBUG) DoDebug("Spell DC passed to DoRacialSLA: " + IntToString(nTotalDC)); + if(nTotalDC == 0) + nTotalDC = 10 + +StringToInt(Get2DACache("spells", "Innate", nSpellID)) + +GetAbilityModifier(ABILITY_CHARISMA); + + ActionDoCommand(SetLocalInt(OBJECT_SELF, "SpellIsSLA", TRUE)); + if(DEBUG) DoDebug("Spell DC entered in ActionCastSpell: " + IntToString(nTotalDC)); + ActionCastSpell(nSpellID, nCasterlevel, 0, nTotalDC, METAMAGIC_NONE, CLASS_TYPE_INVALID, FALSE, FALSE, OBJECT_INVALID, bInstantCast); + //ActionCastSpell(nSpellID, nCasterlevel, 0, nTotalDC); + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "SpellIsSLA")); +} + +void DeleteLocalManifestation(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oManifester"); + + DeleteLocalInt(oObject, sName + "_bCanManifest"); + DeleteLocalInt(oObject, sName + "_nPPCost"); + DeleteLocalInt(oObject, sName + "_nPsiFocUsesRemain"); + DeleteLocalInt(oObject, sName + "_nManifesterLevel"); + DeleteLocalInt(oObject, sName + "_nSpellID"); + + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_1"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_2"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_3"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_4"); + DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_5"); + DeleteLocalInt(oObject, sName + "_nTimesGenericAugUsed"); + + DeleteLocalInt(oObject, sName + "_bChain"); + DeleteLocalInt(oObject, sName + "_bEmpower"); + DeleteLocalInt(oObject, sName + "_bExtend"); + DeleteLocalInt(oObject, sName + "_bMaximize"); + DeleteLocalInt(oObject, sName + "_bSplit"); + DeleteLocalInt(oObject, sName + "_bTwin"); + DeleteLocalInt(oObject, sName + "_bWiden"); + DeleteLocalInt(oObject, sName + "_bQuicken"); +} + +void DeleteLocalMystery(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oShadow"); + + DeleteLocalInt(oObject, sName + "_bCanMyst"); + DeleteLocalInt(oObject, sName + "_nShadowcasterLevel"); + DeleteLocalInt(oObject, sName + "_nMystId"); + DeleteLocalInt(oObject, sName + "_nPen"); + DeleteLocalInt(oObject, sName + "_bIgnoreSR"); + + DeleteLocalInt(oObject, sName + "_bEmpower"); + DeleteLocalInt(oObject, sName + "_bExtend"); + DeleteLocalInt(oObject, sName + "_bMaximize"); + DeleteLocalInt(oObject, sName + "_bQuicken"); + + DeleteLocalInt(oObject, sName + "_nSaveDC"); + DeleteLocalFloat(oObject, sName + "_fDur"); +} + +//::void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_damage.nss b/src/include/prc_inc_damage.nss new file mode 100644 index 0000000..869514c --- /dev/null +++ b/src/include/prc_inc_damage.nss @@ -0,0 +1,397 @@ +//:://///////////////////////////////////////////// +//:: Ability Damage application +//:: prc_inc_damage +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Internal constants */ +////////////////////////////////////////////////// + +const string VIRTUAL_ABILITY_SCORE = "PRC_Virtual_Ability_Score_"; +const string UbHealable_ABILITY_DAMAGE = "PRC_UbHealableAbilityDamage_"; +const string ABILITY_DAMAGE_SPECIALS = "PRC_Ability_Damage_Special_Effects_Flags"; +const string ABILITY_DAMAGE_MONITOR = "PRC_Ability_Monitor"; +const int ABILITY_DAMAGE_EFFECT_PARALYZE = 1; +const int ABILITY_DAMAGE_EFFECT_KNOCKDOWN = 2; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gets the amount of ubHealable ability damage suffered by the creature to given ability + * + * @param oTarget The creature whose ubHealable ability damage to examine + * @param nAbility One of the ABILITY_* constants + */ +int GetUnHealableAbilityDamage(object oTarget, int nAbility); + +/** + * Removes the specified amount of normally unHealable ability damage from the target + * + * @param oTarget The creature to restore + * @param nAbility Ability to restore, one of the ABILITY_* constants + * @param nAmount Amount to restore the ability by, should be > 0 for the function + * to have any effect + */ +void RecoverUnHealableAbilityDamage(object oTarget, int nAbility, int nAmount); + +/** + * Applies the ability damage to the given target. Handles the virtual loss of + * ability scores below 3 and the effects of reaching 0 and making the damage + * ubHealable by standard means if requested. + * + * + * @param oTarget The creature about to take ability damage + * @param nAbility One of the ABILITY_* constants + * @param nAmount How much to reduce the ability score by + * @param nDurationType One of the DURATION_TYPE_* contants + * @param bHealable Whether the damage is healable by normal means or not. + * Implemented by applying the damage as an iprop on the hide + * + * The following are passed to SPApplyEffectToObject: + * @param fDuration If temporary, the duration. If this is -1.0, the damage + * will be applied so that it wears off at the rate of 1 point + * per ingame day. + * @param bDispellable Is the effect dispellable? If FALSE, the system will delay + * the application of the effect a short moment (10ms) to break + * spellID association. This will make effects from the same + * source stack with themselves. + * @param oSource Object causing the ability damage + */ +void ApplyAbilityDamage(object oTarget, int nAbility, int nAmount, int nDurationType, int bHealable = TRUE, + float fDuration = 0.0f, int bDispellable = FALSE, object oSource = OBJECT_SELF); + +// Similar funcionality to ApplyAbilityDamage() but used only for alcohol effects +// If you add new Alcohol effects or modify ApplyAbilityDamage() function, you +// should update this function as well. +void ApplyAlcoholEffect(object oTarget, int nAmount, float fDuration); + +/** + * Sets the values of ability decrease on target's hide to be the same as the value + * tracked on the target object itself. This is called with delay from ScrubPCSkin() + * in order to synchronise the tracked value of ubHealable damage with that actually + * present on the hide. + * Please call this if you do similar operations on the hide. + * + * @param oTarget The creature whose hide and tracked value to synchronise. + */ +void ReApplyUnhealableAbilityDamage(object oTarget); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "prc_inc_racial" +#include "prc_effect_inc" +#include "inc_item_props" + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + + +void ApplyAbilityDamage(object oTarget, int nAbility, int nAmount, int nDurationType, int bHealable = TRUE, + float fDuration = 0.0f, int bDispellable = FALSE, object oSource = OBJECT_SELF) +{ + // Immunity check + if(GetIsImmune(oTarget, IMMUNITY_TYPE_ABILITY_DECREASE, oSource)) + return; + + if (GetLocalInt(oTarget, "IncarnumDefenseCE") && nAbility == ABILITY_STRENGTH) + return; + + if (GetIsMeldBound(oTarget, MELD_VITALITY_BELT) == CHAKRA_WAIST && nAbility == ABILITY_CONSTITUTION) + return; + + if (GetHasSpellEffect(VESTIGE_DAHLVERNAR, oTarget) && nAbility == ABILITY_WISDOM && GetLocalInt(oTarget, "ExploitVestige") != VESTIGE_DAHLVERNAR_MAD_SOUL) + return; + + // Strongheart Vest reduces by Essentia amount + 1. If it's bound, reduces ability drain as well + if (GetHasSpellEffect(MELD_STRONGHEART_VEST, oTarget) && bHealable) + { + int nEssentia = GetEssentiaInvested(oTarget, MELD_STRONGHEART_VEST); + nAmount = nAmount - (nEssentia + 1); + // If there's no damage, jump out. + if (0 >= nAmount) return; + } + else if (GetIsMeldBound(oTarget, MELD_STRONGHEART_VEST) == CHAKRA_WAIST && !bHealable) + { + int nEssentia = GetEssentiaInvested(oTarget, MELD_STRONGHEART_VEST); + nAmount = nAmount - (nEssentia + 1); + // If there's no damage, jump out. + if (0 >= nAmount) return; + } + + // Get the value of the stat before anything is done + int nStartingValue = GetAbilityScore(oTarget, nAbility); + + // First, apply the whole damage as an effect + //SendMessageToPC(GetFirstPC(), "Applying " + IntToString(nAmount) + " damage to stat " + IntToString(nAbility)); + if(bHealable) + { + // Is the damage temporary and specified to heal at the PnP rate + if(nDurationType == DURATION_TYPE_TEMPORARY && fDuration == -1.0f) + { + int i; + for(i = 1; i <= nAmount; i++) + DelayCommand(0.01f, ApplyEffectToObject(nDurationType, bDispellable ? TagEffect(EffectAbilityDecrease(nAbility, 1), IntToString(nAbility)+IntToString(1)) : TagEffect(SupernaturalEffect(EffectAbilityDecrease(nAbility, 1)), IntToString(nAbility)+IntToString(1)), oTarget, HoursToSeconds(24) * i)); + } + else if(!bDispellable) + { + DelayCommand(0.01f, ApplyEffectToObject(nDurationType, TagEffect(SupernaturalEffect(EffectAbilityDecrease(nAbility, nAmount)), IntToString(nAbility)+IntToString(nAmount)), oTarget, fDuration)); + } + else + { + ApplyEffectToObject(nDurationType, TagEffect(EffectAbilityDecrease(nAbility, nAmount), IntToString(nAbility)+IntToString(nAmount)), oTarget, fDuration); + } + } + // Non-healable damage + else + { + int nIPType; + int nTotalAmount; + string sVarName = "PRC_UbHealableAbilityDamage_"; + switch(nAbility) + { + case ABILITY_STRENGTH: nIPType = IP_CONST_ABILITY_STR; sVarName += "STR"; break; + case ABILITY_DEXTERITY: nIPType = IP_CONST_ABILITY_DEX; sVarName += "DEX"; break; + case ABILITY_CONSTITUTION: nIPType = IP_CONST_ABILITY_CON; sVarName += "CON"; break; + case ABILITY_INTELLIGENCE: nIPType = IP_CONST_ABILITY_INT; sVarName += "INT"; break; + case ABILITY_WISDOM: nIPType = IP_CONST_ABILITY_WIS; sVarName += "WIS"; break; + case ABILITY_CHARISMA: nIPType = IP_CONST_ABILITY_CHA; sVarName += "CHA"; break; + + default: + WriteTimestampedLogEntry("Unknown nAbility passed to ApplyAbilityDamage: " + IntToString(nAbility)); + return; + } + + // Sum the damage being added with damage that was present previously + nTotalAmount = GetLocalInt(oTarget, sVarName) + nAmount; + + // Apply the damage + SetCompositeBonus(GetPCSkin(oTarget), sVarName, nTotalAmount, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, nIPType); + + // Also store the amount of damage on the PC itself so it can be restored at a later date. + SetLocalInt(oTarget, sVarName, nTotalAmount); + + // Schedule recovering if the damage is temporary + if(nDurationType == DURATION_TYPE_TEMPORARY) + { + // If the damage is specified to heal at the PnP rate, schedule one point to heal per day + if(fDuration == -1.0f) + { + int i; + for(i = 1; i <= nAmount; i++) + DelayCommand(HoursToSeconds(24) * i, RecoverUnHealableAbilityDamage(oTarget, nAbility, 1)); + } + // Schedule everything to heal at once + else + DelayCommand(fDuration, RecoverUnHealableAbilityDamage(oTarget, nAbility, nAmount)); + } + } + + // The system is off by default + if(!GetPRCSwitch(PRC_PNP_ABILITY_DAMAGE_EFFECTS)) + return; + + // If the target is at the minimum supported by NWN, check if they have had their ability score reduced below already + if(nStartingValue == 3) + nStartingValue = GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(nAbility)) ? + GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(nAbility)) - 1 : + nStartingValue; + + // See if any of the damage goes into the virtual area of score < 3 + if(nStartingValue - nAmount < 3) + { + int nVirtual = nStartingValue - nAmount; + if(nVirtual < 0) nVirtual = 0; + + // Mark the virtual value + SetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(nAbility), nVirtual + 1); + + // Cause effects for being at 0 + if(nVirtual == 0) + { + // Apply the effects + switch(nAbility) + { + // Lying down + case ABILITY_STRENGTH: + case ABILITY_INTELLIGENCE: + case ABILITY_WISDOM: + case ABILITY_CHARISMA: + // Do not apply duplicate effects + /*if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_PARALYZE)) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oTarget);*/ + if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_KNOCKDOWN)) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 9999.0f); + SetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS, GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) | ABILITY_DAMAGE_EFFECT_KNOCKDOWN); + } + //break; + + // Paralysis + case ABILITY_DEXTERITY: + // Do not apply duplicate effects + if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_PARALYZE)) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oTarget); + SetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS, GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) | ABILITY_DAMAGE_EFFECT_PARALYZE); + } + break; + + // Death + case ABILITY_CONSTITUTION: + // Non-constitution score critters avoid this one + if(!(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || + MyPRCGetRacialType(oTarget) == RACIAL_TYPE_CONSTRUCT + ) ) + { + DeathlessFrenzyCheck(oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDeath()), oTarget); + + } + break; + + default: + WriteTimestampedLogEntry("Unknown nAbility passed to ApplyAbilityDamage: " + IntToString(nAbility)); + return; + } + + // Start the monitor HB if it is not active yet + if(GetThreadState(ABILITY_DAMAGE_MONITOR, oTarget) == THREAD_STATE_DEAD) + SpawnNewThread(ABILITY_DAMAGE_MONITOR, "prc_abil_monitor", 1.0f, oTarget); + + // Note the ability score for monitoring + SetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR, GetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR) | (1 << nAbility)); + } + } +} + +void ApplyAlcoholEffect(object oTarget, int nAmount, float fDuration) +{ + // Immunity check + if(GetIsImmune(oTarget, IMMUNITY_TYPE_ABILITY_DECREASE)) + return; + + // Get the value of the stat before anything is done + int nStartingValue = GetAbilityScore(oTarget, ABILITY_INTELLIGENCE); + + // First, apply the whole damage as an effect + DelayCommand(0.01f, AssignCommand(GetPCSkin(oTarget), ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nAmount)), oTarget, fDuration))); + + // The system is off by default + if(!GetPRCSwitch(PRC_PNP_ABILITY_DAMAGE_EFFECTS)) + return; + + // If the target is at the minimum supported by NWN, check if they have had their ability score reduced below already + if(nStartingValue == 3) + nStartingValue = GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE)) ? + GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE)) - 1 : + nStartingValue; + + // See if any of the damage goes into the virtual area of score < 3 + if(nStartingValue - nAmount < 3) + { + int nVirtual = nStartingValue - nAmount; + if(nVirtual < 0) nVirtual = 0; + + // Mark the virtual value + SetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE), nVirtual + 1); + + // Cause effects for being at 0 + if(!nVirtual) + { + // Do not apply duplicate effects + /*if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_PARALYZE)) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oTarget);*/ + if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_KNOCKDOWN)) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 9999.0f); + SetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS, GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) | ABILITY_DAMAGE_EFFECT_KNOCKDOWN); + } + + // Start the monitor HB if it is not active yet + if(GetThreadState(ABILITY_DAMAGE_MONITOR, oTarget) == THREAD_STATE_DEAD) + SpawnNewThread(ABILITY_DAMAGE_MONITOR, "prc_abil_monitor", 1.0f, oTarget); + + // Note the ability score for monitoring + SetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR, GetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR) | (1 << ABILITY_INTELLIGENCE)); + } + } +} + +void ReApplyUnhealableAbilityDamage(object oTarget) +{ + object oSkin = GetPCSkin(oTarget); + SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_STR", + GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_STR"), + ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_STR); + SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_DEX", + GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_DEX"), + ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_DEX); + SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_CON", + GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_CON"), + ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_CON); + SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_INT", + GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_INT"), + ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_INT); + SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_WIS", + GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_WIS"), + ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_WIS); + SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_CHA", + GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_CHA"), + ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_CHA); +} + +int GetUnHealableAbilityDamage(object oTarget, int nAbility) +{ + int nIPType; + string sVarName = "PRC_UbHealableAbilityDamage_"; + switch(nAbility) + { + case ABILITY_STRENGTH: sVarName += "STR"; break; + case ABILITY_DEXTERITY: sVarName += "DEX"; break; + case ABILITY_CONSTITUTION: sVarName += "CON"; break; + case ABILITY_INTELLIGENCE: sVarName += "INT"; break; + case ABILITY_WISDOM: sVarName += "WIS"; break; + case ABILITY_CHARISMA: sVarName += "CHA"; break; + + default: + WriteTimestampedLogEntry("Unknown nAbility passed to GetUnHealableAbilityDamage: " + IntToString(nAbility)); + return FALSE; + } + + return GetLocalInt(oTarget, sVarName); +} + + +void RecoverUnHealableAbilityDamage(object oTarget, int nAbility, int nAmount) +{ + // Sanity check, one should not be able to cause more damage via this function, ApplyAbilityDamage() is for that. + if(nAmount < 0) return; + + int nIPType, nNewVal; + string sVarName = "PRC_UbHealableAbilityDamage_"; + switch(nAbility) + { + case ABILITY_STRENGTH: nIPType = IP_CONST_ABILITY_STR; sVarName += "STR"; break; + case ABILITY_DEXTERITY: nIPType = IP_CONST_ABILITY_DEX; sVarName += "DEX"; break; + case ABILITY_CONSTITUTION: nIPType = IP_CONST_ABILITY_CON; sVarName += "CON"; break; + case ABILITY_INTELLIGENCE: nIPType = IP_CONST_ABILITY_INT; sVarName += "INT"; break; + case ABILITY_WISDOM: nIPType = IP_CONST_ABILITY_WIS; sVarName += "WIS"; break; + case ABILITY_CHARISMA: nIPType = IP_CONST_ABILITY_CHA; sVarName += "CHA"; break; + + default: + WriteTimestampedLogEntry("Unknown nAbility passed to ApplyAbilityDamage: " + IntToString(nAbility)); + return; + } + + nNewVal = GetLocalInt(oTarget, sVarName) - nAmount; + if(nNewVal < 0) nNewVal = 0; + + SetCompositeBonus(GetPCSkin(oTarget), sVarName, nNewVal, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, nIPType); + SetLocalInt(oTarget, sVarName, nNewVal); +} diff --git a/src/include/prc_inc_descrptr.nss b/src/include/prc_inc_descrptr.nss new file mode 100644 index 0000000..bbee255 --- /dev/null +++ b/src/include/prc_inc_descrptr.nss @@ -0,0 +1,260 @@ +//:://///////////////////////////////////////////// +//:: Magic descriptors and subschools include +//:: prc_inc_descrptr +//:://///////////////////////////////////////////// +/** @file prc_inc_descrptr + A set of constants and functions for managing + spell's / power's / other stuffs's descriptors + and sub{school|discipline|whatever}s. + + The functions SetDescriptor() and SetSubschool() + should be called at the beginning of the + spellscript, before spellhook or equivalent. + + The values are stored on the module object and + are automatically cleaned up after script + execution terminates (ie, DelayCommand(0.0f)). + This is a potential gotcha, as the descriptor + and subschool data will no longer be available + during the spell's delayed operations. An + ugly workaround would be to set the descriptor + values again in such cases. + If you come up with an elegant solution, please + try to generalise it and change this as needed. + + + @author Ornedan + @date Created - 2006.06.30 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// The descriptor and subschool constants are bit flags for easy combination and lookup +const int DESCRIPTOR_ACID = 0x00001; +const int DESCRIPTOR_AIR = 0x00002; +const int DESCRIPTOR_CHAOTIC = 0x00004; +const int DESCRIPTOR_COLD = 0x00008; +const int DESCRIPTOR_DARKNESS = 0x00010; +const int DESCRIPTOR_DEATH = 0x00020; +const int DESCRIPTOR_EARTH = 0x00040; +const int DESCRIPTOR_ELECTRICITY = 0x00080; +const int DESCRIPTOR_EVIL = 0x00100; +const int DESCRIPTOR_FEAR = 0x00200; +const int DESCRIPTOR_FIRE = 0x00400; +const int DESCRIPTOR_FORCE = 0x00800; +const int DESCRIPTOR_GOOD = 0x01000; +const int DESCRIPTOR_LANGUAGEDEPENDENT = 0x02000; +const int DESCRIPTOR_LAWFUL = 0x04000; +const int DESCRIPTOR_LIGHT = 0x08000; +const int DESCRIPTOR_MINDAFFECTING = 0x10000; +const int DESCRIPTOR_SONIC = 0x20000; +const int DESCRIPTOR_WATER = 0x40000; +// update if any of elemental descriptors change! +// int DESCRIPTOR_ELEMENTAL = DESCRIPTOR_ACID | DESCRIPTOR_COLD | DESCRIPTOR_ELECTRICITY | DESCRIPTOR_FIRE | DESCRIPTOR_SONIC; +const int DESCRIPTOR_ELEMENTAL = 0x20489; + +const int SUBSCHOOL_CALLING = 0x00001; +const int SUBSCHOOL_CHARM = 0x00002; +const int SUBSCHOOL_COMPULSION = 0x00004; +const int SUBSCHOOL_CREATION = 0x00008; +const int SUBSCHOOL_FIGMENT = 0x00010; +const int SUBSCHOOL_GLAMER = 0x00020; +const int SUBSCHOOL_HEALING = 0x00040; +const int SUBSCHOOL_PATTERN = 0x00080; +const int SUBSCHOOL_PHANTASM = 0x00100; +const int SUBSCHOOL_POLYMORPH = 0x00200; +const int SUBSCHOOL_SCRYING = 0x00400; +const int SUBSCHOOL_SHADOW = 0x00800; +const int SUBSCHOOL_SUMMONING = 0x01000; +const int SUBSCHOOL_TELEPORTATION = 0x02000; + +const string PRC_DESCRIPTOR = "PRC_Descriptor"; +const string PRC_SUBSCHOOL = "PRC_Subschool"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Sets the descriptor of currently being cast spell / power / whatever + * to be the given value. This should be called before spellhook / powerhook + * / whateverhook. + * + * If the magic in question has multiple descriptors, they should be OR'd together. + * For example, Phantasmal Killer would call the function thus: + * SetDescriptor(DESCRIPTOR_FEAR | DESCRIPTOR_MINDAFFECTING); + * + * This will override values set in prc_spells.2da + * + * @param nfDescriptorFlags The descriptors of a spell / power / X being currently used + */ +void SetDescriptor(int nfDescriptorFlags, object oPC = OBJECT_SELF); + +/** + * Sets the subschool / subdiscipline / subwhatever of currently being cast + * spell / power / whatever to be the given value. This should be called before + * spellhook / powerhook / whateverhook. + * + * If the magic in question has multiple subschools, they should be OR'd together. + * For example, Mislead would call the function thus: + * SetDescriptor(SUBSCHOOL_FIGMENT | SUBSCHOOL_GLAMER); + * + * This will override values set in prc_spells.2da + * + * @param nfSubschoolFlags The subschools of a spell / power / X being currently used + */ +void SetSubschool(int nfSubschoolFlags, object oPC = OBJECT_SELF); + +/** + * Tests whether a magic being currently used has the given descriptor. + * + * NOTE: Multiple descriptors may be tested for at once. If so, the return value + * will be true only if all the descriptors tested for are present. Doing so is + * technically a misuse of this function. + * + * + * @param nSpellID row number of tested spell in spells.2da + * @param nDescriptorFlag The descriptor to test for + * @return TRUE if the magic being used has the given descriptor(s), FALSE otherwise + */ +int GetHasDescriptor(int nSpellID, int nDescriptorFlag); + +/** + * Returns TRUE if given spell has one of the elemental descriptors + * (acid, cold, electricity, fire or sonic) + * + * @param nSpellID row number of tested spell in spells.2da + * @return ID of elemental descriptor or FALSE + */ +int GetIsElementalSpell(int nSpellID, int nDescriptor = -1); + +/** + * Tests whether a magic being currently used is of the given subschool. + * + * NOTE: Multiple subschools may be tested for at once. If so, the return value + * will be true only if all the subschools tested for are present. Doing so is + * technically a misuse of this function. + * + * + * @param nSpellID row number of tested spell in spells.2da + * @param nDescriptorFlag The subschool to test for + * @return TRUE if the magic being used is of the given subschool(s), FALSE otherwise + */ +int GetIsOfSubschool(int nSpellID, int nSubschoolFlag); + +/** + * Returns an integer value containing the bitflags of descriptors set in prc_spells.2da + * for a given SpellID + * + * @param nSpellID row number of tested spell in spells.2da + * @return The converted raw integer value from prc_spells.2da + */ +int GetDescriptorFlags(int nSpellID); + +/** + * Returns an integer value containing the bitflags of subschools set in prc_spells.2da + * for a given SpellID + * + * @param nSpellID row number of tested spell in spells.2da + * @return The converted raw integer value from prc_spells.2da + */ +int GetSubschoolFlags(int nSpellID); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_2dacache" // already has access via inc_utility +//#include "inc_utility" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void SetDescriptor(int nfDescriptorFlags, object oPC = OBJECT_SELF) +{ + // Store the value + SetLocalInt(oPC, PRC_DESCRIPTOR, nfDescriptorFlags); + + // Queue cleanup. No duplicacy checks, this function is not particularly likely to be called more than once anyway + DelayCommand(0.0f, DeleteLocalInt(oPC, PRC_DESCRIPTOR)); +} + +void SetSubschool(int nfSubschoolFlags, object oPC = OBJECT_SELF) +{ + // Store the value + SetLocalInt(oPC, PRC_SUBSCHOOL, nfSubschoolFlags); + + // Queue cleanup. No duplicacy checks, this function is not particularly likely to be called more than once anyway + DelayCommand(0.0f, DeleteLocalInt(oPC, PRC_SUBSCHOOL)); +} + +int GetHasDescriptor(int nSpellID, int nDescriptorFlag) +{ + //check for descriptor override + int nDescriptor = GetLocalInt(OBJECT_SELF, PRC_DESCRIPTOR); + if(!nDescriptor) + nDescriptor = GetDescriptorFlags(nSpellID); + + if(nDescriptorFlag & DESCRIPTOR_ELEMENTAL) + { + return nDescriptorFlag & GetIsElementalSpell(nSpellID, nDescriptor); + } + + return nDescriptor & nDescriptorFlag; +} + +int GetIsElementalSpell(int nSpellID, int nDescriptor = -1) +{ + if(nDescriptor == -1) + { + //check for descriptor override + nDescriptor = GetLocalInt(OBJECT_SELF, PRC_DESCRIPTOR); + if(!nDescriptor) + nDescriptor = GetDescriptorFlags(nSpellID); + } + + int nMastery = GetLocalInt(OBJECT_SELF, "archmage_mastery_elements"); + + //mastery of elements is active + if(nMastery) + { + switch(nMastery) + { + case DAMAGE_TYPE_ACID: return DESCRIPTOR_ACID; + case DAMAGE_TYPE_COLD: return DESCRIPTOR_COLD; + case DAMAGE_TYPE_ELECTRICAL: return DESCRIPTOR_ELECTRICITY; + case DAMAGE_TYPE_FIRE: return DESCRIPTOR_FIRE; + case DAMAGE_TYPE_SONIC: return DESCRIPTOR_SONIC; + } + } + + return nDescriptor & DESCRIPTOR_ELEMENTAL; +} + +int GetIsOfSubschool(int nSpellID, int nSubschoolFlag) +{ + //check for subschool override + int nSubschool = GetLocalInt(OBJECT_SELF, PRC_SUBSCHOOL); + if(!nSubschool) + nSubschool = GetSubschoolFlags(nSpellID); + return (nSubschool & nSubschoolFlag); +} + +int GetDescriptorFlags(int nSpellID) +{ + return HexToInt(Get2DACache("prc_spells", "Descriptor", nSpellID)); +} + +int GetSubschoolFlags(int nSpellID) +{ + return HexToInt(Get2DACache("prc_spells", "Subschool", nSpellID)); +} + +// Test main +//void main(){} diff --git a/src/include/prc_inc_domain.nss b/src/include/prc_inc_domain.nss new file mode 100644 index 0000000..6b2f1ce --- /dev/null +++ b/src/include/prc_inc_domain.nss @@ -0,0 +1,586 @@ +//:://///////////////////////////////////////////// +//:: PRC Bonus Domains +//:: prc_inc_domain.nss +//::////////////////////////////////////////////// +//:: Handles all of the code for bonus domains. +//::////////////////////////////////////////////// +//:: Created By: Stratovarius. +//:: Created On: August 31st, 2005 +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + + +// Function returns the domain in the input slot. +// A person can have a maximum of 5 bonus domains. +int GetBonusDomain(object oPC, int nSlot); + +// Function will add a bonus domain to the stored list on the character. +void AddBonusDomain(object oPC, int nDomain); + +// Uses the slot and level to find the appropriate spell, then casts it using ActionCastSpell +// It will also decrement a spell from that level +// If the domain does not have an appropriate spell for that level, an error message appears and nothing happens +void CastDomainSpell(object oPC, int nSlot, int nLevel); + +// Takes the domain and spell level and uses it to find the appropriate spell. +// Right now it uses 2da reads on the domains.2da, although it could be scripted if desired. +int GetDomainSpell(int nDomain, int nLevel, object oPC); + +// Takes the spell level, and returns the radial feat for that level. +// Used in case there is no spell of the appropriate level. +int SpellLevelToFeat(int nLevel); + +// Will return the domain name as a string +// This is used to tell a PC what domains he has in what slot +string GetDomainName(int nDomain); + +// This is the starter function, and fires from Enter and Levelup +// It checks all of the bonus domain feats, and gives the PC the correct domains +void CheckBonusDomains(object oPC); + +// Returns the spell to be burned for CastDomainSpell +int GetBurnableSpell(object oPC, int nLevel); + +// Returns the Domain Power feat +int GetDomainFeat(int nDomain); + +// Returns the Uses per day of the feat entered +int GetDomainFeatUsesPerDay(int nFeat, object oPC); + +// This counts down the number of times a domain has been used in a day +// Returns TRUE if the domain use is valid +// Returns FALSE if the player is out of uses per day +int DecrementDomainUses(int nDomain, object oPC); + +// Used to determine which domain has cast the Turn Undead spell +// Returns the domain constant +int GetTurningDomain(int nSpell); + +// Checks to see if the player has a domain. +// Looks for the domain power constants since every domain has those +int GetHasDomain(object oPC, int nDomain); + +// Cleans the ints that limit the domain spells to being cast 1/day +void BonusDomainRest(object oPC); + +//#include "prc_inc_clsfunc" +#include "prc_alterations" +#include "prc_getbest_inc" +#include "inc_dynconv" + +int GetBonusDomain(object oPC, int nSlot) +{ + /*string sName = "PRCBonusDomain" + IntToString(nSlot); + // Return value in case there is nothing in the slot + int nDomain = 0; + nDomain = GetPersistantLocalInt(oPC, sName);*/ + + return GetPersistantLocalInt(oPC, "PRCBonusDomain" + IntToString(nSlot)); +} + +void AddBonusDomain(object oPC, int nDomain) +{ + //if(DEBUG) DoDebug("AddBonusDomain is running."); + + // Loop through the domain slots to see if there is an open one. + int nSlot = 1; + int nTest = GetBonusDomain(oPC, nSlot); + while(nTest > 0 && 5 >= nSlot) + { + nSlot += 1; + // If the test domain and the domain to be added are the same + // shut down the function, since you don't want to add a domain twice. + if(nTest == nDomain) + { + //FloatingTextStringOnCreature("You already have this domain as a bonus domain.", oPC, FALSE); + return; + } + nTest = GetBonusDomain(oPC, nSlot); + } + // If you run out of slots, display message and end function + if (nSlot > 5) + { + FloatingTextStringOnCreature("You have more than 5 bonus domains, your last domain is lost.", oPC, FALSE); + return; + } + + // If we're here, we know we have an open slot, so we add the domain into it. + string sName = "PRCBonusDomain" + IntToString(nSlot); + SetPersistantLocalInt(oPC, sName, nDomain); + FloatingTextStringOnCreature("You have " + GetStringByStrRef(StringToInt(Get2DACache("prc_domains", "Name", nDomain - 1))) + " as a bonus domain", oPC, FALSE); +} + +int TestSpellTarget(object oPC, object oTarget, int nSpell) +{ + int nTargetType = ~(HexToInt(Get2DACache("spells", "TargetType", nSpell))); + + if(oTarget == oPC && nTargetType & 1) + { + SendMessageToPC(oPC, "You cannot target yourself!"); + return FALSE; + } + else if(GetIsObjectValid(oTarget)) + { + int nObjectType = GetObjectType(oTarget); + if(nObjectType == OBJECT_TYPE_CREATURE && nTargetType & 2) + { + SendMessageToPC(oPC, "You cannot target creatures"); + return FALSE; + } + else if(nObjectType == OBJECT_TYPE_ITEM && nTargetType & 8) + { + SendMessageToPC(oPC, "You cannot target items"); + return FALSE; + } + else if(nObjectType == OBJECT_TYPE_DOOR && nTargetType & 16) + { + SendMessageToPC(oPC, "You cannot target doors"); + return FALSE; + } + else if(nObjectType == OBJECT_TYPE_PLACEABLE && nTargetType & 32) + { + SendMessageToPC(oPC, "You cannot target placeables"); + return FALSE; + } + } + else if(nTargetType & 4) + { + SendMessageToPC(oPC, "You cannot target locations"); + return FALSE; + } + return TRUE; +} + +// Classes using new spellbook systems are handeled separately +int GetIsBioDivineClass(int nClass) +{ + return nClass == CLASS_TYPE_CLERIC + || nClass == CLASS_TYPE_DRUID + || nClass == CLASS_TYPE_PALADIN + || nClass == CLASS_TYPE_SHAMAN + || nClass == CLASS_TYPE_UR_PRIEST + || nClass == CLASS_TYPE_RANGER; +} + +void CastDomainSpell(object oPC, int nSlot, int nLevel) +{ + if(GetLocalInt(oPC, "DomainCastSpell" + IntToString(nLevel))) //Already cast a spell of this level? + { + FloatingTextStringOnCreature("You have already cast your domain spell for level " + IntToString(nLevel), oPC, FALSE); + return; + } + + int nSpell = GetDomainSpell(GetBonusDomain(oPC, nSlot), nLevel, oPC); + // If there is no spell for that level, you cant cast it. + if(nSpell == -1) + return; + + // Subradial spells are handled through conversation + int bSubRadial = Get2DACache("spells", "SubRadSpell1", nSpell) != ""; + + // Domain casting feats use generic targeting, so check if spell can be cast at selected target + object oTarget = GetSpellTargetObject(); + if(!bSubRadial && !TestSpellTarget(oPC, oTarget, nSpell)) + return; + + int nClass, nCount, nMetamagic = METAMAGIC_NONE; + + // Mystic is a special case - checked first + if(GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) || GetLevelByClass(CLASS_TYPE_NIGHTSTALKER, oPC)) + { + // Mystics can use metamagic with domain spells + nClass = GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) ? CLASS_TYPE_MYSTIC : CLASS_TYPE_NIGHTSTALKER; + nMetamagic = GetLocalInt(oPC, "MetamagicFeatAdjust"); + int nSpellLevel = nLevel; + if(nMetamagic) + { + //Need to check if metamagic can be applied to a spell + int nMetaTest; + int nMetaType = HexToInt(Get2DACache("spells", "MetaMagic", nSpell)); + + switch(nMetamagic) + { + case METAMAGIC_NONE: nMetaTest = 1; break; //no need to change anything + case METAMAGIC_EMPOWER: nMetaTest = nMetaType & 1; nSpellLevel += 2; break; + case METAMAGIC_EXTEND: nMetaTest = nMetaType & 2; nSpellLevel += 1; break; + case METAMAGIC_MAXIMIZE: nMetaTest = nMetaType & 4; nSpellLevel += 3; break; + case METAMAGIC_QUICKEN: nMetaTest = nMetaType & 8; nSpellLevel += 4; break; + case METAMAGIC_SILENT: nMetaTest = nMetaType & 16; nSpellLevel += 1; break; + case METAMAGIC_STILL: nMetaTest = nMetaType & 32; nSpellLevel += 1; break; + } + if(!nMetaTest)//can't use selected metamagic with this spell + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "You can't use "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpell)))+"with selected metamagic.")); + nSpellLevel = nLevel; + } + else if(nLevel > 9)//now test the spell level + { + nMetamagic = METAMAGIC_NONE; + ActionDoCommand(SendMessageToPC(oPC, "Modified spell level is to high! Casting spell without metamagic")); + nSpellLevel = nLevel; + } + else if(GetLocalInt(oPC, "PRC_metamagic_state") == 1) + SetLocalInt(oPC, "MetamagicFeatAdjust", 0); + } + + nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(CLASS_TYPE_MYSTIC), nSpellLevel); + // we can't cast metamagiced version of the spell - assuming that player want to cast the spell anyway + if(!nCount) + nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(CLASS_TYPE_MYSTIC), nLevel); + // Do we have slots available? + if(nCount) + { + // Prepare to cast the spell + nLevel = nSpellLevel;//correct the spell level if we're using metamagic + SetLocalInt(oPC, "NSB_Class", nClass); + SetLocalInt(oPC, "NSB_SpellLevel", nLevel); + } + } + + // checking 'newspellbook' classes is much faster than checking bioware spellbooks + if(!nCount) + { + int n; + for(n = 1; n < 9; n++) + { + nClass = GetClassByPosition(n, oPC); + + // Check to see if you can burn a spell of that slot or if the person has already + // cast all of their level X spells for the day + if(!GetIsBioDivineClass(nClass)) + { + int nSpellbook = GetSpellbookTypeForClass(nClass); + if(nSpellbook == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oPC, "NewSpellbookMem_" + IntToString(nClass), nLevel); + if(nCount) + {// Prepare to cast the spell + SetLocalInt(oPC, "NSB_Class", nClass); + SetLocalInt(oPC, "NSB_SpellLevel", nLevel); + } + } + else if(nSpellbook == SPELLBOOK_TYPE_PREPARED) + { + string sArray = "NewSpellbookMem_"+IntToString(nClass); + string sIDX = "SpellbookIDX" + IntToString(nLevel) + "_" + IntToString(nClass); + int i, nSpellbookID, nMax = persistant_array_get_size(oPC, sIDX); + for(i = 0; i < nMax; i++) + { + nSpellbookID = persistant_array_get_int(oPC, sIDX, i); + nCount = persistant_array_get_int(oPC, sArray, nSpellbookID); + if(nCount) + { + SetLocalInt(oPC, "NSB_Class", nClass); + SetLocalInt(oPC, "NSB_SpellbookID", nSpellbookID); + break; + } + } + } + } + if(nCount) + //we have found valid spell slot, no point in running this loop again + break; + } + } + + // test bioware spellbooks + if(!nCount) + { + nCount = GetBurnableSpell(oPC, nLevel) + 1;//fix for Acid Fog spell + if(nCount) + { + SetLocalInt(oPC, "Domain_BurnableSpell", nCount); + nClass = GetPrimaryDivineClass(oPC); + } + } + + //No spell left to burn? Tell the player that. + if(!nCount) + { + FloatingTextStringOnCreature("You have no spells left to trade for a domain spell.", oPC, FALSE); + return; + } + + SetLocalInt(oPC, "DomainCast", nLevel); + if(bSubRadial) + { + SetLocalInt(oPC, "DomainOrigSpell", nSpell); + SetLocalInt(oPC, "DomainCastClass", nClass); + SetLocalObject(oPC, "DomainTarget", oTarget); + SetLocalLocation(oPC, "DomainTarget", GetSpellTargetLocation()); + StartDynamicConversation("prc_domain_conv", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC); + } + else + { + if(nMetamagic & METAMAGIC_QUICKEN) + { + //Adding Auto-Quicken III for one round - deleted after casting is finished. + object oSkin = GetPCSkin(oPC); + int nCastDur = StringToInt(Get2DACache("spells", "ConjTime", nSpell)) + StringToInt(Get2DACache("spells", "CastTime", nSpell)); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nCastDur/1000.0f)); + } + int nDC = 10 + nLevel + GetDCAbilityModForClass(nClass, oPC); + ActionCastSpell(nSpell, 0, nDC, 0, nMetamagic, nClass, FALSE, FALSE, OBJECT_INVALID, FALSE); + ActionDoCommand(DeleteLocalInt(oPC, "DomainCast")); + } +} + +int GetDomainSpell(int nDomain, int nLevel, object oPC) +{ + // The -1 on nDomains is to adjust from a base 1 to a base 0 system. + string sSpell = Get2DACache("prc_domains", "Level_" + IntToString(nLevel), (nDomain - 1)); + if (DEBUG) DoDebug("Domain Spell: " + sSpell); + //if (DEBUG) DoDebug("GetDomainSpell has fired"); + int nSpell = -1; + if(sSpell == "") + { + FloatingTextStringOnCreature("You do not have a domain spell of that level.", oPC, FALSE); + //int nFeat = SpellLevelToFeat(nLevel); + //IncrementRemainingFeatUses(oPC, nFeat); + } + else + { + nSpell = StringToInt(sSpell); + } + + return nSpell; +} + +int SpellLevelToFeat(int nLevel) +{ + switch(nLevel) + { + case 1: return FEAT_CAST_DOMAIN_LEVEL_ONE; + case 2: return FEAT_CAST_DOMAIN_LEVEL_TWO; + case 3: return FEAT_CAST_DOMAIN_LEVEL_THREE; + case 4: return FEAT_CAST_DOMAIN_LEVEL_FOUR; + case 5: return FEAT_CAST_DOMAIN_LEVEL_FIVE; + case 6: return FEAT_CAST_DOMAIN_LEVEL_SIX; + case 7: return FEAT_CAST_DOMAIN_LEVEL_SEVEN; + case 8: return FEAT_CAST_DOMAIN_LEVEL_EIGHT; + case 9: return FEAT_CAST_DOMAIN_LEVEL_NINE; + } + + return -1; +} + +string GetDomainName(int nDomain) +{ + string sName; + // Check that the domain slot is not empty + if(nDomain) + { + sName = Get2DACache("prc_domains", "Name", (nDomain - 1)); + sName = GetStringByStrRef(StringToInt(sName)); + } + else + sName = GetStringByStrRef(6497); // "Empty Slot" + + return sName; +} + +void CheckBonusDomains(object oPC) +{ + int nBonusDomain, nDomainFeat; + int nSlot = 1; + while(nSlot < 6) + { + nBonusDomain = GetBonusDomain(oPC, nSlot); + nDomainFeat = GetDomainFeat(nBonusDomain); + if(!GetHasFeat(nDomainFeat, oPC)) SetPersistantLocalInt(oPC, "PRCBonusDomain" + IntToString(nSlot), 0); + //SendMessageToPC(oPC, "PRCBonusDomain"+IntToString(nSlot)" = "+IntToString(nBonusDomain)); + //SendMessageToPC(oPC, "PRCBonusDomain"+IntToString(nSlot)" feat = "+IntToString(GetDomainFeat(nDomainFeat))); + nSlot += 1; + } + + if (GetHasFeat(FEAT_BONUS_DOMAIN_AIR, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_AIR); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ANIMAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ANIMAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DEATH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DEATH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DESTRUCTION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DESTRUCTION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_EARTH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_EARTH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_EVIL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_EVIL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FIRE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FIRE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_GOOD, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_GOOD); + if (GetHasFeat(FEAT_BONUS_DOMAIN_HEALING, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_HEALING); + if (GetHasFeat(FEAT_BONUS_DOMAIN_KNOWLEDGE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_KNOWLEDGE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_MAGIC, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_MAGIC); + if (GetHasFeat(FEAT_BONUS_DOMAIN_PLANT, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_PLANT); + if (GetHasFeat(FEAT_BONUS_DOMAIN_PROTECTION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_PROTECTION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_STRENGTH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_STRENGTH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SUN); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TRAVEL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TRAVEL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TRICKERY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TRICKERY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_WAR, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_WAR); + if (GetHasFeat(FEAT_BONUS_DOMAIN_WATER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_WATER); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DARKNESS, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DARKNESS); + if (GetHasFeat(FEAT_BONUS_DOMAIN_STORM, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_STORM); + if (GetHasFeat(FEAT_BONUS_DOMAIN_METAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_METAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_PORTAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_PORTAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FORCE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FORCE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SLIME, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SLIME); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TYRANNY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TYRANNY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DOMINATION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DOMINATION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SPIDER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SPIDER); + if (GetHasFeat(FEAT_BONUS_DOMAIN_UNDEATH, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_UNDEATH); + if (GetHasFeat(FEAT_BONUS_DOMAIN_TIME, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_TIME); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DWARF, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DWARF); + if (GetHasFeat(FEAT_BONUS_DOMAIN_CHARM, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_CHARM); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ELF, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ELF); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FAMILY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FAMILY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_FATE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_FATE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_GNOME, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_GNOME); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ILLUSION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ILLUSION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_HATRED, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_HATRED); + if (GetHasFeat(FEAT_BONUS_DOMAIN_HALFLING, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_HALFLING); + if (GetHasFeat(FEAT_BONUS_DOMAIN_NOBILITY, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_NOBILITY); + if (GetHasFeat(FEAT_BONUS_DOMAIN_OCEAN, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_OCEAN); + if (GetHasFeat(FEAT_BONUS_DOMAIN_ORC, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_ORC); + if (GetHasFeat(FEAT_BONUS_DOMAIN_RENEWAL, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_RENEWAL); + if (GetHasFeat(FEAT_BONUS_DOMAIN_RETRIBUTION, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_RETRIBUTION); + if (GetHasFeat(FEAT_BONUS_DOMAIN_RUNE, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_RUNE); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SPELLS, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SPELLS); + if (GetHasFeat(FEAT_BONUS_DOMAIN_SCALEYKIND, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_SCALEYKIND); + if (GetHasFeat(FEAT_BONUS_DOMAIN_BLIGHTBRINGER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_BLIGHTBRINGER); + if (GetHasFeat(FEAT_BONUS_DOMAIN_DRAGON, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_DRAGON); + if (GetHasFeat(FEAT_BONUS_DOMAIN_COLD, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_COLD); + if (GetHasFeat(FEAT_BONUS_DOMAIN_WINTER, oPC)) AddBonusDomain(oPC, PRC_DOMAIN_WINTER); + + //if (DEBUG) FloatingTextStringOnCreature("Check Bonus Domains is running", oPC, FALSE); +} + +int GetBurnableSpell(object oPC, int nLevel) +{ + int nBurnableSpell = -1; + + if (nLevel == 1) nBurnableSpell = GetBestL1Spell(oPC, nBurnableSpell); + else if (nLevel == 2) nBurnableSpell = GetBestL2Spell(oPC, nBurnableSpell); + else if (nLevel == 3) nBurnableSpell = GetBestL3Spell(oPC, nBurnableSpell); + else if (nLevel == 4) nBurnableSpell = GetBestL4Spell(oPC, nBurnableSpell); + else if (nLevel == 5) nBurnableSpell = GetBestL5Spell(oPC, nBurnableSpell); + else if (nLevel == 6) nBurnableSpell = GetBestL6Spell(oPC, nBurnableSpell); + else if (nLevel == 7) nBurnableSpell = GetBestL7Spell(oPC, nBurnableSpell); + else if (nLevel == 8) nBurnableSpell = GetBestL8Spell(oPC, nBurnableSpell); + else if (nLevel == 9) nBurnableSpell = GetBestL9Spell(oPC, nBurnableSpell); + + return nBurnableSpell; +} + +int GetDomainFeat(int nDomain) +{ + // The -1 on nDomain is to adjust from a base 1 to a base 0 system. + // Returns the domain power feat + return StringToInt(Get2DACache("domains", "GrantedFeat", nDomain - 1)); +} + +int GetDomainFeatUsesPerDay(int nFeat, object oPC) +{ + int nUses = StringToInt(Get2DACache("feat", "USESPERDAY", nFeat)); + // These are the domains that have ability based uses per day + if (nUses == 33) + { + // The Strength domain, which uses Strength when the Cleric has Kord levels + // Without Kord levels, its 1 use per day + if(nFeat == FEAT_STRENGTH_DOMAIN_POWER) + { + nUses = 1; + if(GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oPC)) nUses = GetAbilityModifier(ABILITY_STRENGTH, oPC); + // Catching exceptions + if(nUses < 1) nUses = 1; + } + if(nFeat == FEAT_SUN_DOMAIN_POWER) + { + if(GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC) && GetLevelByClass(CLASS_TYPE_MYSTIC, oPC)) + { + nUses = GetHasFeat(FEAT_EXTRA_TURNING, oPC) ? 7 : 3; + nUses += GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + else + nUses = 1; + } + + // All other ones so far are the Charisma based turning domains + nUses = 3 + GetAbilityModifier(ABILITY_CHARISMA, oPC); + } + + return nUses; +} + +int DecrementDomainUses(int nDomain, object oPC) +{ + int nReturn = TRUE; + int nUses = GetLocalInt(oPC, "BonusDomainUsesPerDay" + GetDomainName(nDomain)); + // If there is still a valid use left, remove it + if (nUses >= 1) SetLocalInt(oPC, "BonusDomainUsesPerDay" + GetDomainName(nDomain), (nUses - 1)); + // Tell the player how many uses he has left + else // He has no more uses for the day + { + nReturn = FALSE; + } + + FloatingTextStringOnCreature("You have " + IntToString(nUses - 1) + " uses per day left of the " + GetDomainName(nDomain) + " power.", oPC, FALSE); + + return nReturn; +} + +int GetTurningDomain(int nSpell) +{ + switch(nSpell) + { + case SPELL_TURN_REPTILE: return PRC_DOMAIN_SCALEYKIND; + case SPELL_TURN_OOZE: return PRC_DOMAIN_SLIME; + case SPELL_TURN_SPIDER: return PRC_DOMAIN_SPIDER; + case SPELL_TURN_PLANT: return PRC_DOMAIN_PLANT; + case SPELL_TURN_AIR: return PRC_DOMAIN_AIR; + case SPELL_TURN_EARTH: return PRC_DOMAIN_EARTH; + case SPELL_TURN_FIRE: return PRC_DOMAIN_FIRE; + case SPELL_TURN_WATER: return PRC_DOMAIN_WATER; + case SPELL_TURN_BLIGHTSPAWNED: return PRC_DOMAIN_BLIGHTBRINGER; + } + + return -1; +} + +int GetHasDomain(object oPC, int nDomain) +{ + // Get the domain power feat for the appropriate domain + int nFeat = GetDomainFeat(nDomain); + + return GetHasFeat(nFeat, oPC); +} + +void BonusDomainRest(object oPC) +{ + // Bonus Domain ints that limit you to casting 1/day per level + int i; + for (i = 1; i < 10; i++) + { + DeleteLocalInt(oPC, "DomainCastSpell" + IntToString(i)); + } + + // This is code to stop you from using the Domain per day abilities more than you should be able to + int i2; + // Highest domain constant is 62 + for (i2 = 1; i2 < 63; i2++) + { + // This is to ensure they only get the ints set for the domains they do have + if (GetHasDomain(oPC, i2)) + { + // Store the number of uses a day here + SetLocalInt(oPC, "BonusDomainUsesPerDay" + GetDomainName(i2), GetDomainFeatUsesPerDay(GetDomainFeat(i2), oPC)); + } + } +} + +int GetDomainCasterLevel(object oPC) +{ + return GetLevelByClass(CLASS_TYPE_CLERIC, oPC) + + GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) + + GetLevelByClass(CLASS_TYPE_SHAMAN, oPC) + + GetLevelByClass(CLASS_TYPE_TEMPLAR, oPC) + + GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oPC) + + GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oPC) + + GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oPC); +} \ No newline at end of file diff --git a/src/include/prc_inc_dragsham.nss b/src/include/prc_inc_dragsham.nss new file mode 100644 index 0000000..dacdaf0 --- /dev/null +++ b/src/include/prc_inc_dragsham.nss @@ -0,0 +1,188 @@ + +// Metabreath Feats - Not implemented yet. +/** const int FEAT_CLINGING_BREATH = 5000; +const int FEAT_LINGERING_BREATH = 5001; +const int FEAT_ENLARGE_BREATH = 5002; +const int FEAT_HEIGHTEN_BREATH = 5003; +const int FEAT_MAXIMIZE_BREATH = 5004; +const int FEAT_QUICKEN_BREATH = 5005; +const int FEAT_RECOVER_BREATH = 5006; +const int FEAT_SHAPE_BREATH = 5007; +const int FEAT_SPLIT_BREATH = 5008; +const int FEAT_SPREADING_BREATH = 5009; +const int FEAT_EXTEND_SPREADING_BREATH = 5010; +const int FEAT_TEMPEST_BREATH = 5011; **/ + +// Dragon Shaman Aura flag +//const int DRAGON_SHAMAN_AURA_ACTIVE = 5000; + +#include "prc_inc_nwscript" + +int GetIsDragonblooded(object oPC); + +// returns the damage type of dragon that the PC has a totem for. Checks for the presence of the +// various feats that are present indicating such. This is necessary for determining the type +// of breath weapon and the type of damage immunity. +int GetDragonDamageType(int nTotem); + +// Used to create a flag on the caster and store the Aura currently being run. +//int StartDragonShamanAura(object oCaster, int nSpellId); + +// Resets the available ToV points; for use after rest or on enter. +void ResetTouchOfVitality(object oPC); + +// Applies any metabreath feats that the dragon shaman may have to his breathweapon +//int ApplyMetaBreathFeatMods( int nDuration, object oCaster ); + +int GetIsDragonblooded(object oPC) +{ + int nRace = GetRacialType(oPC); + if(nRace == RACIAL_TYPE_KOBOLD + || nRace == RACIAL_TYPE_SPELLSCALE + || nRace == RACIAL_TYPE_DRAGONBORN + || nRace == RACIAL_TYPE_STONEHUNTER_GNOME + || nRace == RACIAL_TYPE_SILVERBROW_HUMAN + || nRace == RACIAL_TYPE_FORESTLORD_ELF + || nRace == RACIAL_TYPE_FIREBLOOD_DWARF + || nRace == RACIAL_TYPE_GLIMMERSKIN_HALFING + || nRace == RACIAL_TYPE_FROSTBLOOD_ORC + || nRace == RACIAL_TYPE_SUNSCORCH_HOBGOBLIN + || nRace == RACIAL_TYPE_VILETOOTH_LIZARDFOLK) + return TRUE; + + if(GetLevelByClass(CLASS_TYPE_DRAGON_DISCIPLE, oPC) > 9) + return TRUE; + + if(GetHasFeat(FEAT_DRAGONTOUCHED, oPC) + || GetHasFeat(FEAT_DRACONIC_DEVOTEE, oPC) + || GetHasFeat(FEAT_DRAGON, oPC) + || GetHasFeat(DRAGON_BLOODED, oPC)) + return TRUE; + + //Draconic Heritage qualifies for dragonblood + if(GetHasFeat(FEAT_DRACONIC_HERITAGE_BK, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_BL, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_GR, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_RD, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_WH, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_AM, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_CR, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_EM, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_SA, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_TP, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_BS, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_BZ, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_CP, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_GD, oPC) + || GetHasFeat(FEAT_DRACONIC_HERITAGE_SR, oPC)) + return TRUE; + + return FALSE; +} + +int GetDragonDamageType(int nTotem) +{ + int nDamageType = nTotem == FEAT_DRAGONSHAMAN_BLACK ? DAMAGE_TYPE_ACID: + nTotem == FEAT_DRAGONSHAMAN_BLUE ? DAMAGE_TYPE_ELECTRICAL: + nTotem == FEAT_DRAGONSHAMAN_BRASS ? DAMAGE_TYPE_FIRE: + nTotem == FEAT_DRAGONSHAMAN_BRONZE ? DAMAGE_TYPE_ELECTRICAL: + nTotem == FEAT_DRAGONSHAMAN_COPPER ? DAMAGE_TYPE_ACID: + nTotem == FEAT_DRAGONSHAMAN_GOLD ? DAMAGE_TYPE_FIRE: + nTotem == FEAT_DRAGONSHAMAN_GREEN ? DAMAGE_TYPE_ACID: + nTotem == FEAT_DRAGONSHAMAN_RED ? DAMAGE_TYPE_FIRE: + nTotem == FEAT_DRAGONSHAMAN_SILVER ? DAMAGE_TYPE_COLD: + nTotem == FEAT_DRAGONSHAMAN_WHITE ? DAMAGE_TYPE_COLD: + -1; + + return nDamageType; +} + +void ResetTouchOfVitality(object oPC) +{ + if(GetHasFeat(FEAT_DRAGONSHAMAN_TOUCHVITALITY, oPC)) + { + int nChaBonus = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nChaBonus < 0) nChaBonus = 0; + int nVitPoints = 2 * GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oPC) * nChaBonus; + SetLocalInt(oPC, "DRAGON_SHAMAN_TOUCH_REMAIN", nVitPoints); + string sMes = "Healing power: " + IntToString(nVitPoints) + " points."; + FloatingTextStringOnCreature(sMes, oPC, FALSE); + } + if(GetHasFeat(FEAT_ABERRANT_DURABLE_FORM, oPC)) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectTemporaryHitpoints(GetAberrantFeatCount(oPC)), oPC); +} + +int GetMarshalAuraPower(object oPC) +{ + int iMarshalLevel = GetLevelByClass(CLASS_TYPE_MARSHAL, oPC); + int nBonus; + if(iMarshalLevel > 19) + nBonus = iMarshalLevel / 5; + else if(iMarshalLevel > 13) + nBonus = 3; + else if(iMarshalLevel > 6) + nBonus = 2; + else if(iMarshalLevel > 1) + nBonus = 1; + + return nBonus; +} + +int GetDragonShamanAuraPower(object oPC) +{ + int iDragonShamanLevel = GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oPC); + int nBonus = (iDragonShamanLevel / 5) + 1; + + return nBonus; +} + +int GetExtraAuraPower(object oPC) +{ + int nBonus; + if(GetIsDragonblooded(oPC)) + { + int nHD = GetHitDice(oPC); + if(nHD > 19) + nBonus = 4; + else if(nHD > 13) + nBonus = 3; + else if(nHD > 6) + nBonus = 2; + else + nBonus = 1; + } + + return nBonus; + } + +int GetAuraBonus(object oPC) +{ + int nAuraBonus = GetDragonShamanAuraPower(oPC); + int nMarshalBonus = GetMarshalAuraPower(oPC); + int nExtraBonus = GetExtraAuraPower(oPC); + + if(nMarshalBonus > nAuraBonus) + nAuraBonus = nMarshalBonus; + + if(nExtraBonus > nAuraBonus) + nAuraBonus = nExtraBonus; + + return nAuraBonus; +} + +object GetAuraObject(object oShaman, string sTag) +{ + location lTarget = GetLocation(oShaman); + object oAura = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + while(GetIsObjectValid(oAura)) + { + if((GetAreaOfEffectCreator(oAura) == oShaman) //was cast by shaman + && GetTag(oAura) == sTag //it's a draconic aura + && !GetLocalInt(oAura, "SpellID")) //and was not setup before + { + return oAura; + } + oAura = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + } + return OBJECT_INVALID; +} diff --git a/src/include/prc_inc_drugfunc.nss b/src/include/prc_inc_drugfunc.nss new file mode 100644 index 0000000..d40e850 --- /dev/null +++ b/src/include/prc_inc_drugfunc.nss @@ -0,0 +1,92 @@ +//::////////////////////////////////////////////// +//:: Drug system functions +//:: prc_inc_drugfunc +//::////////////////////////////////////////////// +/** @file + A bunch of functions common to the drug + scripts. + + @author Ornedan + @data Created 2006.05.29 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Increments the overdose tracking counter by one for the given drug and queues + * decrementing it by one after the given period. + * The value of this counter is what will be returned by GetHasOverdosed(). + * + */ +void IncrementOverdoseTracker(object oDrugUser, string sODIdentifier, float fODPeriod); + +/** + * Checks if the given drug user is currently in the overdose period of the given drug. + * The value returned is the number of drug uses in the overdose period of which is + * currently active. + * + * @param oDrugUser A creature using a drug. + * @param sODIdentifier The name of the drug's overdose identifier. + * @return The current value of the overdose variable - how many drug uses of + * the given drug are right now in their overdose period. + */ +int GetOverdoseCounter(object oDrugUser, string sODIdentifier); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Implements the decrementing of the overdose counters. + * + * @param oDrugUser A creature using a drug. + * @param sODIdentifier The name of the drug's overdose identifier. + */ +void _prc_inc_drugfunc_DecrementOverdoseTracker(object oDrugUser, string sODIdentifier) +{ + // Delete the variable if decrementing would it would make it 0 + if(GetLocalInt(oDrugUser, sODIdentifier) <= 1) + DeleteLocalInt(oDrugUser, sODIdentifier); + else + SetLocalInt(oDrugUser, sODIdentifier, GetLocalInt(oDrugUser, sODIdentifier) - 1); +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void IncrementOverdoseTracker(object oDrugUser, string sODIdentifier, float fODPeriod) +{ + SetLocalInt(oDrugUser, sODIdentifier, GetLocalInt(oDrugUser, sODIdentifier) + 1); + DelayCommand(fODPeriod, _prc_inc_drugfunc_DecrementOverdoseTracker(oDrugUser, sODIdentifier)); +} + + +int GetOverdoseCounter(object oDrugUser, string sODIdentifier) +{ + return GetLocalInt(oDrugUser, sODIdentifier); +} + + +// Test main +//void main(){} diff --git a/src/include/prc_inc_effect.nss b/src/include/prc_inc_effect.nss new file mode 100644 index 0000000..aadb468 --- /dev/null +++ b/src/include/prc_inc_effect.nss @@ -0,0 +1,1900 @@ +/* + +This include file details the PRC replacement for biowares effect variable type +It is required so that we can access the components of an effect much as we can +with itemproperties. +Also useful for storing things like metamagic, spell level, etc + + +Primogenitor +*/ + +struct PRCeffect{ + effect eEffect; + int nEffectType; + int nEffectSubtype; + int nDurationType; + float fDuration; + int nVersesRace; + int nVersesTraps; + int nVersesAlignmentOrder; + int nVersesAlignmentMoral; + + //these are the subcomponents + //probably more here than needed, but better safe than sorry! + int nVar1; object oVar1; string sVar1; location lVar1; float fVar1; + int nVar2; object oVar2; string sVar2; location lVar2; float fVar2; + int nVar3; object oVar3; string sVar3; location lVar3; float fVar3; + int nVar4; object oVar4; string sVar4; location lVar4; float fVar4; + int nVar5; object oVar5; string sVar5; location lVar5; float fVar5; + int nVar6; object oVar6; string sVar6; location lVar6; float fVar6; + int nVar7; object oVar7; string sVar7; location lVar7; float fVar7; + int nVar8; object oVar8; string sVar8; location lVar8; float fVar8; + int nVar9; object oVar9; string sVar9; location lVar9; float fVar9; + + int nLinkedCount; + int nLinkedID; + //linked effects are stored in an array on the module with the prefix PRC_LinkEffects_X + //where X is an ID number that is incremented for each new effect that has linked effects + + int nSpellID; + int nCasterLevel; + object oCaster; + int nMetamagic; + int nSpellLevel; + int nWeave; +}; + +//get/set local handlers +void SetLocalPRCEffect(object oObject, string sVarName, struct PRCeffect eValue); +struct PRCeffect GetLocalPRCEffect(object oObject, string sVarName); +void DeleteLocalPRCEffect(object oObject, string sVarName); + +//default constructor +struct PRCeffect GetNewPRCEffectBase(); + +// Get the first in-game effect on oCreature. +struct PRCeffect PRCGetFirstEffect(object oCreature); + +// Get the next in-game effect on oCreature. +struct PRCeffect PRCGetNextEffect(object oCreature); + +// * Returns TRUE if eEffect is a valid effect. The effect must have been applied to +// * an object or else it will return FALSE +int PRCGetIsEffectValid(struct PRCeffect prceEffect); + +// Get the duration type (DURATION_TYPE_*) of eEffect. +// * Return value if eEffect is not valid: -1 +int PRCGetEffectDurationType(struct PRCeffect prceEffect); + +// Get the subtype (SUBTYPE_*) of eEffect. +// * Return value on error: 0 +int PRCGetEffectSubType(struct PRCeffect prceEffect); + +// Get the object that created eEffect. +// * Returns OBJECT_INVALID if eEffect is not a valid effect. +object PRCGetEffectCreator(struct PRCeffect prceEffect); + +// Get the effect type (EFFECT_TYPE_*) of eEffect. +// * Return value if eEffect is invalid: EFFECT_INVALIDEFFECT +int PRCGetEffectType(struct PRCeffect prceEffect); + +// Get the spell (SPELL_*) that applied eSpellEffect. +// * Returns -1 if eSpellEffect was applied outside a spell script. +int PRCGetEffectSpellId(struct PRCeffect prceEffect); + +// gets the real effect based on an effect structure +// should never be needed outside the effect system itself +effect GetEffectOnObjectFromPRCEffect(struct PRCeffect prceEffect, object oObject); + +// Remove eEffect from oCreature. +// * No return value +void PRCRemoveEffect(object oCreature, struct PRCeffect eEffect); + +// Set the subtype of eEffect to Magical and return eEffect. +// (Effects default to magical if the subtype is not set) +// Magical effects are removed by resting, and by dispel magic +struct PRCeffect PRCMagicalEffect(struct PRCeffect eEffect); + +// Set the subtype of eEffect to Supernatural and return eEffect. +// (Effects default to magical if the subtype is not set) +// Permanent supernatural effects are not removed by resting +struct PRCeffect PRCSupernaturalEffect(struct PRCeffect eEffect); + +// Set the subtype of eEffect to Extraordinary and return eEffect. +// (Effects default to magical if the subtype is not set) +// Extraordinary effects are removed by resting, but not by dispel magic +struct PRCeffect PRCExtraordinaryEffect(struct PRCeffect eEffect); + +// Set eEffect to be versus a specific alignment. +// - eEffect +// - nLawChaos: ALIGNMENT_LAWFUL/ALIGNMENT_CHAOTIC/ALIGNMENT_ALL +// - nGoodEvil: ALIGNMENT_GOOD/ALIGNMENT_EVIL/ALIGNMENT_ALL +struct PRCeffect PRCVersusAlignmentEffect(struct PRCeffect eEffect, int nLawChaos=ALIGNMENT_ALL, int nGoodEvil=ALIGNMENT_ALL); + +// Set eEffect to be versus nRacialType. +// - eEffect +// - nRacialType: RACIAL_TYPE_* +struct PRCeffect PRCVersusRacialTypeEffect(struct PRCeffect eEffect, int nRacialType); + +// Set eEffect to be versus traps. +struct PRCeffect PRCVersusTrapEffect(struct PRCeffect eEffect); + +// Create a Heal effect. This should be applied as an instantaneous effect. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nDamageToHeal < 0. +struct PRCeffect PRCEffectHeal(int nDamageToHeal); + +// Create a Damage effect +// - nDamageAmount: amount of damage to be dealt. This should be applied as an +// instantaneous effect. +// - nDamageType: DAMAGE_TYPE_* +// - nDamagePower: DAMAGE_POWER_* +struct PRCeffect PRCEffectDamage(int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL); + +// Create an Ability Increase effect +// - bAbilityToIncrease: ABILITY_* +struct PRCeffect PRCEffectAbilityIncrease(int nAbilityToIncrease, int nModifyBy); + +// Create a Damage Resistance effect that removes the first nAmount points of +// damage of type nDamageType, up to nLimit (or infinite if nLimit is 0) +// - nDamageType: DAMAGE_TYPE_* +// - nAmount +// - nLimit +struct PRCeffect PRCEffectDamageResistance(int nDamageType, int nAmount, int nLimit=0); + +// Create a Summon Creature effect. The creature is created and placed into the +// caller's party/faction. +// - sCreatureResref: Identifies the creature to be summoned +// - nVisualEffectId: VFX_* +// - fDelaySeconds: There can be delay between the visual effect being played, and the +// creature being added to the area +// - nUseAppearAnimation: should this creature play it's "appear" animation when it is +// summoned. If zero, it will just fade in somewhere near the target. If the value is 1 +// it will use the appear animation, and if it's 2 it will use appear2 (which doesn't exist for most creatures) +struct PRCeffect PRCEffectSummonCreature(string sCreatureResref, int nVisualEffectId=VFX_NONE, float fDelaySeconds=0.0f, int nUseAppearAnimation=0); + +// Create a Resurrection effect. This should be applied as an instantaneous effect. +struct PRCeffect PRCEffectResurrection(); + +// Create an AC Increase effect +// - nValue: size of AC increase +// - nModifyType: AC_*_BONUS +// - nDamageType: DAMAGE_TYPE_* +// * Default value for nDamageType should only ever be used in this function prototype. +struct PRCeffect PRCEffectACIncrease(int nValue, int nModifyType=AC_DODGE_BONUS, int nDamageType=AC_VS_DAMAGE_TYPE_ALL); + +// Create a Saving Throw Increase effect +// - nSave: SAVING_THROW_* (not SAVING_THROW_TYPE_*) +// SAVING_THROW_ALL +// SAVING_THROW_FORT +// SAVING_THROW_REFLEX +// SAVING_THROW_WILL +// - nValue: size of the Saving Throw increase +// - nSaveType: SAVING_THROW_TYPE_* (e.g. SAVING_THROW_TYPE_ACID ) +struct PRCeffect PRCEffectSavingThrowIncrease(int nSave, int nValue, int nSaveType=SAVING_THROW_TYPE_ALL); + +// Create an Attack Increase effect +// - nBonus: size of attack bonus +// - nModifierType: ATTACK_BONUS_* +struct PRCeffect PRCEffectAttackIncrease(int nBonus, int nModifierType=ATTACK_BONUS_MISC); + +// Create a Damage Reduction effect +// - nAmount: amount of damage reduction +// - nDamagePower: DAMAGE_POWER_* +// - nLimit: How much damage the effect can absorb before disappearing. +// Set to zero for infinite +struct PRCeffect PRCEffectDamageReduction(int nAmount, int nDamagePower, int nLimit=0); + +// Create a Damage Increase effect +// - nBonus: DAMAGE_BONUS_* +// - nDamageType: DAMAGE_TYPE_* +// NOTE! You *must* use the DAMAGE_BONUS_* constants! Using other values may +// result in odd behaviour. +struct PRCeffect PRCEffectDamageIncrease(int nBonus, int nDamageType=DAMAGE_TYPE_MAGICAL); + + +// Create an Entangle effect +// When applied, this effect will restrict the creature's movement and apply a +// (-2) to all attacks and a -4 to AC. +struct PRCeffect PRCEffectEntangle(); + +// Create a Death effect +// - nSpectacularDeath: if this is TRUE, the creature to which this effect is +// applied will die in an extraordinary fashion +// - nDisplayFeedback +struct PRCeffect PRCEffectDeath(int nSpectacularDeath=FALSE, int nDisplayFeedback=TRUE); + +// Create a Knockdown effect +// This effect knocks creatures off their feet, they will sit until the effect +// is removed. This should be applied as a temporary effect with a 3 second +// duration minimum (1 second to fall, 1 second sitting, 1 second to get up). +struct PRCeffect PRCEffectKnockdown(); + +// Create a Curse effect. +// - nStrMod: strength modifier +// - nDexMod: dexterity modifier +// - nConMod: constitution modifier +// - nIntMod: intelligence modifier +// - nWisMod: wisdom modifier +// - nChaMod: charisma modifier +struct PRCeffect PRCEffectCurse(int nStrMod=1, int nDexMod=1, int nConMod=1, int nIntMod=1, int nWisMod=1, int nChaMod=1); + +// Create a Paralyze effect +struct PRCeffect PRCEffectParalyze(); + +// Create a Spell Immunity effect. +// There is a known bug with this function. There *must* be a parameter specified +// when this is called (even if the desired parameter is SPELL_ALL_SPELLS), +// otherwise an effect of type EFFECT_TYPE_INVALIDEFFECT will be returned. +// - nImmunityToSpell: SPELL_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nImmunityToSpell is +// invalid. +struct PRCeffect PRCEffectSpellImmunity(int nImmunityToSpell=SPELL_ALL_SPELLS); + +// Create a Deaf effect +struct PRCeffect PRCEffectDeaf(); + +// Create a Sleep effect +struct PRCeffect PRCEffectSleep(); + +// Create a Charm effect +struct PRCeffect PRCEffectCharmed(); + +// Create a Confuse effect +struct PRCeffect PRCEffectConfused(); + +// Create a Frighten effect +struct PRCeffect PRCEffectFrightened(); + +// Create a Dominate effect +struct PRCeffect PRCEffectDominated(); + +// Create a Daze effect +struct PRCeffect PRCEffectDazed(); + +// Create a Stun effect +struct PRCeffect PRCEffectStunned(); + +// Create a Regenerate effect. +// - nAmount: amount of damage to be regenerated per time interval +// - fIntervalSeconds: length of interval in seconds +struct PRCeffect PRCEffectRegenerate(int nAmount, float fIntervalSeconds); + +// Create a Movement Speed Increase effect. +// - nPercentChange - range 0 through 99 +// eg. +// 0 = no change in speed +// 50 = 50% faster +// 99 = almost twice as fast +struct PRCeffect PRCEffectMovementSpeedIncrease(int nPercentChange); + +// Create an Area Of struct PRCeffect PRCEffect in the area of the creature it is applied to. +// If the scripts are not specified, default ones will be used. +struct PRCeffect PRCEffectAreaOfEffect(int nAreaEffectId, string sOnEnterScript="", string sHeartbeatScript="", string sOnExitScript=""); + +// * Create a Visual Effect that can be applied to an object. +// - nVisualEffectId +// - nMissEffect: if this is TRUE, a random vector near or past the target will +// be generated, on which to play the effect +struct PRCeffect PRCEffectVisualEffect(int nVisualEffectId, int nMissEffect=FALSE); + +// Link the two supplied effects, returning eChildEffect as a child of +// eParentEffect. +// Note: When applying linked effects if the target is immune to all valid +// effects all other effects will be removed as well. This means that if you +// apply a visual effect and a silence effect (in a link) and the target is +// immune to the silence effect that the visual effect will get removed as well. +// Visual Effects are not considered "valid" effects for the purposes of +// determining if an effect will be removed or not and as such should never be +// packaged *only* with other visual effects in a link. +struct PRCeffect PRCEffectLinkEffects(struct PRCeffect eChildEffect, struct PRCeffect eParentEffect ); + +// Create a Beam effect. +// - nBeamVisualEffect: VFX_BEAM_* +// - oEffector: the beam is emitted from this creature +// - nBodyPart: BODY_NODE_* +// - bMissEffect: If this is TRUE, the beam will fire to a random vector near or +// past the target +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nBeamVisualEffect is +// not valid. +struct PRCeffect PRCEffectBeam(int nBeamVisualEffect, object oEffector, int nBodyPart, int bMissEffect=FALSE); + +// Create a Spell Resistance Increase effect. +// - nValue: size of spell resistance increase +struct PRCeffect PRCEffectSpellResistanceIncrease(int nValue); + +// Create a Poison effect. +// - nPoisonType: POISON_* +struct PRCeffect PRCEffectPoison(int nPoisonType); + +// Create a Disease effect. +// - nDiseaseType: DISEASE_* +struct PRCeffect PRCEffectDisease(int nDiseaseType); + +// Create a Silence effect. +struct PRCeffect PRCEffectSilence(); + +// Create a Haste effect. +struct PRCeffect PRCEffectHaste(); + +// Create a Slow effect. +struct PRCeffect PRCEffectSlow(); + +// Create an Immunity effect. +// - nImmunityType: IMMUNITY_TYPE_* +struct PRCeffect PRCEffectImmunity(int nImmunityType); + +// Creates a Damage Immunity Increase effect. +// - nDamageType: DAMAGE_TYPE_* +// - nPercentImmunity +struct PRCeffect PRCEffectDamageImmunityIncrease(int nDamageType, int nPercentImmunity); + +// Create a Temporary Hitpoints effect. +// - nHitPoints: a positive integer +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nHitPoints < 0. +struct PRCeffect PRCEffectTemporaryHitpoints(int nHitPoints); + +// Create a Skill Increase effect. +// - nSkill: SKILL_* +// - nValue +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nSkill is invalid. +struct PRCeffect PRCEffectSkillIncrease(int nSkill, int nValue); + +// Create a Turned effect. +// Turned effects are supernatural by default. +struct PRCeffect PRCEffectTurned(); + +// Create a Hit Point Change When Dying effect. +// - fHitPointChangePerRound: this can be positive or negative, but not zero. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if fHitPointChangePerRound is 0. +struct PRCeffect PRCEffectHitPointChangeWhenDying(float fHitPointChangePerRound); + +// Create an Ability Decrease effect. +// - nAbility: ABILITY_* +// - nModifyBy: This is the amount by which to decrement the ability +struct PRCeffect PRCEffectAbilityDecrease(int nAbility, int nModifyBy); + +// Create an Attack Decrease effect. +// - nPenalty +// - nModifierType: ATTACK_BONUS_* +struct PRCeffect PRCEffectAttackDecrease(int nPenalty, int nModifierType=ATTACK_BONUS_MISC); + +// Create a Damage Decrease effect. +// - nPenalty +// - nDamageType: DAMAGE_TYPE_* +struct PRCeffect PRCEffectDamageDecrease(int nPenalty, int nDamageType=DAMAGE_TYPE_MAGICAL); + +// Create a Damage Immunity Decrease effect. +// - nDamageType: DAMAGE_TYPE_* +// - nPercentImmunity +struct PRCeffect PRCEffectDamageImmunityDecrease(int nDamageType, int nPercentImmunity); + +// Create an AC Decrease effect. +// - nValue +// - nModifyType: AC_* +// - nDamageType: DAMAGE_TYPE_* +// * Default value for nDamageType should only ever be used in this function prototype. +struct PRCeffect PRCEffectACDecrease(int nValue, int nModifyType=AC_DODGE_BONUS, int nDamageType=AC_VS_DAMAGE_TYPE_ALL); + +// Create a Movement Speed Decrease effect. +// - nPercentChange - range 0 through 99 +// eg. +// 0 = no change in speed +// 50 = 50% slower +// 99 = almost immobile +struct PRCeffect PRCEffectMovementSpeedDecrease(int nPercentChange); + +// Create a Saving Throw Decrease effect. +// - nSave: SAVING_THROW_* (not SAVING_THROW_TYPE_*) +// SAVING_THROW_ALL +// SAVING_THROW_FORT +// SAVING_THROW_REFLEX +// SAVING_THROW_WILL +// - nValue: size of the Saving Throw decrease +// - nSaveType: SAVING_THROW_TYPE_* (e.g. SAVING_THROW_TYPE_ACID ) +struct PRCeffect PRCEffectSavingThrowDecrease(int nSave, int nValue, int nSaveType=SAVING_THROW_TYPE_ALL); + +// Create a Skill Decrease effect. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nSkill is invalid. +struct PRCeffect PRCEffectSkillDecrease(int nSkill, int nValue); + +// Create a Spell Resistance Decrease effect. +struct PRCeffect PRCEffectSpellResistanceDecrease(int nValue); + +// Create an Invisibility effect. +// - nInvisibilityType: INVISIBILITY_TYPE_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nInvisibilityType +// is invalid. +struct PRCeffect PRCEffectInvisibility(int nInvisibilityType); + +// Create a Concealment effect. +// - nPercentage: 1-100 inclusive +// - nMissChanceType: MISS_CHANCE_TYPE_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nPercentage < 1 or +// nPercentage > 100. +struct PRCeffect PRCEffectConcealment(int nPercentage, int nMissType=MISS_CHANCE_TYPE_NORMAL); + +// Create a Darkness effect. +struct PRCeffect PRCEffectDarkness(); + +// Create a Dispel Magic All effect. +// If no parameter is specified, USE_CREATURE_LEVEL will be used. This will +// cause the dispel effect to use the level of the creature that created the +// effect. +struct PRCeffect PRCEffectDispelMagicAll(int nCasterLevel=USE_CREATURE_LEVEL); + +// Create an Ultravision effect. +struct PRCeffect PRCEffectUltravision(); + +// Create a Negative Level effect. +// - nNumLevels: the number of negative levels to apply. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nNumLevels > 100. +struct PRCeffect PRCEffectNegativeLevel(int nNumLevels, int bHPBonus=FALSE); + +// Create a Polymorph effect. +struct PRCeffect PRCEffectPolymorph(int nPolymorphSelection, int nLocked=FALSE); + +// Create a Sanctuary effect. +// - nDifficultyClass: must be a non-zero, positive number +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nDifficultyClass <= 0. +struct PRCeffect PRCEffectSanctuary(int nDifficultyClass); + +// Create a True Seeing effect. +struct PRCeffect PRCEffectTrueSeeing(); + +// Create a See Invisible effect. +struct PRCeffect PRCEffectSeeInvisible(); + +// Create a Time Stop effect. +struct PRCeffect PRCEffectTimeStop(); + +// Create a Blindness effect. +struct PRCeffect PRCEffectBlindness(); + +// Create a Spell Level Absorption effect. +// - nMaxSpellLevelAbsorbed: maximum spell level that will be absorbed by the +// effect +// - nTotalSpellLevelsAbsorbed: maximum number of spell levels that will be +// absorbed by the effect +// - nSpellSchool: SPELL_SCHOOL_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if: +// nMaxSpellLevelAbsorbed is not between -1 and 9 inclusive, or nSpellSchool +// is invalid. +struct PRCeffect PRCEffectSpellLevelAbsorption(int nMaxSpellLevelAbsorbed, int nTotalSpellLevelsAbsorbed=0, int nSpellSchool=SPELL_SCHOOL_GENERAL ); + +// Create a Dispel Magic Best effect. +// If no parameter is specified, USE_CREATURE_LEVEL will be used. This will +// cause the dispel effect to use the level of the creature that created the +// effect. +struct PRCeffect PRCEffectDispelMagicBest(int nCasterLevel=USE_CREATURE_LEVEL); + +// Create a Miss Chance effect. +// - nPercentage: 1-100 inclusive +// - nMissChanceType: MISS_CHANCE_TYPE_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nPercentage < 1 or +// nPercentage > 100. +struct PRCeffect PRCEffectMissChance(int nPercentage, int nMissChanceType=MISS_CHANCE_TYPE_NORMAL); + +// Create a Disappear/Appear effect. +// The object will "fly away" for the duration of the effect and will reappear +// at lLocation. +// - nAnimation determines which appear and disappear animations to use. Most creatures +// only have animation 1, although a few have 2 (like beholders) +struct PRCeffect PRCEffectDisappearAppear(location lLocation, int nAnimation=1); + +// Create a Disappear effect to make the object "fly away" and then destroy +// itself. +// - nAnimation determines which appear and disappear animations to use. Most creatures +// only have animation 1, although a few have 2 (like beholders) +struct PRCeffect PRCEffectDisappear(int nAnimation=1); + +// Create an Appear effect to make the object "fly in". +// - nAnimation determines which appear and disappear animations to use. Most creatures +// only have animation 1, although a few have 2 (like beholders) +struct PRCeffect PRCEffectAppear(int nAnimation=1); + +// Create a Modify Attacks effect to add attacks. +// - nAttacks: maximum is 5, even with the effect stacked +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nAttacks > 5. +struct PRCeffect PRCEffectModifyAttacks(int nAttacks); + +// Create a Damage Shield effect which does (nDamageAmount + nRandomAmount) +// damage to any melee attacker on a successful attack of damage type nDamageType. +// - nDamageAmount: an integer value +// - nRandomAmount: DAMAGE_BONUS_* +// - nDamageType: DAMAGE_TYPE_* +// NOTE! You *must* use the DAMAGE_BONUS_* constants! Using other values may +// result in odd behaviour. +struct PRCeffect PRCEffectDamageShield(int nDamageAmount, int nRandomAmount, int nDamageType); + +// Create a Swarm effect. +// - nLooping: If this is TRUE, for the duration of the effect when one creature +// created by this effect dies, the next one in the list will be created. If +// the last creature in the list dies, we loop back to the beginning and +// sCreatureTemplate1 will be created, and so on... +// - sCreatureTemplate1 +// - sCreatureTemplate2 +// - sCreatureTemplate3 +// - sCreatureTemplate4 +struct PRCeffect PRCEffectSwarm(int nLooping, string sCreatureTemplate1, string sCreatureTemplate2="", string sCreatureTemplate3="", string sCreatureTemplate4=""); + +// Create a Turn Resistance Decrease effect. +// - nHitDice: a positive number representing the number of hit dice for the +/// decrease +struct PRCeffect PRCEffectTurnResistanceDecrease(int nHitDice); + +// Create a Turn Resistance Increase effect. +// - nHitDice: a positive number representing the number of hit dice for the +// increase +struct PRCeffect PRCEffectTurnResistanceIncrease(int nHitDice); + +// returns an effect that will petrify the target +// * currently applies EffectParalyze and the stoneskin visual effect. +struct PRCeffect PRCEffectPetrify(); + +// returns an effect that is guaranteed to paralyze a creature. +// this effect is identical to EffectParalyze except that it cannot be resisted. +struct PRCeffect PRCEffectCutsceneParalyze(); + +// Returns an effect that is guaranteed to dominate a creature +// Like EffectDominated but cannot be resisted +struct PRCeffect PRCEffectCutsceneDominated(); + +// Creates an effect that inhibits spells +// - nPercent - percentage of failure +// - nSpellSchool - the school of spells affected. +struct PRCeffect PRCEffectSpellFailure(int nPercent=100, int nSpellSchool=SPELL_SCHOOL_GENERAL); + +// Returns an effect of type EFFECT_TYPE_ETHEREAL which works just like EffectSanctuary +// except that the observers get no saving throw +struct PRCeffect PRCEffectEthereal(); + +// Creates a cutscene ghost effect, this will allow creatures +// to pathfind through other creatures without bumping into them +// for the duration of the effect. +struct PRCeffect PRCEffectCutsceneGhost(); + +// Returns an effect that when applied will paralyze the target's legs, rendering +// them unable to walk but otherwise unpenalized. This effect cannot be resisted. +struct PRCeffect PRCEffectCutsceneImmobilize(); + +// Apply eEffect at lLocation. +void PRCApplyEffectAtLocation(int nDurationType, struct PRCeffect prceEffect, location lLocation, float fDuration=0.0f); + +//#include "inc_dispel" + +void PRCApplyEffectAtLocation(int nDurationType, struct PRCeffect prceEffect, location lLocation, float fDuration=0.0f) +{ + ApplyEffectAtLocation(nDurationType, prceEffect.eEffect, lLocation, fDuration); +} + +//get/set local handlers +void SetLocalPRCEffect(object oObject, string sVarName, struct PRCeffect eValue) +{ + SetLocalInt (oObject, sVarName+".nEffectType", eValue.nEffectType); + SetLocalInt (oObject, sVarName+".nEffectSubtype", eValue.nEffectSubtype); + SetLocalInt (oObject, sVarName+".nDurationType", eValue.nDurationType); + SetLocalFloat (oObject, sVarName+".fDuration", eValue.fDuration); + SetLocalInt (oObject, sVarName+".nVersesRace", eValue.nVersesRace); + SetLocalInt (oObject, sVarName+".nVersesTraps", eValue.nVersesTraps); + SetLocalInt (oObject, sVarName+".nVersesAlignmentOrder", eValue.nVersesAlignmentOrder); + SetLocalInt (oObject, sVarName+".nVersesAlignmentMoral", eValue.nVersesAlignmentMoral); + SetLocalInt (oObject, sVarName+".nLinkedCount", eValue.nLinkedCount); + SetLocalInt (oObject, sVarName+".nLinkedID", eValue.nLinkedID); + SetLocalInt (oObject, sVarName+".nSpellID", eValue.nSpellID); + SetLocalInt (oObject, sVarName+".nCasterLevel", eValue.nCasterLevel); + SetLocalObject (oObject, sVarName+".oCaster", eValue.oCaster); + SetLocalInt (oObject, sVarName+".nMetamagic", eValue.nMetamagic); + SetLocalInt (oObject, sVarName+".nSpellLevel", eValue.nSpellLevel); + SetLocalInt (oObject, sVarName+".nWeave", eValue.nWeave); + SetLocalInt (oObject, sVarName+".nVar1", eValue.nVar1); + SetLocalInt (oObject, sVarName+".nVar2", eValue.nVar2); + SetLocalInt (oObject, sVarName+".nVar3", eValue.nVar3); + SetLocalInt (oObject, sVarName+".nVar4", eValue.nVar4); + SetLocalInt (oObject, sVarName+".nVar5", eValue.nVar5); + SetLocalInt (oObject, sVarName+".nVar6", eValue.nVar6); + SetLocalInt (oObject, sVarName+".nVar7", eValue.nVar7); + SetLocalInt (oObject, sVarName+".nVar8", eValue.nVar8); + SetLocalInt (oObject, sVarName+".nVar9", eValue.nVar9); + SetLocalObject (oObject, sVarName+".oVar1", eValue.oVar1); + SetLocalObject (oObject, sVarName+".oVar2", eValue.oVar2); + SetLocalObject (oObject, sVarName+".oVar3", eValue.oVar3); + SetLocalObject (oObject, sVarName+".oVar1", eValue.oVar4); + SetLocalObject (oObject, sVarName+".oVar5", eValue.oVar5); + SetLocalObject (oObject, sVarName+".oVar6", eValue.oVar6); + SetLocalObject (oObject, sVarName+".oVar7", eValue.oVar7); + SetLocalObject (oObject, sVarName+".oVar8", eValue.oVar8); + SetLocalObject (oObject, sVarName+".oVar9", eValue.oVar9); + SetLocalString (oObject, sVarName+".sVar1", eValue.sVar1); + SetLocalString (oObject, sVarName+".sVar2", eValue.sVar2); + SetLocalString (oObject, sVarName+".sVar3", eValue.sVar3); + SetLocalString (oObject, sVarName+".sVar4", eValue.sVar4); + SetLocalString (oObject, sVarName+".sVar5", eValue.sVar5); + SetLocalString (oObject, sVarName+".sVar6", eValue.sVar6); + SetLocalString (oObject, sVarName+".sVar7", eValue.sVar7); + SetLocalString (oObject, sVarName+".sVar8", eValue.sVar8); + SetLocalString (oObject, sVarName+".sVar9", eValue.sVar9); + SetLocalLocation(oObject, sVarName+".lVar1", eValue.lVar1); + SetLocalLocation(oObject, sVarName+".lVar2", eValue.lVar2); + SetLocalLocation(oObject, sVarName+".lVar3", eValue.lVar3); + SetLocalLocation(oObject, sVarName+".lVar4", eValue.lVar4); + SetLocalLocation(oObject, sVarName+".lVar5", eValue.lVar5); + SetLocalLocation(oObject, sVarName+".lVar6", eValue.lVar6); + SetLocalLocation(oObject, sVarName+".lVar7", eValue.lVar7); + SetLocalLocation(oObject, sVarName+".lVar8", eValue.lVar8); + SetLocalLocation(oObject, sVarName+".lVar9", eValue.lVar9); + SetLocalFloat (oObject, sVarName+".fVar1", eValue.fVar1); + SetLocalFloat (oObject, sVarName+".fVar2", eValue.fVar2); + SetLocalFloat (oObject, sVarName+".fVar3", eValue.fVar3); + SetLocalFloat (oObject, sVarName+".fVar4", eValue.fVar4); + SetLocalFloat (oObject, sVarName+".fVar5", eValue.fVar5); + SetLocalFloat (oObject, sVarName+".fVar6", eValue.fVar6); + SetLocalFloat (oObject, sVarName+".fVar7", eValue.fVar7); + SetLocalFloat (oObject, sVarName+".fVar8", eValue.fVar8); + SetLocalFloat (oObject, sVarName+".fVar9", eValue.fVar9); +} +struct PRCeffect GetLocalPRCEffect(object oObject, string sVarName) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nEffectType= GetLocalInt (oObject, sVarName+".nEffectType"); + eReturn.nEffectSubtype= GetLocalInt (oObject, sVarName+".nEffectSubtype"); + eReturn.nDurationType= GetLocalInt (oObject, sVarName+".nDurationType"); + eReturn.fDuration= GetLocalFloat (oObject, sVarName+".fDuration"); + eReturn.nVersesRace= GetLocalInt (oObject, sVarName+".nVersesRace"); + eReturn.nVersesTraps= GetLocalInt (oObject, sVarName+".nVersesTraps"); + eReturn.nVersesAlignmentOrder=GetLocalInt(oObject,sVarName+".nVersesAlignmentOrder"); + eReturn.nVersesAlignmentMoral=GetLocalInt(oObject,sVarName+".nVersesAlignmentMoral"); + eReturn.nLinkedCount= GetLocalInt (oObject, sVarName+".nLinkedCount"); + eReturn.nLinkedID= GetLocalInt (oObject, sVarName+".nLinkedID"); + eReturn.nSpellID= GetLocalInt (oObject, sVarName+".nSpellID"); + eReturn.nCasterLevel= GetLocalInt (oObject, sVarName+".nCasterLevel"); + eReturn.oCaster= GetLocalObject (oObject, sVarName+".oCaster"); + eReturn.nMetamagic= GetLocalInt (oObject, sVarName+".nMetamagic"); + eReturn.nSpellLevel= GetLocalInt (oObject, sVarName+".nSpellLevel"); + eReturn.nWeave= GetLocalInt (oObject, sVarName+".nWeave"); + eReturn.nVar1= GetLocalInt (oObject, sVarName+".nVar1"); + eReturn.nVar2= GetLocalInt (oObject, sVarName+".nVar2"); + eReturn.nVar3= GetLocalInt (oObject, sVarName+".nVar3"); + eReturn.nVar4= GetLocalInt (oObject, sVarName+".nVar4"); + eReturn.nVar5= GetLocalInt (oObject, sVarName+".nVar5"); + eReturn.nVar6= GetLocalInt (oObject, sVarName+".nVar6"); + eReturn.nVar7= GetLocalInt (oObject, sVarName+".nVar7"); + eReturn.nVar8= GetLocalInt (oObject, sVarName+".nVar8"); + eReturn.nVar9= GetLocalInt (oObject, sVarName+".nVar9"); + eReturn.oVar1= GetLocalObject (oObject, sVarName+".oVar1"); + eReturn.oVar2= GetLocalObject (oObject, sVarName+".oVar2"); + eReturn.oVar3= GetLocalObject (oObject, sVarName+".oVar3"); + eReturn.oVar4= GetLocalObject (oObject, sVarName+".oVar4"); + eReturn.oVar5= GetLocalObject (oObject, sVarName+".oVar5"); + eReturn.oVar6= GetLocalObject (oObject, sVarName+".oVar6"); + eReturn.oVar7= GetLocalObject (oObject, sVarName+".oVar7"); + eReturn.oVar8= GetLocalObject (oObject, sVarName+".oVar8"); + eReturn.oVar9= GetLocalObject (oObject, sVarName+".oVar9"); + eReturn.sVar1= GetLocalString (oObject, sVarName+".sVar1"); + eReturn.sVar2= GetLocalString (oObject, sVarName+".sVar2"); + eReturn.sVar3= GetLocalString (oObject, sVarName+".sVar3"); + eReturn.sVar4= GetLocalString (oObject, sVarName+".sVar4"); + eReturn.sVar5= GetLocalString (oObject, sVarName+".sVar5"); + eReturn.sVar6= GetLocalString (oObject, sVarName+".sVar6"); + eReturn.sVar7= GetLocalString (oObject, sVarName+".sVar7"); + eReturn.sVar8= GetLocalString (oObject, sVarName+".sVar8"); + eReturn.sVar9= GetLocalString (oObject, sVarName+".sVar9"); + eReturn.lVar1= GetLocalLocation(oObject, sVarName+".lVar1"); + eReturn.lVar2= GetLocalLocation(oObject, sVarName+".lVar2"); + eReturn.lVar3= GetLocalLocation(oObject, sVarName+".lVar3"); + eReturn.lVar4= GetLocalLocation(oObject, sVarName+".lVar4"); + eReturn.lVar5= GetLocalLocation(oObject, sVarName+".lVar5"); + eReturn.lVar6= GetLocalLocation(oObject, sVarName+".lVar6"); + eReturn.lVar7= GetLocalLocation(oObject, sVarName+".lVar7"); + eReturn.lVar8= GetLocalLocation(oObject, sVarName+".lVar8"); + eReturn.lVar9= GetLocalLocation(oObject, sVarName+".lVar9"); + eReturn.fVar1= GetLocalFloat (oObject, sVarName+".fVar1"); + eReturn.fVar2= GetLocalFloat (oObject, sVarName+".fVar2"); + eReturn.fVar3= GetLocalFloat (oObject, sVarName+".fVar3"); + eReturn.fVar4= GetLocalFloat (oObject, sVarName+".fVar4"); + eReturn.fVar5= GetLocalFloat (oObject, sVarName+".fVar5"); + eReturn.fVar6= GetLocalFloat (oObject, sVarName+".fVar6"); + eReturn.fVar7= GetLocalFloat (oObject, sVarName+".fVar7"); + eReturn.fVar8= GetLocalFloat (oObject, sVarName+".fVar8"); + eReturn.fVar9= GetLocalFloat (oObject, sVarName+".fVar9"); + return eReturn; +} + +void DeleteLocalPRCEffect(object oObject, string sVarName) +{ + DeleteLocalInt (oObject, sVarName+".nEffectType"); + DeleteLocalInt (oObject, sVarName+".nEffectSubtype"); + DeleteLocalInt (oObject, sVarName+".nDurationType"); + DeleteLocalFloat (oObject, sVarName+".fDuration"); + DeleteLocalInt (oObject, sVarName+".nVersesRace"); + DeleteLocalInt (oObject, sVarName+".nVersesTraps"); + DeleteLocalInt (oObject, sVarName+".nVersesAlignmentOrder"); + DeleteLocalInt (oObject, sVarName+".nVersesAlignmentMoral"); + DeleteLocalInt (oObject, sVarName+".nLinkedCount"); + DeleteLocalInt (oObject, sVarName+".nLinkedID"); + DeleteLocalInt (oObject, sVarName+".nSpellID"); + DeleteLocalInt (oObject, sVarName+".nCasterLevel"); + DeleteLocalObject (oObject, sVarName+".oCaster"); + DeleteLocalInt (oObject, sVarName+".nMetamagic"); + DeleteLocalInt (oObject, sVarName+".nSpellLevel"); + DeleteLocalInt (oObject, sVarName+".nWeave"); + DeleteLocalInt (oObject, sVarName+".nVar1"); + DeleteLocalInt (oObject, sVarName+".nVar2"); + DeleteLocalInt (oObject, sVarName+".nVar3"); + DeleteLocalInt (oObject, sVarName+".nVar4"); + DeleteLocalInt (oObject, sVarName+".nVar5"); + DeleteLocalInt (oObject, sVarName+".nVar6"); + DeleteLocalInt (oObject, sVarName+".nVar7"); + DeleteLocalInt (oObject, sVarName+".nVar8"); + DeleteLocalInt (oObject, sVarName+".nVar9"); + DeleteLocalObject (oObject, sVarName+".oVar1"); + DeleteLocalObject (oObject, sVarName+".oVar2"); + DeleteLocalObject (oObject, sVarName+".oVar3"); + DeleteLocalObject (oObject, sVarName+".oVar1"); + DeleteLocalObject (oObject, sVarName+".oVar5"); + DeleteLocalObject (oObject, sVarName+".oVar6"); + DeleteLocalObject (oObject, sVarName+".oVar7"); + DeleteLocalObject (oObject, sVarName+".oVar8"); + DeleteLocalObject (oObject, sVarName+".oVar9"); + DeleteLocalString (oObject, sVarName+".sVar1"); + DeleteLocalString (oObject, sVarName+".sVar2"); + DeleteLocalString (oObject, sVarName+".sVar3"); + DeleteLocalString (oObject, sVarName+".sVar4"); + DeleteLocalString (oObject, sVarName+".sVar5"); + DeleteLocalString (oObject, sVarName+".sVar6"); + DeleteLocalString (oObject, sVarName+".sVar7"); + DeleteLocalString (oObject, sVarName+".sVar8"); + DeleteLocalString (oObject, sVarName+".sVar9"); + DeleteLocalLocation(oObject, sVarName+".lVar1"); + DeleteLocalLocation(oObject, sVarName+".lVar2"); + DeleteLocalLocation(oObject, sVarName+".lVar3"); + DeleteLocalLocation(oObject, sVarName+".lVar4"); + DeleteLocalLocation(oObject, sVarName+".lVar5"); + DeleteLocalLocation(oObject, sVarName+".lVar6"); + DeleteLocalLocation(oObject, sVarName+".lVar7"); + DeleteLocalLocation(oObject, sVarName+".lVar8"); + DeleteLocalLocation(oObject, sVarName+".lVar9"); + DeleteLocalFloat (oObject, sVarName+".fVar1"); + DeleteLocalFloat (oObject, sVarName+".fVar2"); + DeleteLocalFloat (oObject, sVarName+".fVar3"); + DeleteLocalFloat (oObject, sVarName+".fVar4"); + DeleteLocalFloat (oObject, sVarName+".fVar5"); + DeleteLocalFloat (oObject, sVarName+".fVar6"); + DeleteLocalFloat (oObject, sVarName+".fVar7"); + DeleteLocalFloat (oObject, sVarName+".fVar8"); + DeleteLocalFloat (oObject, sVarName+".fVar9"); + +} + +//default constructor +struct PRCeffect GetNewPRCEffectBase() +{ + //not sure if anything needs to be here at the moment + struct PRCeffect eReturn; + return eReturn; +} + +//new effect-related functions +string GetIdentifierFromEffect(effect eEffect) +{ + string sReturn; + sReturn += IntToString(GetEffectType(eEffect))+"_"; + sReturn += IntToString(GetEffectSubType(eEffect))+"_"; + sReturn += ObjectToString(GetEffectCreator(eEffect))+"_"; + sReturn += IntToString(GetEffectSpellId(eEffect))+"_"; + sReturn += IntToString(GetEffectDurationType(eEffect)); + return sReturn; +} + +//replacements of the bioware functions +//just one or two here + +// Get the first in-game effect on oCreature. +struct PRCeffect PRCGetFirstEffect(object oCreature) +{ + effect eEffect = GetFirstEffect(oCreature); + string sID = GetIdentifierFromEffect(eEffect); + struct PRCeffect prceEffect = GetLocalPRCEffect(oCreature, sID); + prceEffect.eEffect = eEffect; + return prceEffect; +} + +// Get the next in-game effect on oCreature. +struct PRCeffect PRCGetNextEffect(object oCreature) +{ + effect eEffect = GetNextEffect(oCreature); + string sID = GetIdentifierFromEffect(eEffect); + struct PRCeffect prceEffect = GetLocalPRCEffect(oCreature, sID); + prceEffect.eEffect = eEffect; + return prceEffect; +} + +// * Returns TRUE if eEffect is a valid effect. The effect must have been applied to +// * an object or else it will return FALSE +int PRCGetIsEffectValid(struct PRCeffect prceEffect) +{ + return GetIsEffectValid(prceEffect.eEffect); +} + +effect GetEffectOnObjectFromPRCEffect(struct PRCeffect prceEffect, object oObject) +{ + effect eTest; + //quick check, no loop + if(!GetHasSpellEffect(prceEffect.nSpellID, oObject)) + return eTest; + + eTest = GetFirstEffect(oObject); + while(GetIsEffectValid(eTest)) + { + if(GetEffectType(eTest) == prceEffect.nEffectType + && GetEffectSubType(eTest) == prceEffect.nEffectSubtype + && GetEffectCreator(eTest) == prceEffect.oCaster + && GetEffectSpellId(eTest) == prceEffect.nSpellID + && GetEffectDurationType(eTest) == prceEffect.nDurationType) + return eTest; + eTest = GetNextEffect(oObject); + } + return eTest; +} + +// Remove eEffect from oCreature. +// * No return value +void PRCRemoveEffect(object oCreature, struct PRCeffect eEffect) +{ + + +} + +// Get the duration type (DURATION_TYPE_*) of eEffect. +// * Return value if eEffect is not valid: -1 +int PRCGetEffectDurationType(struct PRCeffect prceEffect) +{ + return prceEffect.nDurationType; +} + +// Get the subtype (SUBTYPE_*) of eEffect. +// * Return value on error: 0 +int PRCGetEffectSubType(struct PRCeffect prceEffect) +{ + return prceEffect.nEffectSubtype; +} + +// Get the object that created eEffect. +// * Returns OBJECT_INVALID if eEffect is not a valid effect. +object PRCGetEffectCreator(struct PRCeffect prceEffect) +{ + return prceEffect.oCaster; +} + +// Get the effect type (EFFECT_TYPE_*) of eEffect. +// * Return value if eEffect is invalid: EFFECT_INVALIDEFFECT +int PRCGetEffectType(struct PRCeffect prceEffect) +{ + return prceEffect.nEffectType; +} + +// Get the spell (SPELL_*) that applied eSpellEffect. +// * Returns -1 if eSpellEffect was applied outside a spell script. +int PRCGetEffectSpellId(struct PRCeffect prceEffect) +{ + return prceEffect.nSpellID; +} + +// Set the subtype of eEffect to Magical and return eEffect. +// (Effects default to magical if the subtype is not set) +// Magical effects are removed by resting, and by dispel magic +struct PRCeffect PRCMagicalEffect(struct PRCeffect eEffect) +{ + eEffect.nEffectSubtype = SUBTYPE_MAGICAL; + eEffect.eEffect = MagicalEffect(eEffect.eEffect); + return eEffect; +} + +// Set the subtype of eEffect to Supernatural and return eEffect. +// (Effects default to magical if the subtype is not set) +// Permanent supernatural effects are not removed by resting +struct PRCeffect PRCSupernaturalEffect(struct PRCeffect eEffect) +{ + eEffect.nEffectSubtype = SUBTYPE_SUPERNATURAL; + eEffect.eEffect = SupernaturalEffect(eEffect.eEffect); + return eEffect; +} + +// Set the subtype of eEffect to Extraordinary and return eEffect. +// (Effects default to magical if the subtype is not set) +// Extraordinary effects are removed by resting, but not by dispel magic +struct PRCeffect PRCExtraordinaryEffect(struct PRCeffect eEffect) +{ + eEffect.nEffectSubtype = SUBTYPE_EXTRAORDINARY; + eEffect.eEffect = ExtraordinaryEffect(eEffect.eEffect); + return eEffect; +} + + +// Set eEffect to be versus a specific alignment. +// - eEffect +// - nLawChaos: ALIGNMENT_LAWFUL/ALIGNMENT_CHAOTIC/ALIGNMENT_ALL +// - nGoodEvil: ALIGNMENT_GOOD/ALIGNMENT_EVIL/ALIGNMENT_ALL +struct PRCeffect PRCVersusAlignmentEffect(struct PRCeffect eEffect, int nLawChaos=ALIGNMENT_ALL, int nGoodEvil=ALIGNMENT_ALL) +{ + eEffect.nVersesAlignmentOrder = nLawChaos; + eEffect.nVersesAlignmentMoral = nGoodEvil; + eEffect.eEffect = VersusAlignmentEffect(eEffect.eEffect, nLawChaos, nGoodEvil); + return eEffect; +} + +// Set eEffect to be versus nRacialType. +// - eEffect +// - nRacialType: RACIAL_TYPE_* +struct PRCeffect PRCVersusRacialTypeEffect(struct PRCeffect eEffect, int nRacialType) +{ + eEffect.nVersesRace = nRacialType; + eEffect.eEffect = VersusRacialTypeEffect(eEffect.eEffect, nRacialType); + return eEffect; +} + +// Set eEffect to be versus traps. +struct PRCeffect PRCVersusTrapEffect(struct PRCeffect eEffect) +{ + eEffect.nVersesTraps = TRUE; + eEffect.eEffect = VersusTrapEffect(eEffect.eEffect); + return eEffect; +} + + +//actual constructors + +// Create a Heal effect. This should be applied as an instantaneous effect. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nDamageToHeal < 0. +struct PRCeffect PRCEffectHeal(int nDamageToHeal) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDamageToHeal; + eReturn.eEffect = EffectHeal(nDamageToHeal); + return eReturn; +} + +// Create a Damage effect +// - nDamageAmount: amount of damage to be dealt. This should be applied as an +// instantaneous effect. +// - nDamageType: DAMAGE_TYPE_* +// - nDamagePower: DAMAGE_POWER_* +struct PRCeffect PRCEffectDamage(int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDamageAmount; + eReturn.nVar2 = nDamageType; + eReturn.nVar3 = nDamagePower; + eReturn.eEffect = EffectDamage(nDamageAmount,nDamageType,nDamagePower); + return eReturn; +} + +// Create an Ability Increase effect +// - bAbilityToIncrease: ABILITY_* +struct PRCeffect PRCEffectAbilityIncrease(int nAbilityToIncrease, int nModifyBy) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAbilityToIncrease; + eReturn.nVar2 = nModifyBy; + eReturn.eEffect = EffectAbilityIncrease(nAbilityToIncrease,nModifyBy); + return eReturn; +} + +// Create a Damage Resistance effect that removes the first nAmount points of +// damage of type nDamageType, up to nLimit (or infinite if nLimit is 0) +// - nDamageType: DAMAGE_TYPE_* +// - nAmount +// - nLimit +struct PRCeffect PRCEffectDamageResistance(int nDamageType, int nAmount, int nLimit=0) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDamageType; + eReturn.nVar2 = nAmount; + eReturn.nVar3 = nLimit; + eReturn.eEffect = EffectDamageResistance(nDamageType,nAmount,nLimit); + return eReturn; +} + +// Create a Resurrection effect. This should be applied as an instantaneous effect. +struct PRCeffect PRCEffectResurrection() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectResurrection(); + return eReturn; +} + +// Create a Summon Creature effect. The creature is created and placed into the +// caller's party/faction. +// - sCreatureResref: Identifies the creature to be summoned +// - nVisualEffectId: VFX_* +// - fDelaySeconds: There can be delay between the visual effect being played, and the +// creature being added to the area +// - nUseAppearAnimation: should this creature play it's "appear" animation when it is +// summoned. If zero, it will just fade in somewhere near the target. If the value is 1 +// it will use the appear animation, and if it's 2 it will use appear2 (which doesn't exist for most creatures) +struct PRCeffect PRCEffectSummonCreature(string sCreatureResref, int nVisualEffectId=VFX_NONE, float fDelaySeconds=0.0f, int nUseAppearAnimation=0) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.sVar1 = sCreatureResref; + eReturn.nVar1 = nVisualEffectId; + eReturn.fVar1 = fDelaySeconds; + eReturn.nVar2 = nUseAppearAnimation; + eReturn.eEffect = EffectSummonCreature(sCreatureResref,nVisualEffectId,fDelaySeconds,nUseAppearAnimation); + return eReturn; +} + +// Create an AC Increase effect +// - nValue: size of AC increase +// - nModifyType: AC_*_BONUS +// - nDamageType: DAMAGE_TYPE_* +// * Default value for nDamageType should only ever be used in this function prototype. +struct PRCeffect PRCEffectACIncrease(int nValue, int nModifyType=AC_DODGE_BONUS, int nDamageType=AC_VS_DAMAGE_TYPE_ALL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nValue; + eReturn.nVar2 = nModifyType; + eReturn.nVar3 = nDamageType; + eReturn.eEffect = EffectACIncrease(nValue,nModifyType,nDamageType); + return eReturn; +} + +// Create a Saving Throw Increase effect +// - nSave: SAVING_THROW_* (not SAVING_THROW_TYPE_*) +// SAVING_THROW_ALL +// SAVING_THROW_FORT +// SAVING_THROW_REFLEX +// SAVING_THROW_WILL +// - nValue: size of the Saving Throw increase +// - nSaveType: SAVING_THROW_TYPE_* (e.g. SAVING_THROW_TYPE_ACID ) +struct PRCeffect PRCEffectSavingThrowIncrease(int nSave, int nValue, int nSaveType=SAVING_THROW_TYPE_ALL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nSave; + eReturn.nVar2 = nValue; + eReturn.nVar3 = nSaveType; + eReturn.eEffect = EffectSavingThrowIncrease(nSave,nValue,nSaveType); + return eReturn; +} + +// Create an Attack Increase effect +// - nBonus: size of attack bonus +// - nModifierType: ATTACK_BONUS_* +struct PRCeffect PRCEffectAttackIncrease(int nBonus, int nModifierType=ATTACK_BONUS_MISC) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nBonus; + eReturn.nVar2 = nModifierType; + eReturn.eEffect = EffectAttackIncrease(nBonus,nModifierType); + return eReturn; +} + +// Create a Damage Reduction effect +// - nAmount: amount of damage reduction +// - nDamagePower: DAMAGE_POWER_* +// - nLimit: How much damage the effect can absorb before disappearing. +// Set to zero for infinite +struct PRCeffect PRCEffectDamageReduction(int nAmount, int nDamagePower, int nLimit=0) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAmount; + eReturn.nVar2 = nDamagePower; + eReturn.nVar3 = nLimit; + eReturn.eEffect = EffectDamageReduction(nAmount,nDamagePower,nLimit); + return eReturn; +} + +// Create a Damage Increase effect +// - nBonus: DAMAGE_BONUS_* +// - nDamageType: DAMAGE_TYPE_* +// NOTE! You *must* use the DAMAGE_BONUS_* constants! Using other values may +// result in odd behaviour. +struct PRCeffect PRCEffectDamageIncrease(int nBonus, int nDamageType=DAMAGE_TYPE_MAGICAL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nBonus; + eReturn.nVar2 = nDamageType; + eReturn.eEffect = EffectDamageIncrease(nBonus,nDamageType); + return eReturn; +} + + +// Create an Entangle effect +// When applied, this effect will restrict the creature's movement and apply a +// (-2) to all attacks and a -4 to AC. +struct PRCeffect PRCEffectEntangle() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectEntangle(); + return eReturn; +} + +// Create a Death effect +// - nSpectacularDeath: if this is TRUE, the creature to which this effect is +// applied will die in an extraordinary fashion +// - nDisplayFeedback +struct PRCeffect PRCEffectDeath(int nSpectacularDeath=FALSE, int nDisplayFeedback=TRUE) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nSpectacularDeath; + eReturn.nVar2 = nDisplayFeedback; + eReturn.eEffect = EffectDeath(nSpectacularDeath,nDisplayFeedback); + return eReturn; +} + +// Create a Knockdown effect +// This effect knocks creatures off their feet, they will sit until the effect +// is removed. This should be applied as a temporary effect with a 3 second +// duration minimum (1 second to fall, 1 second sitting, 1 second to get up). +struct PRCeffect PRCEffectKnockdown() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectKnockdown(); + return eReturn; +} + +// Create a Curse effect. +// - nStrMod: strength modifier +// - nDexMod: dexterity modifier +// - nConMod: constitution modifier +// - nIntMod: intelligence modifier +// - nWisMod: wisdom modifier +// - nChaMod: charisma modifier +struct PRCeffect PRCEffectCurse(int nStrMod=1, int nDexMod=1, int nConMod=1, int nIntMod=1, int nWisMod=1, int nChaMod=1) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nStrMod; + eReturn.nVar2 = nDexMod; + eReturn.nVar3 = nConMod; + eReturn.nVar4 = nIntMod; + eReturn.nVar5 = nWisMod; + eReturn.nVar6 = nChaMod; + eReturn.eEffect = EffectCurse(nStrMod,nDexMod,nConMod,nIntMod,nWisMod,nChaMod); + return eReturn; +} + +// Create a Paralyze effect +struct PRCeffect PRCEffectParalyze() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectParalyze(); + return eReturn; +} + +// Create a Spell Immunity effect. +// There is a known bug with this function. There *must* be a parameter specified +// when this is called (even if the desired parameter is SPELL_ALL_SPELLS), +// otherwise an effect of type EFFECT_TYPE_INVALIDEFFECT will be returned. +// - nImmunityToSpell: SPELL_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nImmunityToSpell is +// invalid. +struct PRCeffect PRCEffectSpellImmunity(int nImmunityToSpell=SPELL_ALL_SPELLS) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nImmunityToSpell; + eReturn.eEffect = EffectSpellImmunity(nImmunityToSpell); + return eReturn; +} + +// Create a Deaf effect +struct PRCeffect PRCEffectDeaf() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectDeaf(); + return eReturn; +} + +// Create a Sleep effect +struct PRCeffect PRCEffectSleep() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectSleep(); + return eReturn; +} + +// Create a Charm effect +struct PRCeffect PRCEffectCharmed() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectCharmed(); + return eReturn; +} + +// Create a Confuse effect +struct PRCeffect PRCEffectConfused() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectConfused(); + return eReturn; +} + +// Create a Frighten effect +struct PRCeffect PRCEffectFrightened() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectFrightened(); + return eReturn; +} + +// Create a Dominate effect +struct PRCeffect PRCEffectDominated() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectDominated(); + return eReturn; +} + +// Create a Daze effect +struct PRCeffect PRCEffectDazed() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectDazed(); + return eReturn; +} + +// Create a Stun effect +struct PRCeffect PRCEffectStunned() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectSilence(); + return eReturn; +} + +// Create a Regenerate effect. +// - nAmount: amount of damage to be regenerated per time interval +// - fIntervalSeconds: length of interval in seconds +struct PRCeffect PRCEffectRegenerate(int nAmount, float fIntervalSeconds) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAmount; + eReturn.fVar1 = fIntervalSeconds; + eReturn.eEffect = EffectRegenerate(nAmount,fIntervalSeconds); + return eReturn; +} + +// Create a Movement Speed Increase effect. +// - nPercentChange - range 0 through 99 +// eg. +// 0 = no change in speed +// 50 = 50% faster +// 99 = almost twice as fast +struct PRCeffect PRCEffectMovementSpeedIncrease(int nPercentChange) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPercentChange; + eReturn.eEffect = EffectMovementSpeedIncrease(nPercentChange); + return eReturn; +} + +// Create an Area Of struct PRCeffect PRCEffect in the area of the creature it is applied to. +// If the scripts are not specified, default ones will be used. +struct PRCeffect PRCEffectAreaOfEffect(int nAreaEffectId, string sOnEnterScript="", string sHeartbeatScript="", string sOnExitScript="") +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAreaEffectId; + eReturn.sVar1 = sOnEnterScript; + eReturn.sVar2 = sHeartbeatScript; + eReturn.sVar3 = sOnExitScript; + eReturn.eEffect = EffectAreaOfEffect(nAreaEffectId,sOnEnterScript,sHeartbeatScript, sOnExitScript); + return eReturn; +} + +// * Create a Visual Effect that can be applied to an object. +// - nVisualEffectId +// - nMissEffect: if this is TRUE, a random vector near or past the target will +// be generated, on which to play the effect +struct PRCeffect PRCEffectVisualEffect(int nVisualEffectId, int nMissEffect=FALSE) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nVisualEffectId; + eReturn.nVar2 = nMissEffect; + eReturn.eEffect = EffectVisualEffect(nVisualEffectId,nMissEffect); + return eReturn; +} + +// Link the two supplied effects, returning eChildEffect as a child of +// eParentEffect. +// Note: When applying linked effects if the target is immune to all valid +// effects all other effects will be removed as well. This means that if you +// apply a visual effect and a silence effect (in a link) and the target is +// immune to the silence effect that the visual effect will get removed as well. +// Visual Effects are not considered "valid" effects for the purposes of +// determining if an effect will be removed or not and as such should never be +// packaged *only* with other visual effects in a link. +struct PRCeffect PRCEffectLinkEffects(struct PRCeffect eChildEffect, struct PRCeffect eParentEffect ) +{ + //need to actually do something here + eParentEffect.eEffect = EffectLinkEffects(eChildEffect.eEffect, eParentEffect.eEffect); + return eParentEffect; +} + +// Create a Beam effect. +// - nBeamVisualEffect: VFX_BEAM_* +// - oEffector: the beam is emitted from this creature +// - nBodyPart: BODY_NODE_* +// - bMissEffect: If this is TRUE, the beam will fire to a random vector near or +// past the target +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nBeamVisualEffect is +// not valid. +struct PRCeffect PRCEffectBeam(int nBeamVisualEffect, object oEffector, int nBodyPart, int bMissEffect=FALSE) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nBeamVisualEffect; + eReturn.oVar1 = oEffector; + eReturn.nVar2 = nBodyPart; + eReturn.nVar3 = bMissEffect; + eReturn.eEffect = EffectBeam(nBeamVisualEffect, oEffector, nBodyPart, bMissEffect); + return eReturn; +} + +// Create a Spell Resistance Increase effect. +// - nValue: size of spell resistance increase +struct PRCeffect PRCEffectSpellResistanceIncrease(int nValue) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nValue; + eReturn.eEffect = EffectSpellResistanceIncrease(nValue); + return eReturn; +} + +// Create a Poison effect. +// - nPoisonType: POISON_* +struct PRCeffect PRCEffectPoison(int nPoisonType) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPoisonType; + eReturn.eEffect = EffectPoison(nPoisonType); + return eReturn; +} + +// Create a Disease effect. +// - nDiseaseType: DISEASE_* +struct PRCeffect PRCEffectDisease(int nDiseaseType) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDiseaseType; + eReturn.eEffect = EffectDisease(nDiseaseType); + return eReturn; +} + +// Create a Silence effect. +struct PRCeffect PRCEffectSilence() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectSilence(); + return eReturn; +} + +// Create a Haste effect. +struct PRCeffect PRCEffectHaste() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectHaste(); + return eReturn; +} + +// Create a Slow effect. +struct PRCeffect PRCEffectSlow() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectSlow(); + return eReturn; +} + +// Create an Immunity effect. +// - nImmunityType: IMMUNITY_TYPE_* +struct PRCeffect PRCEffectImmunity(int nImmunityType) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nImmunityType; + eReturn.eEffect = EffectImmunity(nImmunityType); + return eReturn; +} + +// Creates a Damage Immunity Increase effect. +// - nDamageType: DAMAGE_TYPE_* +// - nPercentImmunity +struct PRCeffect PRCEffectDamageImmunityIncrease(int nDamageType, int nPercentImmunity) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDamageType; + eReturn.nVar2 = nPercentImmunity; + eReturn.eEffect = EffectDamageImmunityIncrease(nDamageType,nPercentImmunity); + return eReturn; +} + +// Create a Temporary Hitpoints effect. +// - nHitPoints: a positive integer +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nHitPoints < 0. +struct PRCeffect PRCEffectTemporaryHitpoints(int nHitPoints) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nHitPoints; + eReturn.eEffect = EffectTemporaryHitpoints(nHitPoints); + return eReturn; +} + +// Create a Skill Increase effect. +// - nSkill: SKILL_* +// - nValue +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nSkill is invalid. +struct PRCeffect PRCEffectSkillIncrease(int nSkill, int nValue) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nSkill; + eReturn.nVar2 = nValue; + eReturn.eEffect = EffectSkillIncrease(nSkill,nValue); + return eReturn; +} + +// Create a Turned effect. +// Turned effects are supernatural by default. +struct PRCeffect PRCEffectTurned() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectTurned(); + return eReturn; +} + +// Create a Hit Point Change When Dying effect. +// - fHitPointChangePerRound: this can be positive or negative, but not zero. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if fHitPointChangePerRound is 0. +struct PRCeffect PRCEffectHitPointChangeWhenDying(float fHitPointChangePerRound) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.fVar1 = fHitPointChangePerRound; + eReturn.eEffect = EffectHitPointChangeWhenDying(fHitPointChangePerRound); + return eReturn; +} + +// Create an Ability Decrease effect. +// - nAbility: ABILITY_* +// - nModifyBy: This is the amount by which to decrement the ability +struct PRCeffect PRCEffectAbilityDecrease(int nAbility, int nModifyBy) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAbility; + eReturn.nVar2 = nModifyBy; + eReturn.eEffect = EffectAbilityDecrease(nAbility,nModifyBy); + return eReturn; +} + +// Create an Attack Decrease effect. +// - nPenalty +// - nModifierType: ATTACK_BONUS_* +struct PRCeffect PRCEffectAttackDecrease(int nPenalty, int nModifierType=ATTACK_BONUS_MISC) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPenalty; + eReturn.nVar2 = nModifierType; + eReturn.eEffect = EffectAttackDecrease(nPenalty,nModifierType); + return eReturn; +} + +// Create a Damage Decrease effect. +// - nPenalty +// - nDamageType: DAMAGE_TYPE_* +struct PRCeffect PRCEffectDamageDecrease(int nPenalty, int nDamageType=DAMAGE_TYPE_MAGICAL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPenalty; + eReturn.nVar2 = nDamageType; + eReturn.eEffect = EffectDamageDecrease(nPenalty,nDamageType); + return eReturn; +} + +// Create a Damage Immunity Decrease effect. +// - nDamageType: DAMAGE_TYPE_* +// - nPercentImmunity +struct PRCeffect PRCEffectDamageImmunityDecrease(int nDamageType, int nPercentImmunity) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDamageType; + eReturn.nVar2 = nPercentImmunity; + eReturn.eEffect = EffectDamageImmunityDecrease(nDamageType,nPercentImmunity); + return eReturn; +} + +// Create an AC Decrease effect. +// - nValue +// - nModifyType: AC_* +// - nDamageType: DAMAGE_TYPE_* +// * Default value for nDamageType should only ever be used in this function prototype. +struct PRCeffect PRCEffectACDecrease(int nValue, int nModifyType=AC_DODGE_BONUS, int nDamageType=AC_VS_DAMAGE_TYPE_ALL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nValue; + eReturn.nVar2 = nModifyType; + eReturn.nVar3 = nDamageType; + eReturn.eEffect = EffectACDecrease(nValue,nModifyType,nDamageType); + return eReturn; +} + +// Create a Movement Speed Decrease effect. +// - nPercentChange - range 0 through 99 +// eg. +// 0 = no change in speed +// 50 = 50% slower +// 99 = almost immobile +struct PRCeffect PRCEffectMovementSpeedDecrease(int nPercentChange) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPercentChange; + eReturn.eEffect = EffectMovementSpeedIncrease(nPercentChange); + return eReturn; +} + +// Create a Saving Throw Decrease effect. +// - nSave: SAVING_THROW_* (not SAVING_THROW_TYPE_*) +// SAVING_THROW_ALL +// SAVING_THROW_FORT +// SAVING_THROW_REFLEX +// SAVING_THROW_WILL +// - nValue: size of the Saving Throw decrease +// - nSaveType: SAVING_THROW_TYPE_* (e.g. SAVING_THROW_TYPE_ACID ) +struct PRCeffect PRCEffectSavingThrowDecrease(int nSave, int nValue, int nSaveType=SAVING_THROW_TYPE_ALL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nSave; + eReturn.nVar2 = nValue; + eReturn.nVar3 = nSaveType; + eReturn.eEffect = EffectSavingThrowDecrease(nSave,nValue,nSaveType); + return eReturn; +} + +// Create a Skill Decrease effect. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nSkill is invalid. +struct PRCeffect PRCEffectSkillDecrease(int nSkill, int nValue) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nSkill; + eReturn.nVar2 = nValue; + eReturn.eEffect = EffectSkillDecrease(nSkill,nValue); + return eReturn; +} + +// Create a Spell Resistance Decrease effect. +struct PRCeffect PRCEffectSpellResistanceDecrease(int nValue) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nValue; + eReturn.eEffect = EffectSpellResistanceDecrease(nValue); + return eReturn; +} + +// Create an Invisibility effect. +// - nInvisibilityType: INVISIBILITY_TYPE_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nInvisibilityType +// is invalid. +struct PRCeffect PRCEffectInvisibility(int nInvisibilityType) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nInvisibilityType; + eReturn.eEffect = EffectInvisibility(nInvisibilityType); + return eReturn; +} + +// Create a Concealment effect. +// - nPercentage: 1-100 inclusive +// - nMissChanceType: MISS_CHANCE_TYPE_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nPercentage < 1 or +// nPercentage > 100. +struct PRCeffect PRCEffectConcealment(int nPercentage, int nMissType=MISS_CHANCE_TYPE_NORMAL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPercentage; + eReturn.nVar2 = nMissType; + eReturn.eEffect = EffectConcealment(nPercentage,nMissType); + return eReturn; +} + +// Create a Darkness effect. +struct PRCeffect PRCEffectDarkness() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectDarkness(); + return eReturn; +} + +// Create a Dispel Magic All effect. +// If no parameter is specified, USE_CREATURE_LEVEL will be used. This will +// cause the dispel effect to use the level of the creature that created the +// effect. +struct PRCeffect PRCEffectDispelMagicAll(int nCasterLevel=USE_CREATURE_LEVEL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nCasterLevel; + eReturn.eEffect = EffectDispelMagicAll(nCasterLevel); + return eReturn; +} + +// Create an Ultravision effect. +struct PRCeffect PRCEffectUltravision() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectUltravision(); + return eReturn; +} + +// Create a Negative Level effect. +// - nNumLevels: the number of negative levels to apply. +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nNumLevels > 100. +struct PRCeffect PRCEffectNegativeLevel(int nNumLevels, int bHPBonus=FALSE) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nNumLevels; + eReturn.nVar2 = bHPBonus; + eReturn.eEffect = EffectNegativeLevel(nNumLevels,bHPBonus); + return eReturn; +} + +// Create a Polymorph effect. +struct PRCeffect PRCEffectPolymorph(int nPolymorphSelection, int nLocked=FALSE) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPolymorphSelection; + eReturn.nVar2 = nLocked; + eReturn.eEffect = EffectPolymorph(nPolymorphSelection,nLocked); + return eReturn; +} + +// Create a Sanctuary effect. +// - nDifficultyClass: must be a non-zero, positive number +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nDifficultyClass <= 0. +struct PRCeffect PRCEffectSanctuary(int nDifficultyClass) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDifficultyClass; + eReturn.eEffect = EffectSanctuary(nDifficultyClass); + return eReturn; +} + +// Create a True Seeing effect. +struct PRCeffect PRCEffectTrueSeeing() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectTrueSeeing(); + return eReturn; +} + +// Create a See Invisible effect. +struct PRCeffect PRCEffectSeeInvisible() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectSeeInvisible(); + return eReturn; +} + +// Create a Time Stop effect. +struct PRCeffect PRCEffectTimeStop() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectTimeStop(); + return eReturn; +} + +// Create a Blindness effect. +struct PRCeffect PRCEffectBlindness() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectBlindness(); + return eReturn; +} + +// Create a Spell Level Absorption effect. +// - nMaxSpellLevelAbsorbed: maximum spell level that will be absorbed by the +// effect +// - nTotalSpellLevelsAbsorbed: maximum number of spell levels that will be +// absorbed by the effect +// - nSpellSchool: SPELL_SCHOOL_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if: +// nMaxSpellLevelAbsorbed is not between -1 and 9 inclusive, or nSpellSchool +// is invalid. +struct PRCeffect PRCEffectSpellLevelAbsorption(int nMaxSpellLevelAbsorbed, int nTotalSpellLevelsAbsorbed=0, int nSpellSchool=SPELL_SCHOOL_GENERAL ) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nMaxSpellLevelAbsorbed; + eReturn.nVar2 = nTotalSpellLevelsAbsorbed; + eReturn.nVar3 = nSpellSchool; + eReturn.eEffect = EffectSpellLevelAbsorption(nMaxSpellLevelAbsorbed,nTotalSpellLevelsAbsorbed,nSpellSchool); + return eReturn; +} + +// Create a Dispel Magic Best effect. +// If no parameter is specified, USE_CREATURE_LEVEL will be used. This will +// cause the dispel effect to use the level of the creature that created the +// effect. +struct PRCeffect PRCEffectDispelMagicBest(int nCasterLevel=USE_CREATURE_LEVEL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nCasterLevel; + eReturn.eEffect = EffectDispelMagicBest(nCasterLevel); + return eReturn; +} + +// Create a Miss Chance effect. +// - nPercentage: 1-100 inclusive +// - nMissChanceType: MISS_CHANCE_TYPE_* +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nPercentage < 1 or +// nPercentage > 100. +struct PRCeffect PRCEffectMissChance(int nPercentage, int nMissChanceType=MISS_CHANCE_TYPE_NORMAL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPercentage; + eReturn.nVar2 = nMissChanceType; + eReturn.eEffect = EffectMissChance(nPercentage,nMissChanceType); + return eReturn; +} + +// Create a Disappear/Appear effect. +// The object will "fly away" for the duration of the effect and will reappear +// at lLocation. +// - nAnimation determines which appear and disappear animations to use. Most creatures +// only have animation 1, although a few have 2 (like beholders) +struct PRCeffect PRCEffectDisappearAppear(location lLocation, int nAnimation=1) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.lVar1 = lLocation; + eReturn.nVar1 = nAnimation; + eReturn.eEffect = EffectDisappearAppear(lLocation,nAnimation); + return eReturn; +} + +// Create a Disappear effect to make the object "fly away" and then destroy +// itself. +// - nAnimation determines which appear and disappear animations to use. Most creatures +// only have animation 1, although a few have 2 (like beholders) +struct PRCeffect PRCEffectDisappear(int nAnimation=1) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAnimation; + eReturn.eEffect = EffectDisappear(nAnimation); + return eReturn; +} + +// Create an Appear effect to make the object "fly in". +// - nAnimation determines which appear and disappear animations to use. Most creatures +// only have animation 1, although a few have 2 (like beholders) +struct PRCeffect PRCEffectAppear(int nAnimation=1) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nAnimation; + eReturn.eEffect = EffectAppear(nAnimation); + return eReturn; +} + +// Create a Modify Attacks effect to add attacks. +// - nAttacks: maximum is 5, even with the effect stacked +// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nAttacks > 5. +struct PRCeffect PRCEffectModifyAttacks(int nAttacks) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectModifyAttacks(nAttacks); + return eReturn; +} + +// Create a Damage Shield effect which does (nDamageAmount + nRandomAmount) +// damage to any melee attacker on a successful attack of damage type nDamageType. +// - nDamageAmount: an integer value +// - nRandomAmount: DAMAGE_BONUS_* +// - nDamageType: DAMAGE_TYPE_* +// NOTE! You *must* use the DAMAGE_BONUS_* constants! Using other values may +// result in odd behaviour. +struct PRCeffect PRCEffectDamageShield(int nDamageAmount, int nRandomAmount, int nDamageType) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nDamageAmount; + eReturn.nVar2 = nRandomAmount; + eReturn.nVar3 = nDamageType; + eReturn.eEffect = EffectDamageShield(nDamageAmount,nRandomAmount,nDamageType); + return eReturn; +} + +// Create a Swarm effect. +// - nLooping: If this is TRUE, for the duration of the effect when one creature +// created by this effect dies, the next one in the list will be created. If +// the last creature in the list dies, we loop back to the beginning and +// sCreatureTemplate1 will be created, and so on... +// - sCreatureTemplate1 +// - sCreatureTemplate2 +// - sCreatureTemplate3 +// - sCreatureTemplate4 +struct PRCeffect PRCEffectSwarm(int nLooping, string sCreatureTemplate1, string sCreatureTemplate2="", string sCreatureTemplate3="", string sCreatureTemplate4="") +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nLooping; + eReturn.sVar1 = sCreatureTemplate1; + eReturn.sVar2 = sCreatureTemplate2; + eReturn.sVar3 = sCreatureTemplate3; + eReturn.sVar4 = sCreatureTemplate4; + eReturn.eEffect = EffectSwarm(nLooping,sCreatureTemplate1,sCreatureTemplate2,sCreatureTemplate3,sCreatureTemplate4); + return eReturn; +} + +// Create a Turn Resistance Decrease effect. +// - nHitDice: a positive number representing the number of hit dice for the +/// decrease +struct PRCeffect PRCEffectTurnResistanceDecrease(int nHitDice) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nHitDice; + eReturn.eEffect = EffectTurnResistanceDecrease(nHitDice); + return eReturn; +} + +// Create a Turn Resistance Increase effect. +// - nHitDice: a positive number representing the number of hit dice for the +// increase +struct PRCeffect PRCEffectTurnResistanceIncrease(int nHitDice) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nHitDice; + eReturn.eEffect = EffectTurnResistanceIncrease(nHitDice); + return eReturn; +} + +// returns an effect that will petrify the target +// * currently applies EffectParalyze and the stoneskin visual effect. +struct PRCeffect PRCEffectPetrify() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectPetrify(); + return eReturn; +} + +// returns an effect that is guaranteed to paralyze a creature. +// this effect is identical to EffectParalyze except that it cannot be resisted. +struct PRCeffect PRCEffectCutsceneParalyze() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectCutsceneParalyze(); + return eReturn; +} + +// Returns an effect that is guaranteed to dominate a creature +// Like EffectDominated but cannot be resisted +struct PRCeffect PRCEffectCutsceneDominated() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectCutsceneDominated(); + return eReturn; +} + +// Creates an effect that inhibits spells +// - nPercent - percentage of failure +// - nSpellSchool - the school of spells affected. +struct PRCeffect PRCEffectSpellFailure(int nPercent=100, int nSpellSchool=SPELL_SCHOOL_GENERAL) +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.nVar1 = nPercent; + eReturn.nVar2 = nSpellSchool; + eReturn.eEffect = EffectSpellFailure(nPercent,nSpellSchool); + return eReturn; +} + +// Returns an effect of type EFFECT_TYPE_ETHEREAL which works just like EffectSanctuary +// except that the observers get no saving throw +struct PRCeffect PRCEffectEthereal() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectEthereal(); + return eReturn; +} + +// Creates a cutscene ghost effect, this will allow creatures +// to pathfind through other creatures without bumping into them +// for the duration of the effect. +struct PRCeffect PRCEffectCutsceneGhost() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectCutsceneGhost(); + return eReturn; +} + +// Returns an effect that when applied will paralyze the target's legs, rendering +// them unable to walk but otherwise unpenalized. This effect cannot be resisted. +struct PRCeffect PRCEffectCutsceneImmobilize() +{ + struct PRCeffect eReturn = GetNewPRCEffectBase(); + eReturn.eEffect = EffectCutsceneImmobilize(); + return eReturn; +} \ No newline at end of file diff --git a/src/include/prc_inc_factotum.nss b/src/include/prc_inc_factotum.nss new file mode 100644 index 0000000..46be9c4 --- /dev/null +++ b/src/include/prc_inc_factotum.nss @@ -0,0 +1,279 @@ +/* + * Factotum general functions handling. + * + * @author Stratovarius - 2019.12.21 + */ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Stores SpellIds for Arcane Dilettante + * + * @param oPC PC to target + * @param nSpell SpellID to store + */ +void PrepareArcDilSpell(object oPC, int nSpell); + +/** + * Returns TRUE if there are more spells to learn + * + * @param oPC PC to target + */ +void PrepareArcDilSpell(object oPC, int nSpell); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int FACTOTUM_SLOT_1 = 3887; +const int FACTOTUM_SLOT_2 = 3888; +const int FACTOTUM_SLOT_3 = 3889; +const int FACTOTUM_SLOT_4 = 3890; +const int FACTOTUM_SLOT_5 = 3891; +const int FACTOTUM_SLOT_6 = 3892; +const int FACTOTUM_SLOT_7 = 3893; +const int FACTOTUM_SLOT_8 = 3894; + +const int BRILLIANCE_SLOT_1 = 3917; +const int BRILLIANCE_SLOT_2 = 3918; +const int BRILLIANCE_SLOT_3 = 3919; + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "prc_inc_function" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void PrepareArcDilSpell(object oPC, int nSpell) +{ + if (DEBUG) DoDebug("PrepareArcDilSpell "+IntToString(nSpell)); + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + + // Done like this because you can only have a certain amount at a given level + if (!GetLocalInt(oPC, "ArcDilSpell1") && nClass >= 2) SetLocalInt(oPC, "ArcDilSpell1", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell2") && nClass >= 4) SetLocalInt(oPC, "ArcDilSpell2", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell3") && nClass >= 7) SetLocalInt(oPC, "ArcDilSpell3", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell4") && nClass >= 9) SetLocalInt(oPC, "ArcDilSpell4", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell5") && nClass >= 12) SetLocalInt(oPC, "ArcDilSpell5", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell6") && nClass >= 14) SetLocalInt(oPC, "ArcDilSpell6", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell7") && nClass >= 17) SetLocalInt(oPC, "ArcDilSpell7", nSpell); + else if (!GetLocalInt(oPC, "ArcDilSpell8") && nClass >= 20) SetLocalInt(oPC, "ArcDilSpell8", nSpell); +} + +int GetMaxLearnedArcDil(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + int nCount, nMax; + if (GetLocalInt(oPC, "ArcDilSpell1")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell2")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell3")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell4")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell5")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell6")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell7")) nCount++; + if (GetLocalInt(oPC, "ArcDilSpell8")) nCount++; + + if(nClass >= 2) nMax++; + if(nClass >= 4) nMax++; + if(nClass >= 7) nMax++; + if(nClass >= 9) nMax++; + if(nClass >= 12) nMax++; + if(nClass >= 14) nMax++; + if(nClass >= 17) nMax++; + if(nClass >= 20) nMax++; + + int nReturn = FALSE; + if (nMax > nCount) nReturn = TRUE; + + return nReturn; +} + +int GetFactotumSlot(object oPC) +{ + int nSlot = PRCGetSpellId(); + if (nSlot == FACTOTUM_SLOT_1) return GetLocalInt(oPC, "ArcDilSpell1"); + if (nSlot == FACTOTUM_SLOT_2) return GetLocalInt(oPC, "ArcDilSpell2"); + if (nSlot == FACTOTUM_SLOT_3) return GetLocalInt(oPC, "ArcDilSpell3"); + if (nSlot == FACTOTUM_SLOT_4) return GetLocalInt(oPC, "ArcDilSpell4"); + if (nSlot == FACTOTUM_SLOT_5) return GetLocalInt(oPC, "ArcDilSpell5"); + if (nSlot == FACTOTUM_SLOT_6) return GetLocalInt(oPC, "ArcDilSpell6"); + if (nSlot == FACTOTUM_SLOT_7) return GetLocalInt(oPC, "ArcDilSpell7"); + if (nSlot == FACTOTUM_SLOT_8) return GetLocalInt(oPC, "ArcDilSpell8"); + + if (nSlot == BRILLIANCE_SLOT_1) return GetLocalInt(oPC, "CunningAbility1"); + if (nSlot == BRILLIANCE_SLOT_2) return GetLocalInt(oPC, "CunningAbility2"); + if (nSlot == BRILLIANCE_SLOT_3) return GetLocalInt(oPC, "CunningAbility3"); + + return -1; +} + +void CheckFactotumSlots(object oPC) +{ + int i; + for (i = 1; i <= 8; i++) + { + string sSpell = "ArcDilSpell"; + int nSpell = GetLocalInt(oPC, sSpell+IntToString(i)); + sSpell = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpell))); + + if (nSpell > 0) FloatingTextStringOnCreature("Arcane Dilettante Slot "+IntToString(i)+" is "+sSpell, oPC, FALSE); + } +} + +void CheckBrillianceSlots(object oPC) +{ + int i; + for (i = 1; i <= 3; i++) + { + string sSpell = "CunningAbility"; + int nSpell = GetLocalInt(oPC, sSpell+IntToString(i)); + sSpell = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nSpell))); + + if (nSpell > 0) FloatingTextStringOnCreature("Cunning Brilliance Slot "+IntToString(i)+" is "+sSpell, oPC, FALSE); + } +} + +void ClearFactotumSlots(object oPC) +{ + int i; + for (i = 1; i <= 50; i++) + { + DeleteLocalInt(oPC, "ArcDilSpell"+IntToString(i)); + DeleteLocalInt(oPC, "CunningKnowledge"+IntToString(i)); + DeleteLocalInt(oPC, "CunningAbility"+IntToString(i)); + } + DeleteLocalInt(oPC, "CunningBrillianceCount"); + DeleteLocalInt(oPC, "CunningBrilliance"); +} + +int GetMaxArcDilSpellLevel(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + int nMax = -1; + + if(nClass >= 18) nMax = 7; + else if(nClass >= 15) nMax = 6; + else if(nClass >= 13) nMax = 5; + else if(nClass >= 10) nMax = 4; + else if(nClass >= 8) nMax = 3; + else if(nClass >= 5) nMax = 2; + else if(nClass >= 3) nMax = 1; + else if(nClass >= 2) nMax = 0; + + if (DEBUG) DoDebug("GetMaxArcDilSpellLevel "+IntToString(nMax)); + + return nMax; +} + +void SetInspiration(object oPC) +{ + int nInspiration = 2; + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + + if(nClass >= 20) nInspiration = 10; + else if(nClass >= 17) nInspiration = 8; + else if(nClass >= 14) nInspiration = 7; + else if(nClass >= 11) nInspiration = 6; + else if(nClass >= 8) nInspiration = 5; + else if(nClass >= 5) nInspiration = 4; + else if(nClass >= 2) nInspiration = 3; + + int i, nFont; + for(i = FEAT_FONT_INSPIRATION_1; i <= FEAT_FONT_INSPIRATION_10; i++) + if(GetHasFeat(i, oPC)) nFont++; + + nInspiration += nFont * (1 + nFont + 1) / 2; + SetLocalInt(oPC, "InspirationPool", nInspiration); + FloatingTextStringOnCreature("Encounter begins with "+IntToString(nInspiration)+" inspiration", oPC, FALSE); +} + +void ClearInspiration(object oPC) +{ + DeleteLocalInt(oPC, "InspirationPool"); + FloatingTextStringOnCreature("Encounter ends, inspiration lost", oPC, FALSE); +} + +int ExpendInspiration(object oPC, int nCost) +{ + int nInspiration = GetLocalInt(oPC, "InspirationPool"); + if (nInspiration >= nCost) + { + SetLocalInt(oPC, "InspirationPool", nInspiration-nCost); + FloatingTextStringOnCreature("You have "+IntToString(nInspiration-nCost)+" inspiration remaining this encounter", oPC, FALSE); + return TRUE; + } + + FloatingTextStringOnCreature("You do not have enough inspiration", oPC, FALSE); + return FALSE; +} + +void MarkAbilitySaved(object oPC, int nAbil) +{ + if (DEBUG) DoDebug("MarkAbilitySaved nAbil is "+IntToString(nAbil)); + + if (!GetLocalInt(oPC, "CunningAbility1")) SetLocalInt(oPC, "CunningAbility1", nAbil); + else if (!GetLocalInt(oPC, "CunningAbility2")) SetLocalInt(oPC, "CunningAbility2", nAbil); + else if (!GetLocalInt(oPC, "CunningAbility3")) SetLocalInt(oPC, "CunningAbility3", nAbil); +} + +int GetIsAbilitySaved(object oPC, int nAbil) +{ + int i, nCount, nTest; + for (i = 0; i <= 3; i++) + { + nTest = GetLocalInt(oPC, "CunningAbility"+IntToString(i)); + if (nTest == nAbil) + nCount = TRUE; + } + if (DEBUG) DoDebug("GetIsAbilitySaved is "+IntToString(nCount)); + return nCount; +} + +void FactotumTriggerAbil(object oPC, int nAbil) +{ + object oSkin = GetPCSkin(oPC); + itemproperty ipIP; + if (nAbil == FEAT_BARBARIAN_RAGE) + ExecuteScript("NW_S1_BarbRage", oPC); + else if (nAbil == FEAT_BARBARIAN_ENDURANCE) + ipIP = PRCItemPropertyBonusFeat(IP_CONST_FEAT_BarbEndurance); + else if (nAbil == FEAT_SNEAK_ATTACK) + { + SetLocalInt(oPC, "FactotumSneak", TRUE); + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oPC)); + DelayCommand(59.9, DeleteLocalInt(oPC, "FactotumSneak")); + DelayCommand(60.0, ExecuteScript("prc_sneak_att", oPC)); + } + else if (nAbil == 3665) // Mettle + { + SetLocalInt(oPC, "FactotumMettle", TRUE); + DelayCommand(60.0, DeleteLocalInt(oPC, "FactotumMettle")); + } + else if (nAbil == FEAT_CRUSADER_SMITE) + ipIP = PRCItemPropertyBonusFeat(IP_CONST_FEAT_CRUSADER_SMITE); + + IPSafeAddItemProperty(oSkin, ipIP, 60.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); +} + +/*void AddCunningBrillianceAbility(object oPC, int nAbil) +{ + if (DEBUG) DoDebug("AddCunningBrillianceAbility "+IntToString(nAbil)); + + object oSkin = GetPCSkin(oPC); + itemproperty ipIP; + + if (nAbil == FEAT_BARBARIAN_ENDURANCE) + ipIP = PRCItemPropertyBonusFeat(IP_CONST_FEAT_BarbEndurance); + else if (nAbil == FEAT_BARBARIAN_RAGE) + ipIP = PRCItemPropertyBonusFeat(IP_CONST_FEAT_RAGE); + + IPSafeAddItemProperty(oSkin, ipIP, 9999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + MarkAbilitySaved(oPC, nAbil); +} +*/ diff --git a/src/include/prc_inc_fork.nss b/src/include/prc_inc_fork.nss new file mode 100644 index 0000000..3e46b83 --- /dev/null +++ b/src/include/prc_inc_fork.nss @@ -0,0 +1,837 @@ +/** + * @file + * + * Functions used to get weapon data. + * + * Mostly functions moved from prc_inc_combat. Used to get weapon data and weapon-related feats based on weapon type. + * Used by the combat system, weapon restriction/proficiency system and for deity weapons (eg. favoured soul) + */ + +//:: Test void +//void main (){} + +////////////////////////////////////////////////// +/* Constant definitions */ +////////////////////////////////////////////////// + +// used to select certain types of feats associated with a weapon. +const int FEAT_TYPE_FOCUS = 1; +const int FEAT_TYPE_SPECIALIZATION = 2; +const int FEAT_TYPE_EPIC_FOCUS = 3; +const int FEAT_TYPE_EPIC_SPECIALIZATION = 4; +const int FEAT_TYPE_IMPROVED_CRITICAL = 5; +const int FEAT_TYPE_OVERWHELMING_CRITICAL = 6; +const int FEAT_TYPE_DEVASTATING_CRITICAL = 7; +const int FEAT_TYPE_WEAPON_OF_CHOICE = 8; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the appropriate weapon feat given a weapon type. + * + * Similar to GetFeatByWeaponType(), but should be a bit faster, because it does not use strings + * + * @param iWeaponType BASE_ITEM_* constant for the weapon + * @param iFeatType One of: + * FEAT_TYPE_FOCUS, + * FEAT_TYPE_EPIC_FOCUS, + * FEAT_TYPE_SPECIALIZATION, + * FEAT_TYPE_EPIC_SPECIALIZATION, + * FEAT_TYPE_OVERWHELMING_CRITICAL, + * FEAT_TYPE_DEVASTATING_CRITICAL, + * FEAT_TYPE_WEAPON_OF_CHOICE. + * + * @return Appropriate weapon feat number based on arguments. + */ +int GetFeatOfWeaponType(int iWeaponType, int iFeatType); + +/** + * Returns the weapon focus feat associated with the basetype of the weapon. + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike weapon focus + * feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The weapon focus feat associated with the weapon or unarmed if either a + * creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetFocusFeatOfWeaponType(int iWeaponType); + +/** + * Returns the weapon specialization feat associated with the basetype of the weapon + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike weapon specialization + * feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The weapon specialization feat associated with the weapon or unarmed if either a + * creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetSpecializationFeatOfWeaponType(int iWeaponType); + +/** + * Returns the epic weapon focus feat associated with the basetype of the weapon + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike epic weapon focus + * feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The epic weapon focus feat associated with the weapon or unarmed if either a + * creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetEpicFocusFeatOfWeaponType(int iWeaponType); + +/** + * Returns the epic weapon specialization feat associated with the basetype of the weapon + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike epic weapon + * specialization feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The epic weapon specialization feat associated with the weapon or unarmed + * if either a creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetEpicSpecializationFeatOfWeaponType(int iWeaponType); + +/** + * Returns the improved critical feat associated with the basetype of the weapon + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike improved + * critical feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The improved critical feat associated with the weapon or unarmed + * if either a creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetImprovedCriticalFeatOfWeaponType(int iWeaponType); + +/** + * Returns the overwhelming critical feat associated with the basetype of the weapon + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike overwhelming + * critical feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The overwhelming critical feat associated with the weapon or unarmed + * if either a creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetOverwhelmingCriticalFeatOfWeaponType(int iWeaponType); + +/** + * Returns the devastating critical feat associated with the basetype of the weapon + * + * Creature weapon types count as unarmed for this function. Will return unarmed strike devastating + * critical feat if base item is invalid. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The devastating critical feat associated with the weapon or unarmed + * if either a creature weapon or BASE_ITEM_INVALID is passed as argument. + */ +int GetDevastatingCriticalFeatOfWeaponType(int iWeaponType); + +/** + * Returns the weapon of choice feat associated with the basetype of the weapon + * + * Only melee weapons can be weapons of choice. Creature weapon types count as unarmed for this function, so + * will return -1 along with ranged weapons and invalid base items. + * + * @param iWeaponType The BASE_TYPE_* of the weapon. + * @return The devastating critical feat associated with a melee weapon or -1 if + * ranged or creature weapons, or BASE_ITEM_INVALID is passed as argument. + */ +int GetWeaponOfChoiceFeatOfWeaponType(int iWeaponType); + +/** + * Returns the value of the WeaponType column in baseitem.2da. + * + * The number returned will be zero for any Base Item type that is not a weapon, + * it will have different (positive) numbers for different weapon types + * + * @param iBaseType The BASE_TYPE_* of the weapon. + * @return the weapon type of the weapon or 0 if not a weapon + */ +int GetIsWeaponType(int iBaseType); + +/** + * Returns the value of the WeaponType column in baseitem.2da. + * + * The number returned will be zero for any item that is not a weapon, + * it will have different (positive) numbers for different weapon types + * + * @param oItem The item to test. + * @return the weapon type of the weapon or 0 if not a weapon + */ +int GetIsWeapon(object oItem); + +/** + * Gets the size of the weapon. Hard-coded some weapon types to allow large + * races double-wield two-handed weapons (imagine Ogre with two greatswords ;) ) + * + * @param oItem The item to test. + * @return The size of the weapon, -1 if oItem is not a weapon. + */ +int GetWeaponSize(object oWeapon); + +/** + * Returns TRUE if item is a shield + * + * @param oItem The item to test. + * @return TRUE if a shield, otherwise FALSE + */ +int GetIsShield(object oItem); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +#include "prc_misc_const" +#include "prc_feat_const" +#include "inc_2dacache" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +int GetFeatOfWeaponType(int iWeaponType, int iFeatType) +{ + switch(iFeatType) + { + case FEAT_TYPE_FOCUS: return GetFocusFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_SPECIALIZATION: return GetSpecializationFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_EPIC_FOCUS: return GetEpicFocusFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_EPIC_SPECIALIZATION: return GetEpicSpecializationFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_IMPROVED_CRITICAL: return GetImprovedCriticalFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_OVERWHELMING_CRITICAL: return GetOverwhelmingCriticalFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_DEVASTATING_CRITICAL: return GetDevastatingCriticalFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_WEAPON_OF_CHOICE: return GetWeaponOfChoiceFeatOfWeaponType(iWeaponType); + } + return -1; +} + +int GetFocusFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_WEAPON_FOCUS_UNARMED_STRIKE; + case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_FOCUS_BASTARD_SWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_FOCUS_BATTLE_AXE; + case BASE_ITEM_CLUB: return FEAT_WEAPON_FOCUS_CLUB; + case BASE_ITEM_DAGGER: return FEAT_WEAPON_FOCUS_DAGGER; + case BASE_ITEM_DART: return FEAT_WEAPON_FOCUS_DART; + case BASE_ITEM_DIREMACE: return FEAT_WEAPON_FOCUS_DIRE_MACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_WEAPON_FOCUS_DOUBLE_AXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_WEAPON_FOCUS_DWAXE; + case BASE_ITEM_GREATAXE: return FEAT_WEAPON_FOCUS_GREAT_AXE; + case BASE_ITEM_GREATSWORD: return FEAT_WEAPON_FOCUS_GREAT_SWORD; + case BASE_ITEM_HALBERD: return FEAT_WEAPON_FOCUS_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_WEAPON_FOCUS_HAND_AXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_WEAPON_FOCUS_HEAVY_FLAIL; + case BASE_ITEM_KAMA: return FEAT_WEAPON_FOCUS_KAMA; + case BASE_ITEM_KATANA: return FEAT_WEAPON_FOCUS_KATANA; + case BASE_ITEM_KUKRI: return FEAT_WEAPON_FOCUS_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_WEAPON_FOCUS_LIGHT_FLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_WEAPON_FOCUS_LIGHT_HAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_WEAPON_FOCUS_LIGHT_MACE; + case BASE_ITEM_LONGBOW: return FEAT_WEAPON_FOCUS_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_WEAPON_FOCUS_LONG_SWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_WEAPON_FOCUS_MORNING_STAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_WEAPON_FOCUS_STAFF; + case BASE_ITEM_RAPIER: return FEAT_WEAPON_FOCUS_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_WEAPON_FOCUS_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_WEAPON_FOCUS_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_WEAPON_FOCUS_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_WEAPON_FOCUS_SPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_WEAPON_FOCUS_SHORT_SWORD; + case BASE_ITEM_SHURIKEN: return FEAT_WEAPON_FOCUS_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_WEAPON_FOCUS_SICKLE; + case BASE_ITEM_SLING: return FEAT_WEAPON_FOCUS_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_WEAPON_FOCUS_THROWING_AXE; + case BASE_ITEM_TRIDENT: return FEAT_WEAPON_FOCUS_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD; + case BASE_ITEM_WARHAMMER: return FEAT_WEAPON_FOCUS_WAR_HAMMER; + case BASE_ITEM_WHIP: return FEAT_WEAPON_FOCUS_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_WEAPON_FOCUS_EAGLE_CLAW; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_WEAPON_FOCUS_ELVEN_THINBLADE; + case BASE_ITEM_FALCHION: return FEAT_WEAPON_FOCUS_FALCHION; + case BASE_ITEM_GOAD: return FEAT_WEAPON_FOCUS_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_WEAPON_FOCUS_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_WEAPON_FOCUS_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_WEAPON_FOCUS_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_WEAPON_FOCUS_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_WEAPON_FOCUS_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_WEAPON_FOCUS_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_WEAPON_FOCUS_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_WEAPON_FOCUS_SAI; + case BASE_ITEM_SAP: return FEAT_WEAPON_FOCUS_SAP; + } + return -1; +} + +int GetSpecializationFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE; + case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE; + case BASE_ITEM_CLUB: return FEAT_WEAPON_SPECIALIZATION_CLUB; + case BASE_ITEM_DAGGER: return FEAT_WEAPON_SPECIALIZATION_DAGGER; + case BASE_ITEM_DART: return FEAT_WEAPON_SPECIALIZATION_DART; + case BASE_ITEM_DIREMACE: return FEAT_WEAPON_SPECIALIZATION_DIRE_MACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_WEAPON_SPECIALIZATION_DWAXE ; + case BASE_ITEM_GREATAXE: return FEAT_WEAPON_SPECIALIZATION_GREAT_AXE; + case BASE_ITEM_GREATSWORD: return FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD; + case BASE_ITEM_HALBERD: return FEAT_WEAPON_SPECIALIZATION_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_WEAPON_SPECIALIZATION_HAND_AXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL; + case BASE_ITEM_KAMA: return FEAT_WEAPON_SPECIALIZATION_KAMA; + case BASE_ITEM_KATANA: return FEAT_WEAPON_SPECIALIZATION_KATANA; + case BASE_ITEM_KUKRI: return FEAT_WEAPON_SPECIALIZATION_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE; + case BASE_ITEM_LONGBOW: return FEAT_WEAPON_SPECIALIZATION_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_WEAPON_SPECIALIZATION_LONG_SWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_WEAPON_SPECIALIZATION_MORNING_STAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_WEAPON_SPECIALIZATION_STAFF; + case BASE_ITEM_RAPIER: return FEAT_WEAPON_SPECIALIZATION_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_WEAPON_SPECIALIZATION_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_WEAPON_SPECIALIZATION_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_WEAPON_SPECIALIZATION_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_WEAPON_SPECIALIZATION_SPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD; + case BASE_ITEM_SHURIKEN: return FEAT_WEAPON_SPECIALIZATION_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_WEAPON_SPECIALIZATION_SICKLE; + case BASE_ITEM_SLING: return FEAT_WEAPON_SPECIALIZATION_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_WEAPON_SPECIALIZATION_THROWING_AXE; + case BASE_ITEM_TRIDENT: return FEAT_WEAPON_SPECIALIZATION_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD; + case BASE_ITEM_WARHAMMER: return FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER; + case BASE_ITEM_WHIP: return FEAT_WEAPON_SPECIALIZATION_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_WEAPON_SPECIALIZATION_FALCHION; + case BASE_ITEM_GOAD: return FEAT_WEAPON_SPECIALIZATION_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_WEAPON_SPECIALIZATION_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_WEAPON_SPECIALIZATION_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_WEAPON_SPECIALIZATION_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_WEAPON_SPECIALIZATION_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_WEAPON_SPECIALIZATION_SAI; + case BASE_ITEM_SAP: return FEAT_WEAPON_SPECIALIZATION_SAP; + } + return -1; +} + +int GetEpicFocusFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_EPIC_WEAPON_FOCUS_UNARMED; + case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_WEAPON_FOCUS_BASTARDSWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_WEAPON_FOCUS_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_EPIC_WEAPON_FOCUS_CLUB; + case BASE_ITEM_DAGGER: return FEAT_EPIC_WEAPON_FOCUS_DAGGER; + case BASE_ITEM_DART: return FEAT_EPIC_WEAPON_FOCUS_DART; + case BASE_ITEM_DIREMACE: return FEAT_EPIC_WEAPON_FOCUS_DIREMACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_EPIC_WEAPON_FOCUS_DOUBLEAXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_EPIC_WEAPON_FOCUS_DWAXE; + case BASE_ITEM_GREATAXE: return FEAT_EPIC_WEAPON_FOCUS_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_EPIC_WEAPON_FOCUS_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_EPIC_WEAPON_FOCUS_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_EPIC_WEAPON_FOCUS_HANDAXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_EPIC_WEAPON_FOCUS_HEAVYCROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_EPIC_WEAPON_FOCUS_HEAVYFLAIL; + case BASE_ITEM_KAMA: return FEAT_EPIC_WEAPON_FOCUS_KAMA; + case BASE_ITEM_KATANA: return FEAT_EPIC_WEAPON_FOCUS_KATANA; + case BASE_ITEM_KUKRI: return FEAT_EPIC_WEAPON_FOCUS_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_EPIC_WEAPON_FOCUS_LIGHTCROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_EPIC_WEAPON_FOCUS_LIGHTFLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_EPIC_WEAPON_FOCUS_LIGHTHAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_EPIC_WEAPON_FOCUS_LIGHTMACE; + case BASE_ITEM_LONGBOW: return FEAT_EPIC_WEAPON_FOCUS_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_EPIC_WEAPON_FOCUS_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_EPIC_WEAPON_FOCUS_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_EPIC_WEAPON_FOCUS_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_EPIC_WEAPON_FOCUS_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_EPIC_WEAPON_FOCUS_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_EPIC_WEAPON_FOCUS_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_EPIC_WEAPON_FOCUS_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_EPIC_WEAPON_FOCUS_SHORTSPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_EPIC_WEAPON_FOCUS_SHORTSWORD; + case BASE_ITEM_SHURIKEN: return FEAT_EPIC_WEAPON_FOCUS_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_EPIC_WEAPON_FOCUS_SICKLE; + case BASE_ITEM_SLING: return FEAT_EPIC_WEAPON_FOCUS_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_EPIC_WEAPON_FOCUS_THROWINGAXE; + case BASE_ITEM_TRIDENT: return FEAT_EPIC_WEAPON_FOCUS_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_EPIC_WEAPON_FOCUS_TWOBLADEDSWORD; + case BASE_ITEM_WARHAMMER: return FEAT_EPIC_WEAPON_FOCUS_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_EPIC_WEAPON_FOCUS_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_EPIC_WEAPON_FOCUS_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_EPIC_WEAPON_FOCUS_FALCHION; + case BASE_ITEM_GOAD: return FEAT_EPIC_WEAPON_FOCUS_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_EPIC_WEAPON_FOCUS_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_EPIC_WEAPON_FOCUS_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_EPIC_WEAPON_FOCUS_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_EPIC_WEAPON_FOCUS_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_EPIC_WEAPON_FOCUS_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_EPIC_WEAPON_FOCUS_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_EPIC_WEAPON_FOCUS_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_EPIC_WEAPON_FOCUS_SAI; + case BASE_ITEM_SAP: return FEAT_EPIC_WEAPON_FOCUS_SAP; + } + return -1; +} + +int GetEpicSpecializationFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_EPIC_WEAPON_SPECIALIZATION_UNARMED; + case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_BASTARDSWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; + case BASE_ITEM_DAGGER: return FEAT_EPIC_WEAPON_SPECIALIZATION_DAGGER; + case BASE_ITEM_DART: return FEAT_EPIC_WEAPON_SPECIALIZATION_DART; + case BASE_ITEM_DIREMACE: return FEAT_EPIC_WEAPON_SPECIALIZATION_DIREMACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_DOUBLEAXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_DWAXE; + case BASE_ITEM_GREATAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_EPIC_WEAPON_SPECIALIZATION_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_HANDAXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVYCROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVYFLAIL; + case BASE_ITEM_KAMA: return FEAT_EPIC_WEAPON_SPECIALIZATION_KAMA; + case BASE_ITEM_KATANA: return FEAT_EPIC_WEAPON_SPECIALIZATION_KATANA; + case BASE_ITEM_KUKRI: return FEAT_EPIC_WEAPON_SPECIALIZATION_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTCROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTFLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTHAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHTMACE; + case BASE_ITEM_LONGBOW: return FEAT_EPIC_WEAPON_SPECIALIZATION_LONGBOW; // motu99: Longbow and Longsword were interchanged. Corrected that + case BASE_ITEM_LONGSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_EPIC_WEAPON_SPECIALIZATION_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_EPIC_WEAPON_SPECIALIZATION_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_EPIC_WEAPON_SPECIALIZATION_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_EPIC_WEAPON_SPECIALIZATION_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSWORD; + case BASE_ITEM_SHURIKEN: return FEAT_EPIC_WEAPON_SPECIALIZATION_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_EPIC_WEAPON_SPECIALIZATION_SICKLE; + case BASE_ITEM_SLING: return FEAT_EPIC_WEAPON_SPECIALIZATION_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_THROWINGAXE; + case BASE_ITEM_TRIDENT: return FEAT_EPIC_WEAPON_SPECIALIZATION_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_TWOBLADEDSWORD; + case BASE_ITEM_WARHAMMER: return FEAT_EPIC_WEAPON_SPECIALIZATION_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_EPIC_WEAPON_SPECIALIZATION_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_EPIC_WEAPON_SPECIALIZATION_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_EPIC_WEAPON_SPECIALIZATION_FALCHION; + case BASE_ITEM_GOAD: return FEAT_EPIC_WEAPON_SPECIALIZATION_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_EPIC_WEAPON_SPECIALIZATION_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_EPIC_WEAPON_SPECIALIZATION_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_EPIC_WEAPON_SPECIALIZATION_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_EPIC_WEAPON_SPECIALIZATION_SAI; + case BASE_ITEM_SAP: return FEAT_EPIC_WEAPON_SPECIALIZATION_SAP; + } + return -1; +} + +int GetImprovedCriticalFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_IMPROVED_CRITICAL_UNARMED_STRIKE; + case BASE_ITEM_BASTARDSWORD: return FEAT_IMPROVED_CRITICAL_BASTARD_SWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_IMPROVED_CRITICAL_BATTLE_AXE; + case BASE_ITEM_CLUB: return FEAT_IMPROVED_CRITICAL_CLUB; + case BASE_ITEM_DAGGER: return FEAT_IMPROVED_CRITICAL_DAGGER; + case BASE_ITEM_DART: return FEAT_IMPROVED_CRITICAL_DART; + case BASE_ITEM_DIREMACE: return FEAT_IMPROVED_CRITICAL_DIRE_MACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_IMPROVED_CRITICAL_DOUBLE_AXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_IMPROVED_CRITICAL_DWAXE ; + case BASE_ITEM_GREATAXE: return FEAT_IMPROVED_CRITICAL_GREAT_AXE; + case BASE_ITEM_GREATSWORD: return FEAT_IMPROVED_CRITICAL_GREAT_SWORD; + case BASE_ITEM_HALBERD: return FEAT_IMPROVED_CRITICAL_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_IMPROVED_CRITICAL_HAND_AXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL; + case BASE_ITEM_KAMA: return FEAT_IMPROVED_CRITICAL_KAMA; + case BASE_ITEM_KATANA: return FEAT_IMPROVED_CRITICAL_KATANA; + case BASE_ITEM_KUKRI: return FEAT_IMPROVED_CRITICAL_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_IMPROVED_CRITICAL_LIGHT_MACE; + case BASE_ITEM_LONGBOW: return FEAT_IMPROVED_CRITICAL_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_IMPROVED_CRITICAL_LONG_SWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_IMPROVED_CRITICAL_MORNING_STAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_IMPROVED_CRITICAL_STAFF; + case BASE_ITEM_RAPIER: return FEAT_IMPROVED_CRITICAL_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_IMPROVED_CRITICAL_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_IMPROVED_CRITICAL_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_IMPROVED_CRITICAL_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_IMPROVED_CRITICAL_SPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_IMPROVED_CRITICAL_SHORT_SWORD; + case BASE_ITEM_SHURIKEN: return FEAT_IMPROVED_CRITICAL_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_IMPROVED_CRITICAL_SICKLE; + case BASE_ITEM_SLING: return FEAT_IMPROVED_CRITICAL_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_IMPROVED_CRITICAL_THROWING_AXE; + case BASE_ITEM_TRIDENT: return FEAT_IMPROVED_CRITICAL_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD; + case BASE_ITEM_WARHAMMER: return FEAT_IMPROVED_CRITICAL_WAR_HAMMER; + case BASE_ITEM_WHIP: return FEAT_IMPROVED_CRITICAL_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_IMPROVED_CRITICAL_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_IMPROVED_CRITICAL_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_IMPROVED_CRITICAL_FALCHION; + case BASE_ITEM_GOAD: return FEAT_IMPROVED_CRITICAL_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_IMPROVED_CRITICAL_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_IMPROVED_CRITICAL_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_IMPROVED_CRITICAL_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_IMPROVED_CRITICAL_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_IMPROVED_CRITICAL_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_IMPROVED_CRITICAL_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_IMPROVED_CRITICAL_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_IMPROVED_CRITICAL_SAI; + case BASE_ITEM_SAP: return FEAT_IMPROVED_CRITICAL_SAP; + } + return -1; +} + +int GetOverwhelmingCriticalFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_EPIC_OVERWHELMING_CRITICAL_UNARMED; + case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_BASTARDSWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; + case BASE_ITEM_DAGGER: return FEAT_EPIC_OVERWHELMING_CRITICAL_DAGGER; + case BASE_ITEM_DART: return FEAT_EPIC_OVERWHELMING_CRITICAL_DART; + case BASE_ITEM_DIREMACE: return FEAT_EPIC_OVERWHELMING_CRITICAL_DIREMACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_DOUBLEAXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_DWAXE ; + case BASE_ITEM_GREATAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_EPIC_OVERWHELMING_CRITICAL_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_HANDAXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYCROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYFLAIL; + case BASE_ITEM_KAMA: return FEAT_EPIC_OVERWHELMING_CRITICAL_KAMA; + case BASE_ITEM_KATANA: return FEAT_EPIC_OVERWHELMING_CRITICAL_KATANA; + case BASE_ITEM_KUKRI: return FEAT_EPIC_OVERWHELMING_CRITICAL_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTCROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTFLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTHAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTMACE; + case BASE_ITEM_LONGBOW: return FEAT_EPIC_OVERWHELMING_CRITICAL_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_EPIC_OVERWHELMING_CRITICAL_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_EPIC_OVERWHELMING_CRITICAL_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_EPIC_OVERWHELMING_CRITICAL_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_EPIC_OVERWHELMING_CRITICAL_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_EPIC_OVERWHELMING_CRITICAL_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSWORD; + case BASE_ITEM_SHURIKEN: return FEAT_EPIC_OVERWHELMING_CRITICAL_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_EPIC_OVERWHELMING_CRITICAL_SICKLE; + case BASE_ITEM_SLING: return FEAT_EPIC_OVERWHELMING_CRITICAL_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_THROWINGAXE; + case BASE_ITEM_TRIDENT: return FEAT_EPIC_OVERWHELMING_CRITICAL_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_TWOBLADEDSWORD; + case BASE_ITEM_WARHAMMER: return FEAT_EPIC_OVERWHELMING_CRITICAL_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_EPIC_OVERWHELMING_CRITICAL_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_EPIC_OVERWHELMING_CRITICAL_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_EPIC_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_EPIC_OVERWHELMING_CRITICAL_FALCHION; + case BASE_ITEM_GOAD: return FEAT_EPIC_OVERWHELMING_CRITICAL_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_EPIC_OVERWHELMING_CRITICAL_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_EPIC_OVERWHELMING_CRITICAL_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_EPIC_OVERWHELMING_CRITICAL_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_EPIC_OVERWHELMING_CRITICAL_SAI; + case BASE_ITEM_SAP: return FEAT_EPIC_OVERWHELMING_CRITICAL_SAP; + } + return -1; +} + +int GetDevastatingCriticalFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + case BASE_ITEM_INVALID: return FEAT_EPIC_DEVASTATING_CRITICAL_UNARMED; + case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_BASTARDSWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; + case BASE_ITEM_DAGGER: return FEAT_EPIC_DEVASTATING_CRITICAL_DAGGER; + case BASE_ITEM_DART: return FEAT_EPIC_DEVASTATING_CRITICAL_DART; + case BASE_ITEM_DIREMACE: return FEAT_EPIC_DEVASTATING_CRITICAL_DIREMACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_DOUBLEAXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_DWAXE ; + case BASE_ITEM_GREATAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_EPIC_DEVASTATING_CRITICAL_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_HANDAXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYCROSSBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYFLAIL; + case BASE_ITEM_KAMA: return FEAT_EPIC_DEVASTATING_CRITICAL_KAMA; + case BASE_ITEM_KATANA: return FEAT_EPIC_DEVASTATING_CRITICAL_KATANA; + case BASE_ITEM_KUKRI: return FEAT_EPIC_DEVASTATING_CRITICAL_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTCROSSBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTFLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTHAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTMACE; + case BASE_ITEM_LONGBOW: return FEAT_EPIC_DEVASTATING_CRITICAL_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_EPIC_DEVASTATING_CRITICAL_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_EPIC_DEVASTATING_CRITICAL_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_EPIC_DEVASTATING_CRITICAL_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_EPIC_DEVASTATING_CRITICAL_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_EPIC_DEVASTATING_CRITICAL_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_EPIC_DEVASTATING_CRITICAL_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSWORD; + case BASE_ITEM_SHURIKEN: return FEAT_EPIC_DEVASTATING_CRITICAL_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_EPIC_DEVASTATING_CRITICAL_SICKLE; + case BASE_ITEM_SLING: return FEAT_EPIC_DEVASTATING_CRITICAL_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_THROWINGAXE; + case BASE_ITEM_TRIDENT: return FEAT_EPIC_DEVASTATING_CRITICAL_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_TWOBLADEDSWORD; + case BASE_ITEM_WARHAMMER: return FEAT_EPIC_DEVASTATING_CRITICAL_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_EPIC_DEVASTATING_CRITICAL_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_EPIC_DEVASTATING_CRITICAL_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_EPIC_DEVASTATING_CRITICAL_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_EPIC_DEVASTATING_CRITICAL_FALCHION; + case BASE_ITEM_GOAD: return FEAT_EPIC_DEVASTATING_CRITICAL_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_EPIC_DEVASTATING_CRITICAL_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_EPIC_DEVASTATING_CRITICAL_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_EPIC_DEVASTATING_CRITICAL_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_EPIC_DEVASTATING_CRITICAL_SAI; + case BASE_ITEM_SAP: return FEAT_EPIC_DEVASTATING_CRITICAL_SAP; + } + return -1; +} + +int GetWeaponOfChoiceFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_OF_CHOICE_BASTARDSWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_OF_CHOICE_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_WEAPON_OF_CHOICE_CLUB; + case BASE_ITEM_DAGGER: return FEAT_WEAPON_OF_CHOICE_DAGGER; + case BASE_ITEM_DIREMACE: return FEAT_WEAPON_OF_CHOICE_DIREMACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_WEAPON_OF_CHOICE_DOUBLEAXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_WEAPON_OF_CHOICE_DWAXE ; + case BASE_ITEM_GREATAXE: return FEAT_WEAPON_OF_CHOICE_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_WEAPON_OF_CHOICE_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_WEAPON_OF_CHOICE_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_WEAPON_OF_CHOICE_HANDAXE; + case BASE_ITEM_HEAVYFLAIL: return FEAT_WEAPON_OF_CHOICE_HEAVYFLAIL; + case BASE_ITEM_KAMA: return FEAT_WEAPON_OF_CHOICE_KAMA; + case BASE_ITEM_KATANA: return FEAT_WEAPON_OF_CHOICE_KATANA; + case BASE_ITEM_KUKRI: return FEAT_WEAPON_OF_CHOICE_KUKRI; + case BASE_ITEM_LIGHTFLAIL: return FEAT_WEAPON_OF_CHOICE_LIGHTFLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_WEAPON_OF_CHOICE_LIGHTHAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_WEAPON_OF_CHOICE_LIGHTMACE; + case BASE_ITEM_LONGSWORD: return FEAT_WEAPON_OF_CHOICE_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_WEAPON_OF_CHOICE_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_WEAPON_OF_CHOICE_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_WEAPON_OF_CHOICE_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_WEAPON_OF_CHOICE_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_WEAPON_OF_CHOICE_SCYTHE; + case BASE_ITEM_SHORTSPEAR: return FEAT_WEAPON_OF_CHOICE_SHORTSPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_WEAPON_OF_CHOICE_SHORTSWORD; + case BASE_ITEM_SICKLE: return FEAT_WEAPON_OF_CHOICE_SICKLE; + case BASE_ITEM_TRIDENT: return FEAT_WEAPON_OF_CHOICE_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_WEAPON_OF_CHOICE_TWOBLADEDSWORD; + case BASE_ITEM_WARHAMMER: return FEAT_WEAPON_OF_CHOICE_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_WEAPON_OF_CHOICE_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_WEAPON_OF_CHOICE_DBL_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE; + case BASE_ITEM_FALCHION: return FEAT_WEAPON_OF_CHOICE_FALCHION; + case BASE_ITEM_GOAD: return FEAT_WEAPON_OF_CHOICE_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_WEAPON_OF_CHOICE_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_WEAPON_OF_CHOICE_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_WEAPON_OF_CHOICE_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_WEAPON_OF_CHOICE_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_WEAPON_OF_CHOICE_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_WEAPON_OF_CHOICE_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_WEAPON_OF_CHOICE_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_WEAPON_OF_CHOICE_SAI; + case BASE_ITEM_SAP: return FEAT_WEAPON_OF_CHOICE_SAP; + } + return -1; +} + +int GetIsWeaponType(int iBaseType) +{ + return StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType)); +} + +int GetIsWeapon(object oItem) +{ + return GetIsWeaponType(GetBaseItemType(oItem)); +} + +int GetWeaponSize(object oWeapon) +{ + if(!GetIsWeapon(oWeapon)) + return -1; + + int iBaseType = GetBaseItemType(oWeapon); + + // weapons hard-coded by PRC + switch(iBaseType) + { + case BASE_ITEM_LONGBOW: + case BASE_ITEM_HALBERD: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_SHORTSPEAR: + case BASE_ITEM_ELVEN_COURTBLADE: + case BASE_ITEM_MAUL: + case BASE_ITEM_FALCHION: + return 4; + } + + return StringToInt(Get2DACache("baseitems", "WeaponSize", iBaseType)); +} + +int GetIsShield(object oItem) +{ + switch(GetBaseItemType(oItem)) + { + case BASE_ITEM_LARGESHIELD: + case BASE_ITEM_SMALLSHIELD: + case BASE_ITEM_TOWERSHIELD: + return TRUE; + } + return FALSE; +} + +// Special check for weapons with modified WeaponSize entry +// aka "Two-handed weapons equipped as one-handed" +// checks if size returned by GetWeaponSize() is different than setting in baseitems.2da +int PRCLargeWeaponCheck(int iBaseType, int nSize) +{ + string sTest; + switch(iBaseType) + { + case BASE_ITEM_HALBERD: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_SHORTSPEAR: + case BASE_ITEM_ELVEN_COURTBLADE: + case BASE_ITEM_MAUL: + case BASE_ITEM_FALCHION: + { + sTest = Get2DAString("baseitems", "WeaponSize", iBaseType); + break; + } + } + return sTest != "" && sTest != IntToString(nSize); +} \ No newline at end of file diff --git a/src/include/prc_inc_function.nss b/src/include/prc_inc_function.nss new file mode 100644 index 0000000..93f7aa1 --- /dev/null +++ b/src/include/prc_inc_function.nss @@ -0,0 +1,2374 @@ +//:://///////////////////////////////////////////// +//:: [PRC Feat Router] +//:: [prc_inc_function.nss] +//::////////////////////////////////////////////// +//:: This file serves as a hub for the various +//:: PRC passive feat functions. If you need to +//:: add passive feats for a new PRC, link them here. +//:: +//:: This file also contains a few multi-purpose +//:: PRC functions that need to be included in several +//:: places, ON DIFFERENT PRCS. Make local include files +//:: for any functions you use ONLY on ONE PRC. +//::////////////////////////////////////////////// +//:: Created By: Aaon Graywolf +//:: Created On: Dec 19, 2003 +//::////////////////////////////////////////////// + +//:: Updated for 8 class slots by Jaysyn 2024/02/05 + +//-------------------------------------------------------------------------- +// This is the "event" that is called to re-evalutate PRC bonuses. Currently +// it is fired by OnEquip, OnUnequip and OnLevel. If you want to move any +// classes into this event, just copy the format below. Basically, this function +// is meant to keep the code looking nice and clean by routing each class's +// feats to their own self-contained script +//-------------------------------------------------------------------------- + +//:: Test Void +//void main (){} + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int TEMPLATE_SLA_START = 16304; +const int TEMPLATE_SLA_END = 16400; + +const string PRC_ScrubPCSkin_Generation = "PRC_ScrubPCSkin_Generation"; +const string PRC_EvalPRCFeats_Generation = "PRC_EvalPRCFeats_Generation"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +void EvalPRCFeats(object oPC); + +int BlastInfidelOrFaithHeal(object oCaster, object oTarget, int iEnergyType, int iDisplayFeedback); + +void ScrubPCSkin(object oPC, object oSkin); + +void DeletePRCLocalInts(object oSkin); + +void DelayedAddIPFeats(int nExpectedGeneration, object oPC); + +void DelayedReApplyUnhealableAbilityDamage(int nExpectedGeneration, object oPC); + +int nbWeaponFocus(object oPC); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +// Minimalist includes +#include "prc_inc_util" +#include "prc_inc_spells" +#include "prc_inc_stunfist" +#include "inc_nwnx_funcs" +#include "prc_template_con" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void DeleteCharacterData(object oPC) +{ + DeletePersistantLocalString(oPC, "PRC_Class_Script1"); + DeletePersistantLocalString(oPC, "PRC_Class_Script2"); + DeletePersistantLocalString(oPC, "PRC_Class_Script3"); + DeletePersistantLocalString(oPC, "PRC_Class_Script4"); + DeletePersistantLocalString(oPC, "PRC_Class_Script5"); + DeletePersistantLocalString(oPC, "PRC_Class_Script6"); + DeletePersistantLocalString(oPC, "PRC_Class_Script7"); + DeletePersistantLocalString(oPC, "PRC_Class_Script8"); + DeletePersistantLocalInt(oPC, "PRC_Class_Data"); +} + +void SetupCharacterData(object oPC) +{ + // iData format: + // 0x01 - run alternate magic system setup + // 0x02 - add new metamagic feats for spontaneous casters + // 0x04 - run reduced arcane spell failure check + // use bitwise to combine flags + + int i, iData, iShifting; + for(i = 1; i <= 8; i++) + { + int nClassType = GetClassByPosition(i, oPC); + if(nClassType != CLASS_TYPE_INVALID) + { + string sScript; + switch(nClassType) + { + case CLASS_TYPE_ABJURANT_CHAMPION: sScript = "prc_abchamp"; break; + case CLASS_TYPE_ACOLYTE: sScript = "prc_acolyte"; break; + case CLASS_TYPE_ALIENIST: sScript = "prc_alienist"; break; + case CLASS_TYPE_ARCANE_DUELIST: sScript = "prc_arcduel"; break; + case CLASS_TYPE_ARCHIVIST: sScript = "prc_archivist"; iData |= 0x01; break; + case CLASS_TYPE_ASSASSIN: iData |= 0x03; break; + case CLASS_TYPE_BAELNORN: sScript = "prc_baelnorn"; break; + case CLASS_TYPE_BARD: iData |= 0x07; break; + case CLASS_TYPE_BATTLESMITH: sScript = "prc_battlesmith"; break; + case CLASS_TYPE_BEGUILER: iData |= 0x03; break; + case CLASS_TYPE_BINDER: sScript = "bnd_binder"; break; + case CLASS_TYPE_BFZ: sScript = "prc_bfz"; break; + case CLASS_TYPE_BLACK_BLOOD_CULTIST: sScript = "prc_bbc"; break; + case CLASS_TYPE_BLACKGUARD: sScript = "prc_blackguard"; break; + case CLASS_TYPE_BLADESINGER: sScript = "prc_bladesinger"; iData |= 0x04; break; + case CLASS_TYPE_BLIGHTLORD: sScript = "prc_blightlord"; break; + case CLASS_TYPE_BLOODCLAW_MASTER: sScript = "tob_bloodclaw"; break; + case CLASS_TYPE_BONDED_SUMMONNER: sScript = "prc_bondedsumm"; break; + case CLASS_TYPE_CELEBRANT_SHARESS: iData |= 0x03; break; + case CLASS_TYPE_CHILD_OF_NIGHT: sScript = "shd_childnight"; break; + case CLASS_TYPE_COC: sScript = "prc_coc"; break; + case CLASS_TYPE_COMBAT_MEDIC: sScript = "prc_cbtmed"; break; + case CLASS_TYPE_CONTEMPLATIVE: sScript = "prc_contemplate"; break; + case CLASS_TYPE_CRUSADER: sScript = "tob_crusader"; iData |= 0x01; break; + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: sScript = "prc_shatterdpeak"; break; + case CLASS_TYPE_CW_SAMURAI: sScript = "prc_cwsamurai"; break; + case CLASS_TYPE_DEEPSTONE_SENTINEL: sScript = "tob_deepstone"; break; + case CLASS_TYPE_DIAMOND_DRAGON: sScript = "psi_diadra"; break; + case CLASS_TYPE_DISC_BAALZEBUL: sScript = "prc_baalzebul"; break; + case CLASS_TYPE_DISCIPLE_OF_ASMODEUS: sScript = "prc_discasmodeus"; break; + case CLASS_TYPE_DISCIPLE_OF_MEPH: sScript = "prc_discmeph"; break; + case CLASS_TYPE_DISPATER: sScript = "prc_dispater"; break; + case CLASS_TYPE_DRAGON_DEVOTEE: sScript = "prc_dragdev"; break; + case CLASS_TYPE_DRAGON_DISCIPLE: sScript = "prc_dradis"; break; + case CLASS_TYPE_DRAGON_SHAMAN: sScript = "prc_dragonshaman"; break; + case CLASS_TYPE_DRAGONFIRE_ADEPT: sScript = "inv_drgnfireadpt"; iData |= 0x01; break; + case CLASS_TYPE_DREAD_NECROMANCER: sScript = "prc_dreadnecro"; iData |= 0x03; break; + case CLASS_TYPE_DRUID: iShifting = TRUE; break; + case CLASS_TYPE_DRUNKEN_MASTER: sScript = "prc_drunk"; break; + case CLASS_TYPE_DUELIST: sScript = "prc_duelist"; break; + case CLASS_TYPE_DUSKBLADE: sScript = "prc_duskblade"; iData |= 0x03; break; + case CLASS_TYPE_ENLIGHTENEDFIST: sScript = "prc_enlfis"; break; + case CLASS_TYPE_ELEMENTAL_SAVANT: sScript = "prc_elemsavant"; break; + case CLASS_TYPE_ETERNAL_BLADE: sScript = "tob_eternalblade"; break; + case CLASS_TYPE_FACTOTUM: sScript = "prc_factotum"; break; + case CLASS_TYPE_FAVOURED_SOUL: sScript = "prc_favouredsoul"; iData |= 0x03; break; + case CLASS_TYPE_FISTRAZIEL: sScript = "prc_fistraziel"; break; + case CLASS_TYPE_FIST_OF_ZUOKEN: sScript = "psi_zuoken"; iData |= 0x01; break; + case CLASS_TYPE_FOCHLUCAN_LYRIST: sScript = "prc_fochlyr"; break; + case CLASS_TYPE_FOE_HUNTER: sScript = "prc_foe_hntr"; break; + case CLASS_TYPE_FORESTMASTER: sScript = "prc_forestmaster"; break; + case CLASS_TYPE_FORSAKER: sScript = "prc_forsaker"; break; + case CLASS_TYPE_FRE_BERSERKER: sScript = "prc_frebzk"; break; + case CLASS_TYPE_FROSTRAGER: sScript = "prc_frostrager"; break; + case CLASS_TYPE_FROST_MAGE: sScript = "prc_frostmage"; break; + case CLASS_TYPE_HALFLING_WARSLINGER: sScript = "prc_warsling"; break; + case CLASS_TYPE_HARPER: iData |= 0x03; break; + case CLASS_TYPE_HEARTWARDER: sScript = "prc_heartwarder"; break; + case CLASS_TYPE_HENSHIN_MYSTIC: sScript = "prc_henshin"; break; + case CLASS_TYPE_HEXBLADE: iData |= 0x03; break; + case CLASS_TYPE_HEXTOR: sScript = "prc_hextor"; break; + case CLASS_TYPE_IAIJUTSU_MASTER: sScript = "prc_iaijutsu_mst"; break; + case CLASS_TYPE_INCARNATE: sScript = "moi_incarnate"; break; + case CLASS_TYPE_INCARNUM_BLADE: sScript = "moi_iblade"; break; + case CLASS_TYPE_INITIATE_DRACONIC: sScript = "prc_initdraconic"; break; + case CLASS_TYPE_IRONMIND: sScript = "psi_ironmind"; break; + case CLASS_TYPE_IRONSOUL_FORGEMASTER: sScript = "moi_ironsoul"; break; + case CLASS_TYPE_JADE_PHOENIX_MAGE: sScript = "tob_jadephoenix"; break; + case CLASS_TYPE_JUDICATOR: sScript = "prc_judicator"; break; + case CLASS_TYPE_JUSTICEWW: iData |= 0x03; break; + case CLASS_TYPE_KNIGHT: sScript = "prc_knight"; break; + case CLASS_TYPE_KNIGHT_CHALICE: sScript = "prc_knghtch"; break; + case CLASS_TYPE_KNIGHT_WEAVE: iData |= 0x03; break; + case CLASS_TYPE_KNIGHT_SACRED_SEAL: sScript = "bnd_kss"; break; + case CLASS_TYPE_LASHER: sScript = "prc_lasher"; break; + case CLASS_TYPE_LEGENDARY_DREADNOUGHT: sScript = "prc_legendread"; break; + case CLASS_TYPE_LICH: sScript = "pnp_lich_level"; break; + case CLASS_TYPE_MAGEKILLER: sScript = "prc_magekill"; break; + case CLASS_TYPE_MASTER_HARPER: sScript = "prc_masterh"; break; + case CLASS_TYPE_MASTER_OF_NINE: sScript = "tob_masterofnine"; break; + case CLASS_TYPE_MASTER_OF_SHADOW: sScript = "shd_mastershadow"; break; + case CLASS_TYPE_MIGHTY_CONTENDER_KORD: sScript = "prc_contendkord"; break; + case CLASS_TYPE_MORNINGLORD: sScript = "prc_morninglord"; break; + case CLASS_TYPE_NIGHTSHADE: sScript = "prc_nightshade"; break; + case CLASS_TYPE_NINJA: sScript = "prc_ninjca"; break; + case CLASS_TYPE_OLLAM: sScript = "prc_ollam"; break; + case CLASS_TYPE_OOZEMASTER: sScript = "prc_oozemstr"; break; + case CLASS_TYPE_ORDER_BOW_INITIATE: sScript = "prc_ootbi"; break; + case CLASS_TYPE_PEERLESS: sScript = "prc_peerless"; break; + case CLASS_TYPE_PNP_SHIFTER : iShifting = TRUE; break; + case CLASS_TYPE_PRC_EYE_OF_GRUUMSH: sScript = "prc_eog"; break; + case CLASS_TYPE_PSION: iData |= 0x01; break; + case CLASS_TYPE_PSYWAR: iData |= 0x01; break; + case CLASS_TYPE_PSYCHIC_ROGUE: sScript = "psi_psyrogue"; iData |= 0x01; break; + case CLASS_TYPE_PYROKINETICIST: sScript = "psi_pyro"; break; + case CLASS_TYPE_RAGE_MAGE: iData |= 0x04; break; + case CLASS_TYPE_REAPING_MAULER : sScript = "prc_reapmauler"; break; + case CLASS_TYPE_RUBY_VINDICATOR: sScript = "tob_rubyknight"; break; + case CLASS_TYPE_RUNESCARRED: sScript = "prc_runescarred"; break; + case CLASS_TYPE_SACREDFIST: sScript = "prc_sacredfist"; break; + case CLASS_TYPE_SANCTIFIED_MIND: sScript = "psi_sancmind"; break; + case CLASS_TYPE_SAPPHIRE_HIERARCH: sScript = "moi_sapphire"; break; + case CLASS_TYPE_SCOUT: sScript = "prc_scout"; break; + case CLASS_TYPE_SERENE_GUARDIAN: sScript = "prc_sereneguard"; break; + case CLASS_TYPE_SHADOW_ADEPT: sScript = "prc_shadowadept"; break; + case CLASS_TYPE_SHADOWCASTER: sScript = "shd_shadowcaster"; iData |= 0x01; break; + case CLASS_TYPE_SHADOWSMITH: iData |= 0x01; break; + case CLASS_TYPE_SHADOW_SUN_NINJA: sScript = "tob_shadowsun"; break; + case CLASS_TYPE_SHADOWBLADE: sScript = "prc_sb_shdstlth"; break; + case CLASS_TYPE_SHADOWMIND: sScript = "psi_shadowmind"; break; + case CLASS_TYPE_SHADOWBANE_STALKER: sScript = "prc_shadstalker"; break; + case CLASS_TYPE_SHADOW_THIEF_AMN: sScript = "prc_amn"; break; + case CLASS_TYPE_SHAMAN: sScript = "prc_shaman"; break; + case CLASS_TYPE_SHINING_BLADE: sScript = "prc_sbheir"; break; + case CLASS_TYPE_SHOU: sScript = "prc_shou"; break; + case CLASS_TYPE_SKULLCLAN_HUNTER: sScript = "prc_skullclan"; break; + case CLASS_TYPE_SLAYER_OF_DOMIEL: sScript = "prc_slayerdomiel"; break; + case CLASS_TYPE_SOHEI: sScript = "prc_sohei"; break; + case CLASS_TYPE_SOLDIER_OF_LIGHT: sScript = "prc_soldoflight"; break; + case CLASS_TYPE_SORCERER: iData |= 0x03; break; + case CLASS_TYPE_SOULBORN: sScript = "moi_soulborn"; break; + case CLASS_TYPE_SOUL_EATER: iShifting = TRUE; break; + case CLASS_TYPE_SOULKNIFE: sScript = "psi_sk_clseval"; break; + case CLASS_TYPE_SPELLSWORD: sScript = "prc_spellswd"; iData |= 0x04; break; + case CLASS_TYPE_SPINEMELD_WARRIOR: sScript = "moi_spinemeld"; break; + case CLASS_TYPE_STORMLORD: sScript = "prc_stormlord"; break; + case CLASS_TYPE_SUBLIME_CHORD: sScript = "prc_schord"; iData |= 0x03; break; + case CLASS_TYPE_SUEL_ARCHANAMACH: iData |= 0x03; break; + case CLASS_TYPE_SWASHBUCKLER: sScript = "prc_swashbuckler"; break; + case CLASS_TYPE_SWIFT_WING: sScript = "prc_swiftwing"; break; + case CLASS_TYPE_SWORDSAGE: sScript = "tob_swordsage"; iData |= 0x01; break; + case CLASS_TYPE_TALON_OF_TIAMAT: sScript = "prc_talontiamat"; break; + case CLASS_TYPE_TEMPEST: sScript = "prc_tempest"; break; + case CLASS_TYPE_TEMPUS: sScript = "prc_battletempus"; break; + case CLASS_TYPE_TENEBROUS_APOSTATE: sScript = "bnd_tenebrous"; break; + case CLASS_TYPE_THAYAN_KNIGHT: sScript = "prc_thayknight"; break; + case CLASS_TYPE_THRALL_OF_GRAZZT_A: sScript = "tog"; break; + case CLASS_TYPE_THRALLHERD: sScript = "psi_thrallherd"; break; + case CLASS_TYPE_TOTEMIST: sScript = "moi_totemist"; break; + case CLASS_TYPE_TOTEM_RAGER: sScript = "moi_totemrager"; break; + case CLASS_TYPE_TRUENAMER: sScript = "true_truenamer"; iData |= 0x01; break; + case CLASS_TYPE_VASSAL: sScript = "prc_vassal"; break; + case CLASS_TYPE_VIGILANT: sScript = "prc_vigilant"; break; + case CLASS_TYPE_WARBLADE: sScript = "tob_warblade"; iData |= 0x01; break; + case CLASS_TYPE_WARCHIEF: sScript = "prc_warchief"; break; + case CLASS_TYPE_WARFORGED_JUGGERNAUT: sScript = "prc_juggernaut"; break; + case CLASS_TYPE_WARLOCK: sScript = "inv_warlock"; iData |= 0x01; break; + case CLASS_TYPE_WARMAGE: iData |= 0x03; break; + case CLASS_TYPE_WARMIND: sScript = "psi_warmind"; iData |= 0x01; break; + case CLASS_TYPE_WEREWOLF: sScript = "prc_werewolf"; break; + case CLASS_TYPE_WILDER: iData |= 0x01; break; + + // Races that can cast spells + case CLASS_TYPE_DRAGON: iData |= 0x03; break; + case CLASS_TYPE_SHAPECHANGER: iData |= 0x03; break; + case CLASS_TYPE_OUTSIDER: iData |= 0x03; break; + case CLASS_TYPE_ABERRATION: iData |= 0x03; break; + case CLASS_TYPE_MONSTROUS: iData |= 0x03; break; + case CLASS_TYPE_FEY: iData |= 0x03; break; + } + if(sScript != "") + SetPersistantLocalString(oPC, "PRC_Class_Script"+IntToString(i), sScript); + } + if (DEBUG) DoDebug("SetupCharacterData Class: " + IntToString(nClassType)); + if (DEBUG) DoDebug("SetupCharacterData Data: " + IntToString(iData)); + } + if(iData) + SetPersistantLocalInt(oPC, "PRC_Class_Data", iData); + + if(iShifting) + SetPersistantLocalInt(oPC, "PRC_UNI_SHIFT_SCRIPT", 1); + + //Setup class info for onleveldown script + int nCharData = ((GetClassByPosition(8, oPC) & 0xFF) << 56) | + ((GetClassByPosition(7, oPC) & 0xFF) << 48) | + ((GetClassByPosition(6, oPC) & 0xFF) << 40) | + ((GetClassByPosition(5, oPC) & 0xFF) << 32) | + ((GetClassByPosition(4, oPC) & 0xFF) << 24) | + ((GetClassByPosition(3, oPC) & 0xFF) << 16) | + ((GetClassByPosition(2, oPC) & 0xFF) << 8) | + (GetClassByPosition(1, oPC) & 0xFF); + + SetPersistantLocalInt(oPC, "PRC_Character_Data", nCharData); +} + +void DelayedExecuteScript(int nExpectedGeneration, string sScriptName, object oPC) +{ + if (nExpectedGeneration != GetLocalInt(oPC, PRC_EvalPRCFeats_Generation)) + { + //Generation has changed, so don't apply the effect + return; + } + ExecuteScript(sScriptName, oPC); +} + +void DelayedReApplyUnhealableAbilityDamage(int nExpectedGeneration, object oPC) +{ + if (nExpectedGeneration != GetLocalInt(oPC, PRC_ScrubPCSkin_Generation)) + { + //Generation has changed, so don't apply the effect + return; + } + ReApplyUnhealableAbilityDamage(oPC); +} + +int ToBFeats(object oPC) +{ + if(GetHasFeat(FEAT_SHADOW_BLADE, oPC) || + GetHasFeat(FEAT_RAPID_ASSAULT, oPC) || + GetHasFeat(FEAT_DESERT_WIND_DODGE, oPC) || + GetHasFeat(FEAT_DESERT_FIRE, oPC) || + GetHasFeat(FEAT_IRONHEART_AURA, oPC) || + GetHasFeat(FEAT_SHADOW_TRICKSTER, oPC) || + GetHasFeat(FEAT_WHITE_RAVEN_DEFENSE, oPC) || + GetHasFeat(FEAT_DEVOTED_BULWARK, oPC) || + GetHasFeat(FEAT_SNAP_KICK, oPC) || + GetHasFeat(FEAT_THREE_MOUNTAINS, oPC) || + GetHasFeat(FEAT_VAE_SCHOOL, oPC) || + GetHasFeat(FEAT_INLINDL_SCHOOL, oPC) || + GetHasFeat(FEAT_XANIQOS_SCHOOL, oPC) || + GetHasFeat(FEAT_SHIELD_WALL, oPC) || + GetHasFeat(FEAT_CROSSBOW_SNIPER, oPC) || + GetHasFeat(FEAT_CRESCENT_MOON, oPC) || + GetHasFeat(FEAT_QUICK_STAFF, oPC) || + GetHasFeat(FEAT_BEAR_FANG, oPC) || + GetHasFeat(FEAT_IMPROVED_RAPID_SHOT, oPC) || + GetHasFeat(FEAT_DIRE_FLAIL_SMASH, oPC) || + GetHasFeat(FEAT_SHIELD_SPECIALIZATION_LIGHT, oPC) || + GetHasFeat(FEAT_SHIELD_SPECIALIZATION_HEAVY, oPC) || + GetHasFeat(FEAT_FOCUSED_SHIELD, oPC) || + GetHasFeat(FEAT_SHIELDMATE, oPC) || + GetHasFeat(FEAT_IMPROVED_SHIELDMATE, oPC) || + GetHasFeat(FEAT_SHIELDED_CASTING, oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_DESERT_WIND , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_DEVOTED_SPIRIT, oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_DIAMOND_MIND , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_IRON_HEART , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_SETTING_SUN , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_SHADOW_HAND , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_STONE_DRAGON , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_TIGER_CLAW , oPC) || + GetHasFeat(FEAT_BLADE_MEDITATION_WHITE_RAVEN , oPC) || + GetRacialType(oPC) == RACIAL_TYPE_RETH_DEKALA || + GetRacialType(oPC) == RACIAL_TYPE_HADRIMOI) + return TRUE; + + return FALSE; +} + +void EvalPRCFeats(object oPC) +{ + // Player is currently making character in ConvoCC + // don't run EvalPRCFeats() yet + if(oPC == GetLocalObject(GetModule(), "ccc_active_pc")) + return; + + int nGeneration = PRC_NextGeneration(GetLocalInt(oPC, PRC_EvalPRCFeats_Generation)); + if (DEBUG > 1) DoDebug("EvalPRCFeats Generation: " + IntToString(nGeneration)); + SetLocalInt(oPC, PRC_EvalPRCFeats_Generation, nGeneration); + + //permanent ability changes + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + ExecuteScript("prc_nwnx_funcs", oPC); + + //Add IP Feats to the hide + DelayCommand(0.0f, DelayedAddIPFeats(nGeneration, oPC)); + + // If there is a bonus domain, it will always be in the first slot, so just check that. + // It also runs things that clerics with those domains need + if (GetPersistantLocalInt(oPC, "PRCBonusDomain1") > 0 || GetLevelByClass(CLASS_TYPE_CLERIC, oPC) || GetLevelByClass(CLASS_TYPE_SHAMAN, oPC)) + DelayCommand(0.1f, ExecuteScript("prc_domain_skin", oPC)); + + // special add atk bonus equal to Enhancement + ExecuteScript("ft_sanctmartial", oPC); + + //hook in the weapon size restrictions script + //ExecuteScript("prc_restwpnsize", oPC); //<- Script no longer exists + + //Route the event to the appropriate class specific scripts + int i, iData; + string sScript; + for (i = 1; i <= 8; i++) + { + sScript = GetPersistantLocalString(oPC, "PRC_Class_Script"+IntToString(i)); + if(sScript != "") + { + if (DEBUG) DoDebug("PRC_Class_Script: "+sScript); + ExecuteScript(sScript, oPC); + } + } + + iData = GetPersistantLocalInt(oPC, "PRC_Class_Data"); + + // Handle alternate caster types gaining new stuff + if(iData & 0x01) + ExecuteScript("prc_amagsys_gain", oPC); + + // Add ip feats for spontaneous casters using metamagic + if(iData & 0x02) + ExecuteScript("prc_metamagic", oPC); + + // Handle classes with reduced arcane spell failure + if(iData & 0x04) + ExecuteScript("prc_reducedasf", oPC); + + if(GetPersistantLocalInt(oPC, "PRC_UNI_SHIFT_SCRIPT")) + //Executing shifter-related stuff like this has these advantages: + //1) All races and classes that need it can get it without a different script needing to be created for each. + //2) PCs with shifter-related stuff from multiple classes or races don't run the same functions multiple times. + ExecuteScript("prc_uni_shift", oPC); + + // Templates + //these go here so feats can be reused + ExecuteScript("prc_templates", oPC); + + // Feats + //these are here so if templates add them the if check runs after the template was applied + ExecuteScript("prc_feats", oPC); + + if (ToBFeats(oPC)) + ExecuteScript("tob_feats", oPC); + + if (GetIsIncarnumUser(oPC) || GetRacialType(oPC) == RACIAL_TYPE_JAEBRIN) + ExecuteScript("moi_events", oPC); + + if (GetIsBinder(oPC)) + ExecuteScript("bnd_events", oPC); + + // check if character with crafting feat has appropriate base item in her inventory + // x - moved from prc_onhb_indiv.nss + if(GetPRCSwitch(PRC_CRAFTING_BASE_ITEMS)) + DelayCommand(0.5f, ExecuteScript("prc_crftbaseitms", oPC)); + + // Add the teleport management feats. + // 2005.11.03: Now added to all base classes on 1st level - Ornedan +// ExecuteScript("prc_tp_mgmt_eval", oPC); + + // Size changes. Removed due to double-dipping most size adjustments + //ExecuteScript("prc_size", oPC); + + // Speed changes + // The local int is for when items are requipped too quickly and this script bugs out + if (!GetLocalInt(oPC, "PRCSpeedDelay")) + { + ExecuteScript("prc_speed", oPC); + SetLocalInt(oPC, "PRCSpeedDelay", TRUE); + DelayCommand(0.15, DeleteLocalInt(oPC, "PRCSpeedDelay")); + } + + // ACP system + if((GetIsPC(oPC) && + (GetPRCSwitch(PRC_ACP_MANUAL) || + GetPRCSwitch(PRC_ACP_AUTOMATIC) + ) + ) || + (!GetIsPC(oPC) && + GetPRCSwitch(PRC_ACP_NPC_AUTOMATIC) + ) + ) + ExecuteScript("acp_auto", oPC); + +// this is handled inside the PRC Options conversation now. +/* // Epic spells + if((GetCasterLvl(CLASS_TYPE_CLERIC, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_DRUID, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_SORCERER, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_FAVOURED_SOUL, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_HEALER, oPC) >= 21 || + GetCasterLvl(CLASS_TYPE_WIZARD, oPC) >= 21 + ) && + !GetHasFeat(FEAT_EPIC_SPELLCASTING_REST, oPC) + ) + { + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_REST), 0.0f, + X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + }*/ + + // Miscellaneous + ExecuteScript("prc_sneak_att", oPC); + ExecuteScript("race_skin", oPC); + ExecuteScript("prc_mithral", oPC); + if(GetPRCSwitch(PRC_ENFORCE_RACIAL_APPEARANCE) && GetIsPC(oPC)) + ExecuteScript("race_appear", oPC); + + //Reserve Feats + if(!GetLocalInt(oPC, "ReserveFeatsRunning")) + { + if(GetHasFeat(FEAT_HOLY_WARRIOR, oPC) || + GetHasFeat(FEAT_MYSTIC_BACKLASH, oPC) || + GetHasFeat(FEAT_ACIDIC_SPLATTER, oPC) || + GetHasFeat(FEAT_FIERY_BURST, oPC) || + GetHasFeat(FEAT_STORM_BOLT, oPC) || + GetHasFeat(FEAT_WINTERS_BLAST, oPC) || + GetHasFeat(FEAT_CLAP_OF_THUNDER, oPC) || + GetHasFeat(FEAT_SICKENING_GRASP, oPC) || + GetHasFeat(FEAT_TOUCH_OF_HEALING, oPC) || + GetHasFeat(FEAT_DIMENSIONAL_JAUNT, oPC) || + GetHasFeat(FEAT_CLUTCH_OF_EARTH, oPC) || + GetHasFeat(FEAT_BORNE_ALOFT, oPC) || + GetHasFeat(FEAT_PROTECTIVE_WARD, oPC) || + GetHasFeat(FEAT_SHADOW_VEIL, oPC) || + GetHasFeat(FEAT_SUNLIGHT_EYES, oPC) || + GetHasFeat(FEAT_TOUCH_OF_DISTRACTION, oPC) || + GetHasFeat(FEAT_UMBRAL_SHROUD, oPC) || + GetHasFeat(FEAT_CHARNEL_MIASMA, oPC) || + GetHasFeat(FEAT_DROWNING_GLANCE, oPC) || + GetHasFeat(FEAT_INVISIBLE_NEEDLE, oPC) || + GetHasFeat(FEAT_SUMMON_ELEMENTAL, oPC) || + GetHasFeat(FEAT_DIMENSIONAL_REACH, oPC) || + GetHasFeat(FEAT_HURRICANE_BREATH, oPC) || + GetHasFeat(FEAT_MINOR_SHAPESHIFT, oPC) || + GetHasFeat(FEAT_FACECHANGER, oPC)) + { + ExecuteScript("prc_reservefeat", oPC); + } + } + + /*if(// Psionics + GetLevelByClass(CLASS_TYPE_PSION, oPC) || + GetLevelByClass(CLASS_TYPE_WILDER, oPC) || + GetLevelByClass(CLASS_TYPE_PSYWAR, oPC) || + GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oPC) || + GetLevelByClass(CLASS_TYPE_WARMIND, oPC) || + // New spellbooks + GetLevelByClass(CLASS_TYPE_BARD, oPC) || + GetLevelByClass(CLASS_TYPE_SORCERER, oPC) || + GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oPC) || + GetLevelByClass(CLASS_TYPE_FAVOURED_SOUL, oPC) || + GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) || + GetLevelByClass(CLASS_TYPE_HEXBLADE, oPC) || + GetLevelByClass(CLASS_TYPE_DUSKBLADE, oPC) || + GetLevelByClass(CLASS_TYPE_WARMAGE, oPC) || + GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER,oPC) || + GetLevelByClass(CLASS_TYPE_JUSTICEWW, oPC) || + GetLevelByClass(CLASS_TYPE_WITCH, oPC) || + GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oPC) || + GetLevelByClass(CLASS_TYPE_ARCHIVIST, oPC) || + GetLevelByClass(CLASS_TYPE_BEGUILER, oPC) || + GetLevelByClass(CLASS_TYPE_HARPER, oPC) || + GetLevelByClass(CLASS_TYPE_TEMPLAR, oPC) || + // Truenaming + GetLevelByClass(CLASS_TYPE_TRUENAMER, oPC) || + // Tome of Battle + GetLevelByClass(CLASS_TYPE_CRUSADER, oPC) || + GetLevelByClass(CLASS_TYPE_SWORDSAGE, oPC) || + GetLevelByClass(CLASS_TYPE_WARBLADE, oPC) || + // Invocations + GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oPC) || + GetLevelByClass(CLASS_TYPE_WARLOCK, oPC) || + // Racial casters + (GetLevelByClass(CLASS_TYPE_OUTSIDER, oPC) && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + ) + { + DelayCommand(1.0, DelayedExecuteScript(nGeneration, "prc_amagsys_gain", oPC)); + + //add selectable metamagic feats for spontaneous spellcasters + ExecuteScript("prc_metamagic", oPC); + }*/ + + // Gathers all the calls to UnarmedFists & Feats to one place. + // Must be after all evaluationscripts that need said functions. + //if(GetLocalInt(oPC, "CALL_UNARMED_FEATS") || GetLocalInt(oPC, "CALL_UNARMED_FISTS")) // ExecuteScript() is pretty expensive, do not run it needlessly - 20060702, Ornedan + ExecuteScript("unarmed_caller", oPC); + + // Gathers all the calls to SetBaseAttackBonus() to one place + // Must be after all evaluationscripts that need said function. + ExecuteScript("prc_bab_caller", oPC); + + // Classes an invoker can take + if( GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oPC) || + GetLevelByClass(CLASS_TYPE_ACOLYTE, oPC) || + GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oPC) || + GetLevelByClass(CLASS_TYPE_ARCTRICK, oPC) || + GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oPC) || + GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oPC) || + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oPC) || + GetLevelByClass(CLASS_TYPE_MAESTER, oPC) || + GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oPC) || + GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oPC) || + GetLevelByClass(CLASS_TYPE_VIRTUOSO, oPC) || + GetLevelByClass(CLASS_TYPE_WILD_MAGE, oPC)) + { + //Arcane caster first class position, take arcane + if(GetFirstArcaneClassPosition(oPC) == 1) + SetLocalInt(oPC, "INV_Caster", 1); + //Invoker first class position. take invoker + else if(GetClassByPosition(1, oPC) == CLASS_TYPE_WARLOCK || GetClassByPosition(1, oPC) == CLASS_TYPE_DRAGONFIRE_ADEPT) + SetLocalInt(oPC, "INV_Caster", 2); + //Non arcane first class position, invoker second. Take invoker + else if(GetFirstArcaneClassPosition(oPC) ==0 && (GetClassByPosition(2, oPC) == CLASS_TYPE_WARLOCK || GetClassByPosition(2, oPC) == CLASS_TYPE_DRAGONFIRE_ADEPT)) + SetLocalInt(oPC, "INV_Caster", 2); + //last class would be Non-invoker first class position, arcane second position. take arcane. + else + SetLocalInt(oPC, "INV_Caster", 1); + + //:: Set arcane or invocation bonus caster levels + if ( GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION, oPC) > 0 && + GetHasFeat(FEAT_ABCHAMP_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_ACOLYTE, oPC) > 0 && + GetHasFeat(FEAT_AOTS_NONE, oPC) || GetHasFeat(FEAT_AOTS_MYSTERY_SHADOWCASTER, oPC) || GetHasFeat(FEAT_AOTS_MYSTERY_SHADOWSMITH, oPC)) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oPC) > 0 && + GetHasFeat(FEAT_ANIMA_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_ARCTRICK, oPC) > 0 && + GetHasFeat(FEAT_ARCTRICK_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_BLOOD_MAGUS, oPC) > 0 && + GetHasFeat(FEAT_BLDMAGUS_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oPC) > 0 && + GetHasFeat(FEAT_ASMODEUS_NONE, oPC) || GetHasFeat(FEAT_ASMODEUS_MYSTERY_SHADOWCASTER, oPC) || GetHasFeat(FEAT_ASMODEUS_MYSTERY_SHADOWSMITH, oPC)) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oPC) > 0 && + GetHasFeat(FEAT_ENLIGHTENEDFIST_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_MAESTER, oPC) > 0 && + GetHasFeat(FEAT_MAESTER_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oPC) > 0 && + GetHasFeat(FEAT_TIAMAT_NONE, oPC) || GetHasFeat(FEAT_TIAMAT_MYSTERY_SHADOWCASTER, oPC) || GetHasFeat(FEAT_TIAMAT_MYSTERY_SHADOWSMITH, oPC)) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oPC) > 0 && + GetHasFeat(FEAT_UNSEEN_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_VIRTUOSO, oPC) > 0 && + GetHasFeat(FEAT_VIRTUOSO_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + + if ( GetLevelByClass(CLASS_TYPE_WILD_MAGE, oPC) > 0 && + GetHasFeat(FEAT_WILDMAGE_NONE, oPC) ) + { + SetLocalInt(oPC, "INV_Caster", 1); + } + else + { + SetLocalInt(oPC, "INV_Caster", 2); + } + +/* //Arcane caster first class position, take arcane + if(GetFirstArcaneClassPosition(oPC) == 1) + SetLocalInt(oPC, "INV_Caster", 1); + //Invoker first class position. take invoker + else if(GetClassByPosition(1, oPC) == CLASS_TYPE_WARLOCK || GetClassByPosition(1, oPC) == CLASS_TYPE_DRAGONFIRE_ADEPT) + SetLocalInt(oPC, "INV_Caster", 2); + //Non arcane first class position, invoker second. Take invoker + else if(GetFirstArcaneClassPosition(oPC) ==0 && (GetClassByPosition(2, oPC) == CLASS_TYPE_WARLOCK || GetClassByPosition(2, oPC) == CLASS_TYPE_DRAGONFIRE_ADEPT)) + SetLocalInt(oPC, "INV_Caster", 2); + //last class would be Non-invoker first class position, arcane second position. take arcane. + else + SetLocalInt(oPC, "INV_Caster", 1); */ + } +} + +void DelayedAddIPFeats(int nExpectedGeneration, object oPC) +{ + if (nExpectedGeneration != GetLocalInt(oPC, PRC_EvalPRCFeats_Generation)) + { + //Generation has changed, so don't apply the effect + return; + } + + object oSkin = GetPCSkin(oPC); + + //Horse menu + AddSkinFeat(FEAT_HORSE_MENU, 40, oSkin, oPC); + + // Switch convo feat + //Now everyone gets it at level 1, but just to be on the safe side + AddSkinFeat(FEAT_OPTIONS_CONVERSATION, 229, oSkin, oPC); + + //PnP familiars + if(GetHasFeat(FEAT_SUMMON_FAMILIAR, oPC) && GetPRCSwitch(PRC_PNP_FAMILIARS)) + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_PNP_FAMILIAR), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + else if(!GetHasFeat(FEAT_SUMMON_FAMILIAR, oPC) || !GetPRCSwitch(PRC_PNP_FAMILIARS)) + RemoveItemProperty(oSkin, ItemPropertyBonusFeat(IP_CONST_PNP_FAMILIAR)); + + //PnP Spell Schools + if(GetPRCSwitch(PRC_PNP_SPELL_SCHOOLS) + && GetLevelByClass(CLASS_TYPE_WIZARD, oPC) + && !GetIsPolyMorphedOrShifted(oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_GENERAL, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_ABJURATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_CONJURATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_DIVINATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_ENCHANTMENT, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_EVOCATION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_ILLUSION, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_NECROMANCY, oPC) + && !GetHasFeat(FEAT_PNP_SPELL_SCHOOL_TRANSMUTATION, oPC) + //&& !PRCGetHasEffect(EFFECT_TYPE_POLYMORPH, oPC) //so it doesnt pop up on polymorphing + //&& !GetLocalInt(oSkin, "nPCShifted") //so it doenst pop up on shifting + ) + { + if(GetXP(oPC))// ConvoCC compatibility fix + ExecuteScript("prc_pnp_shcc_s", oPC); + } + + /*//Arcane Archer old imbue arrow + if(GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER, oPC) >= 2 + && !GetHasFeat(FEAT_PRESTIGE_IMBUE_ARROW, oPC) + && GetPRCSwitch(PRC_PNP_SPELL_SCHOOLS)) + { + //add the old feat to the hide + IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_FEAT_FEAT_PRESTIGE_IMBUE_ARROW), 0.0f, + X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + }*/ + + //handle PnP sling switch + if(GetPRCSwitch(PRC_PNP_SLINGS)) + { + if(GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)) == BASE_ITEM_SLING) + IPSafeAddItemProperty(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC), + ItemPropertyMaxRangeStrengthMod(20), + 999999.9); + } +} + +void TemplateSLAs(object oPC) +{ + int i; + for(i = TEMPLATE_SLA_START; i <= TEMPLATE_SLA_END; i++) + { + DeleteLocalInt(oPC, "TemplateSLA_"+IntToString(i)); + } +} + +void DeletePRCLocalInts(object oSkin) +{ + // This will get rid of any SetCompositeAttackBonus LocalInts: + object oPC = GetItemPossessor(oSkin); + DeleteLocalInt(oPC, "CompositeAttackBonusR"); + DeleteLocalInt(oPC, "CompositeAttackBonusL"); + + //Do not use DelayCommand for this--it's too dangerous: + //if SetCompositeAttackBonus is called at the wrong time the result will be incorrect. + DeleteNamedComposites(oPC, "PRC_ComAttBon"); + + // PRCGetClassByPosition and PRCGetLevelByPosition cleanup + // not needed now that GetClassByPosition() works for custom classes + // DeleteLocalInt(oPC, "PRC_ClassInPos1"); + // DeleteLocalInt(oPC, "PRC_ClassInPos2"); + // DeleteLocalInt(oPC, "PRC_ClassInPos3"); + // DeleteLocalInt(oPC, "PRC_ClassLevelInPos1"); + // DeleteLocalInt(oPC, "PRC_ClassLevelInPos2"); + // DeleteLocalInt(oPC, "PRC_ClassLevelInPos3"); + + //persistant local token object cache + //looks like logging off then back on without the server rebooting breaks it + //I guess because the token gets a new ID, but the local still points to the old one + DeleteLocalObject(oPC, "PRC_HideTokenCache"); + + DeleteLocalInt(oSkin, "PRC_ArcaneSpellFailure"); + + DeleteLocalInt(oPC, "PRC_SwiftActionUsed"); + DeleteLocalInt(oPC, "PRC_MoveActionUsed"); + + // In order to work with the PRC system we need to delete some locals for each + // PRC that has a hide + + //Do not use DelayCommand for this--it's too dangerous: + //if SetCompositeBonus is called between the time EvalPRCFeats removes item properties + //and the time this delayed command is executed, the result will be incorrect. + //Since this error case actually happens frequently with any delay here, just don't do it. + DeleteNamedComposites(oSkin, "PRC_CBon"); + + if (DEBUG) DoDebug("Clearing class flags"); + + // Elemental Savants + DeleteLocalInt(oSkin,"ElemSavantResist"); + DeleteLocalInt(oSkin,"ElemSavantPerfection"); + DeleteLocalInt(oSkin,"ElemSavantImmMind"); + DeleteLocalInt(oSkin,"ElemSavantImmParal"); + DeleteLocalInt(oSkin,"ElemSavantImmSleep"); + // HeartWarder + DeleteLocalInt(oSkin,"FeyType"); + // OozeMaster + DeleteLocalInt(oSkin,"IndiscernibleCrit"); + DeleteLocalInt(oSkin,"IndiscernibleBS"); + DeleteLocalInt(oSkin,"OneOozeMind"); + DeleteLocalInt(oSkin,"OneOozePoison"); + // Storm lord + DeleteLocalInt(oSkin,"StormLResElec"); + // Spell sword + DeleteLocalInt(oSkin,"SpellswordSFBonusNormal"); + DeleteLocalInt(oSkin,"SpellswordSFBonusEpic"); + // Acolyte of the skin + DeleteLocalInt(oSkin,"AcolyteSymbBonus"); + DeleteLocalInt(oSkin,"AcolyteResistanceCold"); + DeleteLocalInt(oSkin,"AcolyteResistanceFire"); + DeleteLocalInt(oSkin,"AcolyteResistanceAcid"); + DeleteLocalInt(oSkin,"AcolyteResistanceElectric"); + // Battleguard of Tempus + DeleteLocalInt(oSkin,"FEAT_WEAP_TEMPUS"); + // Bonded Summoner + DeleteLocalInt(oSkin,"BondResEle"); + DeleteLocalInt(oSkin,"BondSubType"); + // Disciple of Meph + DeleteLocalInt(oSkin,"DiscMephResist"); + DeleteLocalInt(oSkin,"DiscMephGlove"); + // Initiate of Draconic Mysteries + DeleteLocalInt(oSkin,"IniSR"); + DeleteLocalInt(oSkin,"IniStunStrk"); + // Man at Arms + DeleteLocalInt(oSkin,"ManArmsCore"); + // Telflammar Shadowlord + DeleteLocalInt(oSkin,"ShaDiscorp"); + // Vile Feats + DeleteLocalInt(oSkin,"DeformGaunt"); + DeleteLocalInt(oSkin,"DeformObese"); + // Sneak Attack + DeleteLocalInt(oSkin,"RogueSneakDice"); + DeleteLocalInt(oSkin,"BlackguardSneakDice"); + // Sacred Fist + DeleteLocalInt(oSkin,"SacFisMv"); + // Minstrel + DeleteLocalInt(oSkin,"MinstrelSFBonus"); //:: @todo Make ASF reduction compositable + // Nightshade + DeleteLocalInt(oSkin,"ImmuNSWeb"); + DeleteLocalInt(oSkin,"ImmuNSPoison"); + // Soldier of Light + DeleteLocalInt(oSkin,"ImmuPF"); + // Ultimate Ranger + DeleteLocalInt(oSkin,"URImmu"); + // Thayan Knight + DeleteLocalInt(oSkin,"ThayHorror"); + DeleteLocalInt(oSkin,"ThayZulkFave"); + DeleteLocalInt(oSkin,"ThayZulkChamp"); + // Black Flame Zealot + DeleteLocalInt(oSkin,"BFZHeart"); + // Henshin Mystic + DeleteLocalInt(oSkin,"Happo"); + DeleteLocalInt(oSkin,"HMInvul"); + //Blightlord + DeleteLocalInt(oSkin, "WntrHeart"); + DeleteLocalInt(oSkin, "BlightBlood"); + // Contemplative + DeleteLocalInt(oSkin, "ContempDisease"); + DeleteLocalInt(oSkin, "ContempPoison"); + DeleteLocalInt(oSkin, "ContemplativeDR"); + DeleteLocalInt(oSkin, "ContemplativeSR"); + // Dread Necromancer + DeleteLocalInt(oSkin, "DNDamageResist"); + // Warsling Sniper + DeleteLocalInt(oPC, "CanRicochet"); + // Blood Magus + DeleteLocalInt(oSkin, "ThickerThanWater"); + //:: Crusader + DeleteLocalInt(oPC, "DelayedDamageHB"); + //:: Factotum + DeleteLocalInt(oPC, "InspirationHB"); + + // Feats + DeleteLocalInt(oPC, "ForceOfPersonalityWis"); + DeleteLocalInt(oPC, "ForceOfPersonalityCha"); + DeleteLocalInt(oPC, "InsightfulReflexesInt"); + DeleteLocalInt(oPC, "InsightfulReflexesDex"); + DeleteLocalInt(oSkin, "TactileTrapsmithSearchIncrease"); + DeleteLocalInt(oSkin, "TactileTrapsmithDisableIncrease"); + DeleteLocalInt(oSkin, "TactileTrapsmithSearchDecrease"); + DeleteLocalInt(oSkin, "TactileTrapsmithDisableDecrease"); + + // Warmind + DeleteLocalInt(oSkin, "EnduringBody"); + + // Ironmind + DeleteLocalInt(oSkin, "IronMind_DR"); + + // Suel Archanamach + DeleteLocalInt(oSkin, "SuelArchanamachSpellFailure"); + + // Favoured Soul + DeleteLocalInt(oSkin, "FavouredSoulResistElementAcid"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementCold"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementElec"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementFire"); + DeleteLocalInt(oSkin, "FavouredSoulResistElementSonic"); + DeleteLocalInt(oSkin, "FavouredSoulDR"); + + // Domains + DeleteLocalInt(oSkin, "StormDomainPower"); + + // Skullclan Hunter + DeleteLocalInt(oSkin, "SkullClanFear"); + DeleteLocalInt(oSkin, "SkullClanDisease"); + DeleteLocalInt(oSkin, "SkullClanProtectionEvil"); + DeleteLocalInt(oSkin, "SkullClanSwordLight"); + DeleteLocalInt(oSkin, "SkullClanParalysis"); + DeleteLocalInt(oSkin, "SkullClanAbilityDrain"); + DeleteLocalInt(oSkin, "SkullClanLevelDrain"); + + // Sohei + DeleteLocalInt(oSkin, "SoheiDamageResist"); + + // Dragon Disciple + DeleteLocalInt(oPC, "DragonDiscipleBreathWeaponUses"); + + //Dragon Shaman + DeleteLocalInt(oPC, "DragonShamanTotem"); + + //Warblade + DeleteLocalInt(oSkin, "PRC_WEAPON_APTITUDE_APPLIED"); + + //Shifter(PnP) + DeleteLocalInt(oSkin, "PRC_SHIFTER_TEMPLATE_APPLIED"); + + DeleteLocalInt(oPC, "ScoutFreeMove"); + DeleteLocalInt(oPC, "ScoutFastMove"); + DeleteLocalInt(oPC, "ScoutBlindsight"); + + //Truenamer + // Called elsewhere now + /*int UtterID; + for(UtterID = 3526; UtterID <= 3639; UtterID++) // All utterances + DeleteLocalInt(oPC, "PRC_LawOfResistance" + IntToString(UtterID)); + for(UtterID = 3418; UtterID <= 3431; UtterID++) // Syllable of Detachment to Word of Heaven, Greater + DeleteLocalInt(oPC, "PRC_LawOfResistance" + IntToString(UtterID));*/ + + //Invocations + DeleteLocalInt(oPC, "ChillingFogLock"); + //Endure Exposure wearing off + array_delete(oPC, "BreathProtected"); + DeleteLocalInt(oPC, "DragonWard"); + + //Scry on Familiar + DeleteLocalInt(oPC, "Scry_Familiar"); + + // Undead HD + DeleteLocalInt(oPC, "PRCUndeadHD"); + DeleteLocalInt(oPC, "PRCUndeadFSPen"); + + //Template Spell-Like Abilities + DelayCommand(0.5f, TemplateSLAs(oPC)); + + // future PRCs Go below here +} + +void ScrubPCSkin(object oPC, object oSkin) +{ + int nGeneration = PRC_NextGeneration(GetLocalInt(oPC, PRC_ScrubPCSkin_Generation)); + if (DEBUG > 1) DoDebug("ScrubPCSkin Generation: " + IntToString(nGeneration)); + SetLocalInt(oPC, PRC_ScrubPCSkin_Generation, nGeneration); + + int iCode = GetHasFeat(FEAT_SF_CODE,oPC); + int ipType, st; + if(!(/*GetIsPolyMorphedOrShifted(oPC) || */GetIsObjectValid(GetMaster(oPC)))) + { + itemproperty ip = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ip)) + { + // Insert Logic here to determine if we spare a property + ipType = GetItemPropertyType(ip); + if(ipType == ITEM_PROPERTY_BONUS_FEAT) + { + // Check for specific Bonus Feats + // Reference iprp_feats.2da + st = GetItemPropertySubType(ip); + + // Spare 400 through 570 and 398 -- epic spells & spell effects + //also spare the new spellbook feats (1000-12000 & 17701-24704 + //also spare the psionic, trunaming, tob, invocation feats (12000-16000) + // spare template, tob stuff (16300-17700) + // changed by fluffyamoeba so that iprp weapon specialization, dev crit, epic weapon focus, epic weapon spec + // overwhelming crit and weapon of choice are no longer skipped. + if ((st < 400 || st > 570) + && st != 102 //:: ACP Feats + && st != 586 //:: ACP Feats + && st != 587 //:: ACP Feats + && st != 398 + && (st < 1000 || st > 13520) + //&& (st < 1000 || st > 13999) + //&& (st < 14501 || st > 15999) + && (st < 16300 || st > 24704) + && (st < 223 || st > 226) // draconic feats + && (st < 229 || st > 249) // Pnp spellschool feats and PRC options feat (231-249 & 229) + && st != 259 // 259 - psionic focus + && (st < 141 || st > 200) // 141 - shadowmaster shades, 142-151 bonus domains casting feats, 152 - 200 bonus domain powers + && st != 26000 // Bullybasher Giant Bearing + && ( (st == IP_CONST_FEAT_PRC_POWER_ATTACK_QUICKS_RADIAL || + st == IP_CONST_FEAT_POWER_ATTACK_SINGLE_RADIAL || + st == IP_CONST_FEAT_POWER_ATTACK_FIVES_RADIAL) ? // Remove the PRC Power Attack radials if the character no longer has Power Attack + !GetHasFeat(FEAT_POWER_ATTACK, oPC) : + TRUE // If the feat is not relevant to this clause, always pass + ) + ) + RemoveItemProperty(oSkin, ip); + } + else if(ipType == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + //bonus spellslots code here + //st = GetItemPropertySubType(ip); + RemoveItemProperty(oSkin, ip); + } + else + RemoveItemProperty(oSkin, ip); + + // Get the next property + ip = GetNextItemProperty(oSkin); + } + } + if(iCode) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(381),oSkin); + + // Schedule restoring the unhealable ability damage + DelayCommand(0.1f, DelayedReApplyUnhealableAbilityDamage(nGeneration, oPC)); + + // Remove all natural weapons too + // ClearNaturalWeapons(oPC); + // Done this way to remove prc_inc_natweap and prc_inc_combat from the include + // Should help with compile speeds and the like + //array_delete(oPC, "ARRAY_NAT_SEC_WEAP_RESREF"); + //array_delete(oPC, "ARRAY_NAT_PRI_WEAP_RESREF"); + //array_delete(oPC, "ARRAY_NAT_PRI_WEAP_ATTACKS"); +} + +int BlastInfidelOrFaithHeal(object oCaster, object oTarget, int iEnergyType, int iDisplayFeedback) +{ + //Don't bother doing anything if iEnergyType isn't either positive/negative energy + if(iEnergyType != DAMAGE_TYPE_POSITIVE && iEnergyType != DAMAGE_TYPE_NEGATIVE) + return FALSE; + + //If the target is undead and damage type is negative + //or if the target is living and damage type is positive + //then we're healing. Otherwise, we're harming. + int iTombTainted = GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD; + int iHeal = ( iEnergyType == DAMAGE_TYPE_NEGATIVE && (MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || iTombTainted)) || + ( iEnergyType == DAMAGE_TYPE_POSITIVE && MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD && !iTombTainted); + int iRetVal = FALSE; + int iAlignDif = CompareAlignment(oCaster, oTarget); + string sFeedback = ""; + + if(iHeal){ + if((GetHasFeat(FEAT_FAITH_HEALING, oCaster) && iAlignDif < 2)){ + iRetVal = TRUE; + sFeedback = "Faith Healing"; + } + } + else{ + if((GetHasFeat(FEAT_BLAST_INFIDEL, oCaster) && iAlignDif >= 2)){ + iRetVal = TRUE; + sFeedback = "Blast Infidel"; + } + } + + if(iDisplayFeedback) FloatingTextStringOnCreature(sFeedback, oCaster); + return iRetVal; +} + +int GetShiftingFeats(object oPC) +{ + int nNumFeats; + nNumFeats = GetHasFeat(FEAT_BEASTHIDE_ELITE, oPC) + + GetHasFeat(FEAT_DREAMSIGHT_ELITE, oPC) + + GetHasFeat(FEAT_GOREBRUTE_ELITE, oPC) + + GetHasFeat(FEAT_LONGSTRIDE_ELITE, oPC) + + GetHasFeat(FEAT_LONGTOOTH_ELITE, oPC) + + GetHasFeat(FEAT_RAZORCLAW_ELITE, oPC) + + GetHasFeat(FEAT_WILDHUNT_ELITE, oPC) + + GetHasFeat(FEAT_EXTRA_SHIFTER_TRAIT, oPC) + + GetHasFeat(FEAT_HEALING_FACTOR, oPC) + + GetHasFeat(FEAT_SHIFTER_AGILITY, oPC) + + GetHasFeat(FEAT_SHIFTER_DEFENSE, oPC) + + GetHasFeat(FEAT_GREATER_SHIFTER_DEFENSE, oPC) + + GetHasFeat(FEAT_SHIFTER_FEROCITY, oPC) + + GetHasFeat(FEAT_SHIFTER_INSTINCTS, oPC) + + GetHasFeat(FEAT_SHIFTER_SAVAGERY, oPC); + + return nNumFeats; +} + +//Including DelayedApplyEffectToObject here because it is often used in conjunction with EvalPRCFeats and I don't know a better place to put it +void DelayedApplyEffectToObject(int nExpectedGeneration, int nCurrentGeneration, int nDuration, effect eEffect, object oTarget, float fDuration) +{ + if (nExpectedGeneration != nCurrentGeneration) + { + //Generation has changed, so don't apply the effect + return; + } + ApplyEffectToObject(nDuration, eEffect, oTarget, fDuration); +} + +//Including DelayedApplyEffectToObject here because it is often used in conjunction with EvalPRCFeats and I don't know a better place to put it +void DelayApplyEffectToObject(float fDelay, string sGenerationName, int nDuration, effect eEffect, object oTarget, float fDuration = 0.0f) +{ + /* + There are a couple of problems that can arise in code that removes and reapplies effects; + this function helps deal with those problems. One example of a typical place where these problems + frequently arise is in the class scripts called by the EvalPRCFeats function. + + The first problem is that when code removes and immediately reapplies a series of effects, + some of those effects may not actually be reapplied. This is because the RemoveEffect() function + doesn't actually remove an effect, it marks it to be removed later--when the currently running + script finishes. If any of the effects we reapply matches one of the effects marked to be + removed, that reapplied effect will be removed when the currently running script finishes + and so will be unexpectedly missing. To illustrate: + 1) We start with effect A and B. + 2) The application function is called; it removes all effects and reapplies effects B and C. + 3) The actual removal happens when the script ends: effect A and B are removed. + End result: we have only effect C instead of the expected B and C. + The solution to this is to reapply the effects later using DelayCommand(). + + This introduces a new problem. If the function that removes and reapplies the effects is called + multiple times quickly, it can queue up a series of delayed applications. This causes two problems: + if the data on which the effects are calculated changes, the earlier delayed applications can + apply effects that should no longer be used, but they are anyway because the delayed code doesn't + know this. To illustrate: + 1) The application function is called; it removes all effects, schedules delayed application of effect A. + 2) The application function is called again; it removes all effects, schedules delayed application of effect B. + 3) Delayed application of effect A occurs. + 4) Delayed application of effect B occurs. + End result: we have both effect A and B instead of the expected result, B alone. + Another problem is that we can end up with multiple copies of the same effect. + If this happens enough, it can cause "Effect List overflow" errors. Also, if the effect stacks + with itself, this gives a greater bonus or penalty than it should. To illustrate: + 1) The application function is called; it removes all effects, schedules delayed application of effect C. + 2) The application function is called; it removes all effects, schedules delayed application of effect C. + 3) Delayed application of effect C occurs. + 4) Delayed application of effect C occurs. + End result: we have effect C twice instead of just once. + The solution is to both these problems is for the application function to increment an integer each time it + is called and to pass this to the delayed application function. The delayed application actually happens only + if the generation when it runs is the same as the generation when it was scheduled. To illustrate: + 1) We start with effect A and B applied. + 2) The application function is called: it increments generation to 2, schedules delayed application of effect B and C. + 3) The application function is called: it increments generation to 3, schedules delayed application of effect C. + 4) The generation 2 delayed application function executes: it sees that the current generation is 3 and simply exits, doing nothing. + 5) The generation 3 delayed application function executes: it sees that the current generation is 3, so it applies effect C. + End result: we have one copy of effect C, which is what we wanted. + */ + + if (fDelay < 0.0f || GetStringLength(sGenerationName) == 0) + { + ApplyEffectToObject(nDuration, eEffect, oTarget, fDuration); + } + else + { + int nExpectedGeneration = GetLocalInt(oTarget, sGenerationName); //This gets the generation now + DelayCommand( + fDelay, + DelayedApplyEffectToObject( + nExpectedGeneration, + GetLocalInt(oTarget, sGenerationName), //This is delayed by the DelayCommand, so it gets the generation when DelayedApplyEffectToObject is actually executed + nDuration, + eEffect, + oTarget, + fDuration + ) + ); + } +} + +//Including DelayedAddItemProperty here to keep it with DelayedApplyEffectToObject, though more properly it should probably be in inc_item_props.nss +void DelayedAddItemProperty(int nExpectedGeneration, int nCurrentGeneration, int nDurationType, itemproperty ipProperty, object oItem, float fDuration) +{ + if (nExpectedGeneration != nCurrentGeneration) + { + //Generation has changed, so don't apply the effect + return; + } + AddItemProperty(nDurationType, ipProperty, oItem, fDuration); +} + +//Including DelayAddItemProperty here to keep it with DelayApplyEffectToObject, though more properly it should probably be in inc_item_props.nss +void DelayAddItemProperty(float fDelay, object oGenerationHolder, string sGenerationName, int nDurationType, itemproperty ipProperty, object oItem, float fDuration = 0.0f) +{ + /* + There are a couple of problems that can arise in code that removes and reapplies item properties; + this function helps deal with those problems. One example of a typical place where these problems + frequently arise is in the class scripts called by the EvalPRCFeats function. + + The first problem is that when code removes and immediately reapplies a series of item properties, + some of those properties may not actually be reapplied. This is because the RemoveItemProperty() function + doesn't actually remove a property, it marks it to be removed later--when the currently running + script finishes. If any of the properties we reapply matches one of the properties marked to be + removed, that reapplied property will be removed when the currently running script finishes + and so will be unexpectedly missing. To illustrate: + 1) We start with properties A and B. + 2) The application function is called; it removes all properties and reapplies properties B and C. + 3) The actual removal happens when the script ends: property A and B are removed. + End result: we have only property C instead of the expected B and C. + The solution to this is to reapply the properties later using DelayCommand(). + + This introduces a new problem. If the function that removes and reapplies the properties is called + multiple times quickly, it can queue up a series of delayed applications. This causes two problems: + if the data on which the properties are calculated changes, the earlier delayed applications can + apply properties that should no longer be used, but they are anyway because the delayed code doesn't + know this. To illustrate: + 1) The application function is called; it removes all properties, schedules delayed application of property A. + 2) The application function is called again; it removes all properties, schedules delayed application of property B. + 3) Delayed application of property A occurs. + 4) Delayed application of property B occurs. + End result: we have both property A and B instead of the expected result, B alone. + Another problem is that we can end up with multiple copies of the same property. + If this happens enough, it can cause "Effect List overflow" errors. Also, if the property stacks + with itself, this gives a greater bonus or penalty than it should. To illustrate: + 1) The application function is called; it removes all properties, schedules delayed application of property C. + 2) The application function is called; it removes all properties, schedules delayed application of property C. + 3) Delayed application of property C occurs. + 4) Delayed application of property C occurs. + End result: we have property C twice instead of just once. + The solution is to both these problems is for the application function to increment an integer each time it + is called and to pass this to the delayed application function. The delayed application actually happens only + if the generation when it runs is the same as the generation when it was scheduled. To illustrate: + 1) We start with property A and B applied. + 2) The application function is called: it increments generation to 2, schedules delayed application of property B and C. + 3) The application function is called: it increments generation to 3, schedules delayed application of property C. + 4) The generation 2 delayed application function executes: it sees that the current generation is 3 and simply exits, doing nothing. + 5) The generation 3 delayed application function executes: it sees that the current generation is 3, so it applies property C. + End result: we have one copy of property C, which is what we wanted. + */ + + if (fDelay < 0.0f || GetStringLength(sGenerationName) == 0) + { + AddItemProperty(nDurationType, ipProperty, oItem, fDuration); + } + else + { + int nExpectedGeneration = GetLocalInt(oGenerationHolder, sGenerationName); //This gets the generation now + DelayCommand( + fDelay, + DelayedAddItemProperty( + nExpectedGeneration, + GetLocalInt(oGenerationHolder, sGenerationName), //This is delayed by the DelayCommand, so it gets the generation when DelayedAddItemProperty is actually executed + nDurationType, + ipProperty, + oItem, + fDuration + ) + ); + } +} + +/** + * @brief Sets the number of remaining uses per day for a feat based on an ability modifier. + * + * This function calculates the number of daily uses for a feat (`iFeat`) that a creature (`oPC`) + * possesses, based on their ability modifier (default: Charisma). It first removes any existing + * remaining uses and then reassigns the number based on the effective modifier. + * + * @param oPC The creature object for whom the feat usage is being set. + * @param iFeat The feat constant (e.g., FEAT_WHATEVER) to set uses for. + * @param iAbiMod (Optional) The ability score to base the uses on (e.g., ABILITY_CHARISMA). + * Use -1 to ignore ability modifiers. Defaults to ABILITY_CHARISMA. + * @param iMod (Optional) A flat modifier to add to the calculated ability modifier. Default is 0. + * @param iMin (Optional) The minimum number of uses to allow (even if the ability modifier is low). Default is 1. + * + * @note + * If the creature does not have the feat, no changes are made. + * If the ability modifier is negative or zero, it is treated as zero. + * If `iAbiMod` is -1, the ability modifier is ignored and `iMod` alone is used. + * The final number of uses will never be below `iMin`. + * + * @see GetAbilityModifier() + * @see GetHasFeat() + * @see DecrementRemainingFeatUses() + * @see IncrementRemainingFeatUses() + */ +void FeatUsePerDay(object oPC, int iFeat, int iAbiMod = ABILITY_CHARISMA, int iMod = 0, int iMin = 1) +{ + if(!GetHasFeat(iFeat,oPC)) + return; + + int iAbi = GetAbilityModifier(iAbiMod, oPC); + iAbi = (iAbi > 0) ? iAbi : 0; + + if (iAbiMod == -1) iAbi = 0; + iAbi += iMod; + + if(iAbi < iMin) + iAbi = iMin; + + while(GetHasFeat(iFeat, oPC)) + DecrementRemainingFeatUses(oPC, iFeat); + + while(iAbi) + { + IncrementRemainingFeatUses(oPC, iFeat); + iAbi--; + } +} + +void DomainUses(object oPC) +{ + int nUses; + if(!GetHasFeat(FEAT_BONUS_DOMAIN_STRENGTH, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_STRENGTH_DOMAIN_POWER)) + { + nUses = GetLevelByClass(CLASS_TYPE_MIGHTY_CONTENDER_KORD, oPC) ? GetAbilityModifier(ABILITY_STRENGTH, oPC) : 1; + FeatUsePerDay(oPC, FEAT_STRENGTH_DOMAIN_POWER, -1, nUses); + } + if(GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC) && GetLevelByClass(CLASS_TYPE_MYSTIC, oPC) && PRC_Funcs_GetFeatKnown(oPC, FEAT_SUN_DOMAIN_POWER)) + { + nUses = GetHasFeat(FEAT_EXTRA_TURNING, oPC) ? 7 : 3; + FeatUsePerDay(oPC, FEAT_SUN_DOMAIN_POWER, ABILITY_CHARISMA, nUses); + } + else if(!GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_SUN_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_SUN_DOMAIN_POWER, -1, 1); + + if(!GetHasFeat(FEAT_BONUS_DOMAIN_BLIGHTBRINGER, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_BLIGHTBRINGER)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_BLIGHTBRINGER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_AIR, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_AIR_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_AIR_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_EARTH, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_EARTH_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_EARTH_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_FIRE, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_FIRE_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_FIRE_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_WATER, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_WATER_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_WATER_DOMAIN_POWER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_SLIME, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_SLIME)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_SLIME, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_SPIDER, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_SPIDER)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_SPIDER, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_SCALEYKIND, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_DOMAIN_POWER_SCALEYKIND)) + FeatUsePerDay(oPC, FEAT_DOMAIN_POWER_SCALEYKIND, ABILITY_CHARISMA, 3); + if(!GetHasFeat(FEAT_BONUS_DOMAIN_PLANT, oPC) || PRC_Funcs_GetFeatKnown(oPC, FEAT_PLANT_DOMAIN_POWER)) + FeatUsePerDay(oPC, FEAT_PLANT_DOMAIN_POWER, ABILITY_CHARISMA, 3); +} + +void HathranFear(object oPC) +{ + if(!GetHasFeat(FEAT_HATH_FEAR1, oPC)) + return; + + int nLevel = GetLevelByClass(CLASS_TYPE_HATHRAN, oPC); + int nUses = 0; + + // Base uses based on progression + if (nLevel >= 8) + { + nUses = 3; + } + else if (nLevel >= 6) + { + nUses = 2; + } + else if (nLevel >= 3) + { + nUses = 1; + } + + // Add +1 per 3 levels gained above 9 + if (nLevel > 9) + { + int nBonusLevels = nLevel - 9; + nUses += nBonusLevels / 3; + } + + FeatUsePerDay(oPC, FEAT_HATH_FEAR1, -1, nUses); + +} + + +void MephlingBreath(object oPC) //:: Mephlings +{ + if(!GetHasFeat(FEAT_MEPHLING_BREATH, oPC)) + return; + + int nMephBreath = ((1 + GetHitDice(oPC)) / 4); + + FeatUsePerDay(oPC, FEAT_MEPHLING_BREATH, -1, nMephBreath); +} + +void FeatAlaghar(object oPC) +{ + int iAlagharLevel = GetLevelByClass(CLASS_TYPE_ALAGHAR, oPC); + + if (!iAlagharLevel) return; + + int iClangStrike = iAlagharLevel/3; + int iClangMight = (iAlagharLevel - 1)/3; + int iRockburst = (iAlagharLevel + 2)/4; + + FeatUsePerDay(oPC, FEAT_CLANGEDDINS_STRIKE, -1, iClangStrike); + FeatUsePerDay(oPC, FEAT_CLANGEDDINS_MIGHT, -1, iClangMight); + FeatUsePerDay(oPC, FEAT_ALAG_ROCKBURST, -1, iRockburst); +} + +void FeatDiabolist(object oPC) +{ + int Diabol = GetLevelByClass(CLASS_TYPE_DIABOLIST, oPC); + + if (!Diabol) return; + + int iUse = (Diabol + 3)/3; + + FeatUsePerDay(oPC,FEAT_DIABOL_DIABOLISM_1,-1,iUse); + FeatUsePerDay(oPC,FEAT_DIABOL_DIABOLISM_2,-1,iUse); + FeatUsePerDay(oPC,FEAT_DIABOL_DIABOLISM_3,-1,iUse); +} + +void FeatNinja (object oPC) +{ + int iNinjaLevel = GetLevelByClass(CLASS_TYPE_NINJA, oPC); + // Ascetic Stalker + if (GetHasFeat(FEAT_ASCETIC_STALKER, oPC)) + iNinjaLevel += GetLevelByClass(CLASS_TYPE_MONK, oPC); + + // Martial Stalker + if (GetHasFeat(FEAT_MARTIAL_STALKER, oPC)) + iNinjaLevel += GetLevelByClass(CLASS_TYPE_FIGHTER, oPC); + + if (!iNinjaLevel) return; + + int nUsesLeft = iNinjaLevel / 2; + if (nUsesLeft < 1) + nUsesLeft = 1; + + // Expanded Ki Pool + if (GetHasFeat(FEAT_EXPANDED_KI_POOL, oPC)) nUsesLeft += 3; + + FeatUsePerDay(oPC, FEAT_KI_POWER, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_GHOST_STEP, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_GHOST_STRIKE, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_GHOST_WALK, ABILITY_WISDOM, nUsesLeft); + FeatUsePerDay(oPC, FEAT_KI_DODGE, ABILITY_WISDOM, nUsesLeft); + + SetLocalInt(oPC, "prc_ninja_ki", nUsesLeft); +} + +void DrowJudicator(object oPC) +{ + int iJudicator = GetLevelByClass(CLASS_TYPE_JUDICATOR, oPC); + + if (iJudicator < 1) return; + + int nUses = 3; // base 3 uses at 10th level + + if (iJudicator > 10) + { + nUses += (iJudicator - 10) / 3; // +1 use per 3 levels above 10 + } + + FeatUsePerDay(oPC, FEAT_SELVETARMS_WRATH, -1, nUses); +} + +void Oozemaster(object oPC) +{ + if (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oPC) < 10) return; + + int iOozeLvl = GetLevelByClass(CLASS_TYPE_OOZEMASTER, oPC); + int nUses = 5; // base 5 uses at 10th level + + if (iOozeLvl > 10) + { + nUses += (iOozeLvl - 10) / 2; // +1 use per 2 levels above 10 + } + + FeatUsePerDay(oPC, FEAT_OOZY_GLOB5, -1, nUses); +} + +/* void Oozemaster(object oPC) +{ + if (GetLevelByClass(CLASS_TYPE_OOZEMASTER, oPC) < 4) return; + + int iOozeLvl = GetLevelByClass(CLASS_TYPE_OOZEMASTER, oPC); + int nUses = 2 + 2 * ((iOozeLvl - 4) / 3); + + FeatUsePerDay(oPC, FEAT_OOZY_GLOB5, -1, nUses); +} */ + +void EyeOfGruumsh(object oPC) +{ + if (GetLevelByClass(CLASS_TYPE_PRC_EYE_OF_GRUUMSH, oPC) < 4) return; + + int iEOGLevel = GetLevelByClass(CLASS_TYPE_PRC_EYE_OF_GRUUMSH, oPC); + int nUses = 2 + 2 * ((iEOGLevel - 4) / 3); + + FeatUsePerDay(oPC, FEAT_BLINDING_SPITTLE, -1, nUses); +} + +void BarbarianRage(object oPC) +{ + if(!GetHasFeat(FEAT_BARBARIAN_RAGE, oPC)) return; + + int nUses = (GetLevelByClass(CLASS_TYPE_BARBARIAN, oPC) + GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oPC) + GetLevelByClass(CLASS_TYPE_PRC_EYE_OF_GRUUMSH, oPC)) / 4 + 1; + nUses += (GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) + 2) / 5; + nUses += (GetLevelByClass(CLASS_TYPE_BATTLERAGER, oPC) + 1) / 2; + nUses += (GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oPC) + 2) / 4; + nUses += GetLevelByClass(CLASS_TYPE_RUNESCARRED, oPC) ? ((GetLevelByClass(CLASS_TYPE_RUNESCARRED, oPC) / 4) + 1) : 0; + nUses += (GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oPC) + 4) / 6; + + if(GetHasFeat(FEAT_EXTRA_RAGE, oPC)) nUses += 2; + + FeatUsePerDay(oPC, FEAT_BARBARIAN_RAGE, -1, nUses); + FeatUsePerDay(oPC, FEAT_GREATER_RAGE, -1, nUses); + + int nLevel = GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC); + if (nLevel > 0) + { + // Spell Rage: starts at 1st level, +1 use every 5 Rage Mage levels + int nUses = 1 + ((nLevel - 1) / 5); + + FeatUsePerDay(oPC, FEAT_SPELL_RAGE, -1, nUses); + } +/* if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) > 0) + { + if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) > 9) + nUses = 3; + else if(GetLevelByClass(CLASS_TYPE_RAGE_MAGE, oPC) > 4) + nUses = 2; + else + nUses = 1; + + FeatUsePerDay(oPC, FEAT_SPELL_RAGE, -1, nUses); + } */ +} + +void BardSong(object oPC) +{ + // This is used to set the number of bardic song uses per day, as bardic PrCs can increase it + // or other classes can grant it on their own + if(!GetHasFeat(FEAT_BARD_SONGS, oPC)) return; + + int nTotal = GetLevelByClass(CLASS_TYPE_BARD, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_DIRGESINGER, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_VIRTUOSO, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_FOCHLUCAN_LYRIST, oPC); + nTotal += GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oPC) / 2; + + if(GetHasFeat(FEAT_EXTRA_MUSIC, oPC)) nTotal += 4; + + FeatUsePerDay(oPC, FEAT_BARD_SONGS, -1, nTotal); +} + +void FeatVirtuoso(object oPC) +{ + int iVirtuosoLevel = GetLevelByClass(CLASS_TYPE_VIRTUOSO, oPC); + if (!iVirtuosoLevel) return; + + int nUses = GetLevelByClass(CLASS_TYPE_BARD, oPC) + iVirtuosoLevel; + if(GetHasFeat(FEAT_EXTRA_MUSIC, oPC)) nUses += 4; + SetPersistantLocalInt(oPC, "Virtuoso_Performance_Uses", nUses); + int nFeat; + for(nFeat = FEAT_VIRTUOSO_SUSTAINING_SONG; nFeat <= FEAT_VIRTUOSO_PERFORMANCE; nFeat++) + { + FeatUsePerDay(oPC, nFeat, -1, nUses); + } +} + +void HexCurse(object oPC) +{ + int iHexLevel = GetLevelByClass(CLASS_TYPE_HEXBLADE, oPC); + + if (!iHexLevel) return; + + //Hexblade's Curse + int nUses = (iHexLevel + 3) / 4; // every 4 levels get 1 more use + FeatUsePerDay(oPC, FEAT_HEXCURSE, ABILITY_CHARISMA, nUses); + + //Swift Cast + if(iHexLevel > 13) + nUses = (iHexLevel + 2) / 4; + else if(iHexLevel > 10) + nUses = 3; + else if(iHexLevel > 7) + nUses = 2; + else if(iHexLevel > 5) + nUses = 1; + else + nUses = 0; + FeatUsePerDay(oPC, FEAT_SWIFT_CAST, -1, nUses); +} + +void FeatShadowblade(object oPC) +{ + int iShadowLevel = GetLevelByClass(CLASS_TYPE_SHADOWBLADE, oPC); + if (!iShadowLevel) return; + + FeatUsePerDay(oPC, FEAT_UNERRING_STRIKE, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_UNEXPECTED_STRIKE, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_EPHEMERAL_WEAPON, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_SHADOWY_STRIKE, -1, iShadowLevel); + FeatUsePerDay(oPC, FEAT_FAR_SHADOW, -1, iShadowLevel); +} + +void FeatIronMind(object oPC) +{ + int iIronmindLvl = GetLevelByClass(CLASS_TYPE_IRONMIND, oPC); + if (iIronmindLvl <= 0) return; + + int nBonus; + + // Armoured Mind: starts at level 1 + if (iIronmindLvl >= 1) + { + nBonus = 1 + (iIronmindLvl - 1) / 3; + FeatUsePerDay(oPC, FEAT_ARMOURED_MIND, -1, nBonus); + } + + // Mind Over Body: starts at level 3 + if (iIronmindLvl >= 3) + { + nBonus = 1 + (iIronmindLvl - 3) / 3; + FeatUsePerDay(oPC, FEAT_MIND_OVER_BODY, -1, nBonus); + } +} + +void FeatNoble(object oPC) +{ + int iNobleLevel = GetLevelByClass(CLASS_TYPE_NOBLE, oPC); + if (!iNobleLevel) return; + + int nBonus = 0; + if (iNobleLevel >= 17) nBonus = 5; + else if (iNobleLevel >= 13) nBonus = 4; + else if (iNobleLevel >= 9) nBonus = 3; + else if (iNobleLevel >= 5) nBonus = 2; + else if (iNobleLevel >= 2) nBonus = 1; + + FeatUsePerDay(oPC, FEAT_NOBLE_CONFIDENCE, -1, nBonus); + + nBonus = (iNobleLevel - 11) / 3 + 1; + + FeatUsePerDay(oPC, FEAT_NOBLE_GREATNESS, -1, nBonus); +} + +void DarkKnowledge(object oPC) +{ + int iArchivistLevel = GetLevelByClass(CLASS_TYPE_ARCHIVIST, oPC); + if(!iArchivistLevel) return; + + int nUses = (iArchivistLevel / 3) + 3; + FeatUsePerDay(oPC, FEAT_DK_TACTICS, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_PUISSANCE, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_FOE, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_DREADSECRET, -1, nUses); + FeatUsePerDay(oPC, FEAT_DK_FOREKNOWLEDGE, -1, nUses); +} + +void FeatImbueArrow(object oPC) +{ + if(GetPRCSwitch(PRC_USE_NEW_IMBUE_ARROW)) + FeatUsePerDay(oPC, FEAT_PRESTIGE_IMBUE_ARROW, -1, 0, 0); +} + +void DragonDisciple(object oPC) +{ + if(!GetHasFeat(FEAT_DRAGON_DIS_BREATH, oPC)) + return; + + //Dragon Disciples that do not possess any breath weapon + if(GetHasFeat(FEAT_CHIANG_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_PAN_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_SHEN_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_TUN_MI_LUNG_DRAGON, oPC) + || GetHasFeat(FEAT_YU_LUNG_DRAGON, oPC)) + DecrementRemainingFeatUses(oPC, FEAT_DRAGON_DIS_BREATH); +} + +void Warlock(object oPC) +{ + if(GetHasFeat(FEAT_FIENDISH_RESILIENCE, oPC)) + { + //Add daily Uses of Fiendish Resilience for epic warlock + if(GetHasFeat(FEAT_EPIC_FIENDISH_RESILIENCE_I, oPC)) + { + int nFeatAmt = 0; + int bDone = FALSE; + while(!bDone) + { + if(nFeatAmt >= 9) + bDone = TRUE; + else if(GetHasFeat(FEAT_EPIC_FIENDISH_RESILIENCE_II + nFeatAmt, oPC)) + nFeatAmt++; + else + bDone = TRUE; + } + nFeatAmt++; + FeatUsePerDay(oPC, FEAT_FIENDISH_RESILIENCE, -1, nFeatAmt); + } + else + FeatUsePerDay(oPC, FEAT_FIENDISH_RESILIENCE, -1, 1); + } + + //Hellfire infusion + int nCha = GetAbilityModifier(ABILITY_CHARISMA, oPC); + FeatUsePerDay(oPC, FEAT_HELLFIRE_INFUSION, -1, nCha); + + //Eldritch Spellweave + nCha += 3; + FeatUsePerDay(oPC, FEAT_ELDRITCH_SPELLWEAVE, -1, nCha); +} + +void KotMC(object oPC) +{ + int iKotMCLevel = GetLevelByClass(CLASS_TYPE_KNIGHT_MIDDLECIRCLE, oPC); + if(!iKotMCLevel) return; + + int nUses = iKotMCLevel / 3; + FeatUsePerDay(oPC, FEAT_KOTMC_TRUE_STRIKE, -1, nUses); +} + +void Ravager(object oPC) +{ + int nRavager = GetLevelByClass(CLASS_TYPE_RAVAGER, oPC); + int nUses; + + if (nRavager < 1) return; + + if (nRavager < 4) + nUses = 1; + else if (nRavager < 7) + nUses = 2; + else if (nRavager < 11) + nUses = 3; + else + nUses = 4 + ((nRavager - 11) / 5); // +1 every 5 levels after 11 + + FeatUsePerDay(oPC, FEAT_PAIN_TOUCH, -1, nUses); + + if(!GetHasFeat(FEAT_AURA_OF_FEAR, oPC)) return; + + nUses = 0; + + if (nRavager < 5) + nUses = 1; + else if (nRavager < 8) + nUses = 2; + else if (nRavager < 12) + nUses = 3; + else if (nRavager < 17) + nUses = 4; + else + nUses = 5 + ((nRavager - 17) / 5); // +1 every 5 levels after 17 + + FeatUsePerDay(oPC, FEAT_AURA_OF_FEAR, -1, nUses); + + if(!GetHasFeat(FEAT_CRUELEST_CUT, oPC)) return; + + nUses = 0; + + if (nRavager < 6) + nUses = 1; + else if (nRavager < 9) + nUses = 2; + else if (nRavager < 13) + nUses = 3; + else if (nRavager < 18) + nUses = 4; + else + nUses = 5 + ((nRavager - 18) / 5); // +1 every 5 levels after 18 + + FeatUsePerDay(oPC, FEAT_CRUELEST_CUT, -1, nUses); + + if(!GetHasFeat(FEAT_VISAGE_OF_TERROR, oPC)) return; + + nUses = 0; + + nUses = 1 + ((nRavager - 10) / 5); // +1 every 5 levels after 10 + + FeatUsePerDay(oPC, FEAT_VISAGE_OF_TERROR, -1, nUses); + +} + + +void Templar(object oPC) +{ + if(!GetHasFeat(FEAT_SECULAR_AUTHORITY, oPC)) return; + + int nTemplar = GetLevelByClass(CLASS_TYPE_TEMPLAR, oPC); + int nUses = nTemplar + ((GetHitDice(oPC) - nTemplar) / 4); + FeatUsePerDay(oPC, FEAT_SECULAR_AUTHORITY, -1, nUses); +} + +void FeatRacial(object oPC) +{ + //Shifter bonus shifting uses + int nRace = GetRacialType(oPC); + if(nRace == RACIAL_TYPE_SHIFTER) + { + int nShiftFeats = GetShiftingFeats(oPC); + int nBonusShiftUses = (nShiftFeats / 2) + 1; + FeatUsePerDay(oPC, FEAT_SHIFTER_SHIFTING, -1, nBonusShiftUses); + } + else if(nRace == RACIAL_TYPE_FORESTLORD_ELF) + { + int nUses = GetHitDice(oPC) / 5 + 1; + FeatUsePerDay(oPC, FEAT_FORESTLORD_TREEWALK, -1, nUses); + } +} + +void CombatMedic(object oPC) +{ + int iCombMed = GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oPC); + if(!iCombMed) return; + + FeatUsePerDay(oPC, FEAT_HEALING_KICKER_1, ABILITY_WISDOM, iCombMed); + FeatUsePerDay(oPC, FEAT_HEALING_KICKER_2, ABILITY_WISDOM, iCombMed); + FeatUsePerDay(oPC, FEAT_HEALING_KICKER_3, ABILITY_WISDOM, iCombMed); +} + +void MasterOfShrouds(object oPC) +{ + if(!GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oPC)) return; + + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_1, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_2, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_3, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_MOS_UNDEAD_4, ABILITY_CHARISMA, 3); +} + +void SLAUses(object oPC) +{ + if(!GetHasFeat(FEAT_SPELL_LIKE_ABILITY_1, oPC)) return; + + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_1, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_1")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_2, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_2")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_3, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_3")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_4, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_4")); + FeatUsePerDay(oPC, FEAT_SPELL_LIKE_ABILITY_5, -1, GetPersistantLocalInt(oPC, "PRC_SLA_Uses_5")); +} + +void ShadowShieldUses(object oPC) +{ + if(!GetHasFeat(FEAT_SA_SHIELDSHADOW, oPC)) return; + FeatUsePerDay(oPC, FEAT_SA_SHIELDSHADOW, -1, GetPrCAdjustedCasterLevelByType(TYPE_ARCANE, oPC)); +} + +void BlighterFeats(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_BLIGHTER, oPC); + if (nClass <= 0) return; + + // Phase 1: Levels 3–10 + int nWildShapeUses = 0; + if (nClass >= 3) + { + int nPhase1 = (nClass <= 10) ? nClass : 10; + nWildShapeUses = 1 + ((nPhase1 - 3) / 2); + } + + // Phase 2: Additional uses every 4 levels after 10 + if (nClass > 10) + { + nWildShapeUses += ((nClass - 11) / 4) + 1; + } + + // Contagious Touch: starts at level 5, +1 use every 2 levels + int nTouchUses = 0; + if (nClass >= 5) + { + nTouchUses = 1 + ((nClass - 5) / 2); + } + + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, nWildShapeUses); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, nTouchUses); +} + + + +/* void BlighterFeats(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_BLIGHTER, oPC); + if(0 >= nClass) return; + + if (nClass == 3) + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 1); + else if (nClass == 4) + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 2); + else if (nClass == 5) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 2); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 1); + } + else if (nClass == 6) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 3); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 1); + } + else if (nClass == 7) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 3); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 2); + } + else if (nClass == 8) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 4); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 2); + } + else if (nClass == 9) + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 4); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 3); + } + else + { + FeatUsePerDay(oPC, FEAT_UNDEAD_WILD_SHAPE, -1, 5); + FeatUsePerDay(oPC, FEAT_CONTAGIOUS_TOUCH, -1, 3); + } +} */ + +void MysteryFeats(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oPC); + if(nClass > 0) + { + if (nClass >= 10) + FeatUsePerDay(oPC, FEAT_CLOAK_SHADOWS, -1, 2); + else if (nClass >= 6) + FeatUsePerDay(oPC, FEAT_CLOAK_SHADOWS, -1, 3); + else + FeatUsePerDay(oPC, FEAT_CLOAK_SHADOWS, -1, 1); + + if (nClass >= 7) + FeatUsePerDay(oPC, FEAT_DANCING_SHADOWS, -1, 2); + else + FeatUsePerDay(oPC, FEAT_DANCING_SHADOWS, -1, 1); + } + nClass = GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oPC); + if (nClass >= 2) + { + int nUses; + if (nClass < 5) + nUses = 1; + else if (nClass < 8) + nUses = 2; + else + nUses = 3 + (nClass - 8) / 3; + + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, nUses); + } +/* { + if (nClass >= 8) + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, 3); + else if (nClass >= 5) + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, 2); + else + FeatUsePerDay(oPC, FEAT_INNATE_COUNTERSPELL, -1, 1); + } */ + nClass = GetLevelByClass(CLASS_TYPE_SHADOWSMITH, oPC); + if(nClass > 0) + { + FeatUsePerDay(oPC, FEAT_TOUCH_SHADOW , -1, nClass); + FeatUsePerDay(oPC, FEAT_SHROUD_SHADOW , -1, nClass); + FeatUsePerDay(oPC, FEAT_SHADOW_CRAFT , -1, nClass/2); + FeatUsePerDay(oPC, FEAT_ARMOR_SHADOW , -1, nClass/2); + FeatUsePerDay(oPC, FEAT_ARMOR_SHADOW_Q, -1, nClass/2); + } +} + +void WildMage(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oPC); + + if (nClass >= 2) + { + int nUses = 1 + ((nClass - 2) / 3); + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, nUses); + } +} +/* void WildMage(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_WILD_MAGE, oPC); + if(nClass > 0) + { + if (nClass >= 8) + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, 2); + else if (nClass >= 5) + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, 3); + else + FeatUsePerDay(oPC, FEAT_WILD_MAGE_RANDOM_DEFLECTOR, -1, 1); + } +} */ + +void Factotum(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + if(nClass > 0) + { + if (nClass >= 20) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 6); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 6); + } + else if (nClass >= 15) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 5); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 5); + } + else if (nClass >= 10) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 4); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 4); + } + else + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 3); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 3); + } + } +} + +/* void Factotum(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC); + if(nClass > 0) + { + if (nClass >= 20) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 6); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 6); + } + else if (nClass >= 15) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 5); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 5); + } + else if (nClass >= 10) + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 4); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 4); + } + else + { + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_TURN, ABILITY_WISDOM, 0, 3); + FeatUsePerDay(oPC, FEAT_OPPORTUNISTIC_PIETY_HEAL, ABILITY_WISDOM, 0, 3); + } + } +} */ + +void Sharess(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CELEBRANT_SHARESS, oPC); + if(nClass > 0) + { + FeatUsePerDay(oPC, FEAT_CELEBRANT_SHARESS_FASCINATE , -1, 0, nClass); + FeatUsePerDay(oPC, FEAT_CELEBRANT_SHARESS_CONFUSE , -1, 0, nClass); + FeatUsePerDay(oPC, FEAT_CELEBRANT_SHARESS_DOMINATE , -1, 0, nClass); + } +} + +void SoulbornDefense(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_SOULBORN, oPC); + if(nClass > 0) + { + if (nClass >= 37) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 8); + else if (nClass >= 33) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 7); + else if (nClass >= 29) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 6); + else if (nClass >= 25) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 5); + else if (nClass >= 21) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 4); + else if (nClass >= 17) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 3); + else if (nClass >= 13) + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_SHARE_INCARNUM_DEFENSE, -1, 0, 1); + } +} + +void TotemistReshape(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_TOTEMIST, oPC); + if(nClass > 0) + { + if (nClass >= 40) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 9); + else if (nClass >= 36) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 8); + else if (nClass >= 32) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 7); + else if (nClass >= 28) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 6); + else if (nClass >= 24) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 5); + else if (nClass >= 20) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 4); + else if (nClass >= 16) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 3); + else if (nClass >= 12) + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_REBIND_TOTEM_SOULMELD, -1, 0, 1); + } +} + +void CWSamurai(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CW_SAMURAI, oPC); + if(nClass > 0) + { + if (nClass >= 17) + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 4); + else if (nClass >= 12) + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 3); + else if (nClass >= 7) + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_KIAI_SMITE, -1, 0, 1); + } +} + +void CrusaderSmite(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC); + if(nClass > 0) + { + if (nClass >= 18) + FeatUsePerDay(oPC, FEAT_CRUSADER_SMITE, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_CRUSADER_SMITE, -1, 0, 1); + } +} + +void AnimaMage(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oPC); + if(nClass > 0) + { + int nUses; + // Levels 1-6: 1 use + if(nClass < 7) + { + nUses = 1; + } + // Levels 7-8: 2 uses + else if(nClass < 9) + { + nUses = 2; + } + // Levels 9-10: 3 uses + else if(nClass <= 10) + { + nUses = 3; + } + // Levels above 10: 1 additional use per 3 levels + else + { + nUses = 3 + ((nClass - 10) / 3); + } + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, nUses); + } +} + +/* void AnimaMage(object oPC) +{ + int nClass = GetLevelByClass(CLASS_TYPE_ANIMA_MAGE, oPC); + if(nClass > 0) + { + if (nClass >= 9) + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, 3); + else if (nClass >= 7) + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, 2); + else + FeatUsePerDay(oPC, FEAT_ANIMA_VESTIGE_METAMAGIC, -1, 0, 1); + } +} */ + +void FeatSpecialUsePerDay(object oPC) +{ + FeatUsePerDay(oPC, FEAT_WWOC_WIDEN_SPELL, ABILITY_CHARISMA, GetLevelByClass(CLASS_TYPE_WAR_WIZARD_OF_CORMYR, oPC)); + FeatUsePerDay(oPC, FEAT_FIST_DAL_QUOR_STUNNING_STRIKE, -1, GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oPC)); + FeatUsePerDay(oPC, FEAT_AD_FALSE_KEENNESS, -1, GetLevelByClass(CLASS_TYPE_ARCANE_DUELIST, oPC)); + FeatUsePerDay(oPC, FEAT_LASHER_STUNNING_SNAP, -1, GetLevelByClass(CLASS_TYPE_LASHER, oPC)); + FeatUsePerDay(oPC, FEAT_AD_BLUR, -1, GetLevelByClass(CLASS_TYPE_ARCANE_DUELIST, oPC)); + FeatUsePerDay(oPC, FEAT_SHADOW_RIDE, -1, GetLevelByClass(CLASS_TYPE_CRINTI_SHADOW_MARAUDER, oPC)); + FeatUsePerDay(oPC, FEAT_SHADOWJUMP, -1, GetLevelByClass(CLASS_TYPE_SHADOWLORD, oPC)); + FeatUsePerDay(oPC, FEAT_SHADOWBANE_SMITE, -1, (GetLevelByClass(CLASS_TYPE_SHADOWBANE_INQUISITOR, oPC)+2)/4); + FeatUsePerDay(oPC, FEAT_INCARNUM_RADIANCE, -1, (GetLevelByClass(CLASS_TYPE_INCARNATE, oPC)+2)/5); + FeatUsePerDay(oPC, FEAT_RAPID_MELDSHAPING, -1, (GetLevelByClass(CLASS_TYPE_INCARNATE, oPC)+1)/6); + FeatUsePerDay(oPC, FEAT_SMITE_OPPOSITION, -1, (GetLevelByClass(CLASS_TYPE_SOULBORN, oPC)+5)/5); + FeatUsePerDay(oPC, FEAT_SMITE_CHAOS, -1, (GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oPC)+2)/3); + FeatUsePerDay(oPC, FEAT_INCANDESCENT_OVERLOAD, -1, (GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oPC)-1)/3); + FeatUsePerDay(oPC, FEAT_NECROCARNATE_SOULSHIELD, -1, GetLevelByClass(CLASS_TYPE_NECROCARNATE, oPC)/2); + FeatUsePerDay(oPC, FEAT_SCION_DANTALION_SCHOLARSHIP, -1, GetLevelByClass(CLASS_TYPE_SCION_DANTALION, oPC)); + FeatUsePerDay(oPC, FEAT_SMITE_GOOD_ALIGN, -1, (GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oPC)+1)/2); + FeatUsePerDay(oPC, FEAT_DOA_LEARN_SECRETS, -1, (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oPC)+1)/2); + FeatUsePerDay(oPC, FEAT_BLIGHTTOUCH, -1, (GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oPC) - 1) / 2); + FeatUsePerDay(oPC, FEAT_FIST_OF_IRON, ABILITY_WISDOM, 3); + FeatUsePerDay(oPC, FEAT_SMITE_UNDEAD, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_COC_WRATH, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_KILLOREN_ASPECT_D, ABILITY_CHARISMA); + FeatUsePerDay(oPC, FEAT_AVENGING_STRIKE, ABILITY_CHARISMA, 0, 1); + FeatUsePerDay(oPC, FEAT_INCARNUM_BLADE_REBIND, ABILITY_CONSTITUTION, 1); + FeatUsePerDay(oPC, FEAT_WITCHBORN_INTEGUMENT, ABILITY_CONSTITUTION, 1); + FeatUsePerDay(oPC, FEAT_LIPS_RAPTUR); + FeatUsePerDay(oPC, FEAT_COMMAND_SPIDERS, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_FM_FOREST_DOMINION, ABILITY_CHARISMA, 3); + FeatUsePerDay(oPC, FEAT_SOD_DEATH_TOUCH, -1, (GetLevelByClass(CLASS_TYPE_SLAYER_OF_DOMIEL, oPC)+4)/4); + FeatUsePerDay(oPC, FEAT_SUEL_DISPELLING_STRIKE, -1, (GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oPC) + 2) / 4); + FeatDiabolist(oPC); + FeatAlaghar(oPC); + ShadowShieldUses(oPC); + CombatMedic(oPC); + FeatNinja(oPC); + FeatNoble(oPC); + MasterOfShrouds(oPC); + HexCurse(oPC); + FeatRacial(oPC); + FeatShadowblade(oPC); + SoulbornDefense(oPC); + FeatIronMind(oPC); + + int nDread = GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC); + if (nDread >= 1) + {//:: Enervating Touch + if (nDread >= 17) + { + FeatUsePerDay(oPC, FEAT_DN_ENERVATING_TOUCH, -1, nDread); + } + else + { + FeatUsePerDay(oPC, FEAT_DN_ENERVATING_TOUCH, -1, nDread/2); + } + } + + if (nDread >= 3) + {//:: Negative Energy Burst + int NegBurstUses = 0; + + if (nDread >= 8) + { + // Gains 1 more daily use of Negative Energy Burst every 5 levels after 3rd + int additionalUses = (nDread - 3) / 5; + NegBurstUses += 1 + additionalUses; + FeatUsePerDay(oPC, FEAT_DN_NEG_NRG_BURST, -1, NegBurstUses); + } + else if (nDread >= 3) + { + // Dread Necromancer gains 1 use at level 3 + NegBurstUses += 1; + FeatUsePerDay(oPC, FEAT_DN_NEG_NRG_BURST, -1, NegBurstUses); + } + } + if (nDread >= 7) + {//:: Scabrous Touch + int ScabUses = 0; + + // Gains 1 more daily use every 5 levels after 21 + if (nDread >= 26) + { + int additionalUses = (nDread - 21) / 5; + ScabUses += 4 + additionalUses; + FeatUsePerDay(oPC, FEAT_DN_SCABROUS_TOUCH, -1, ScabUses); + } + // Epic Dread Necromancer gains 1 more daily use at level 21 + else if (nDread >= 21) + { + ScabUses += 4; + FeatUsePerDay(oPC, FEAT_DN_SCABROUS_TOUCH, -1, ScabUses); + } + else if (nDread >= 16) + { + ScabUses += 3; + FeatUsePerDay(oPC, FEAT_DN_SCABROUS_TOUCH, -1, ScabUses); + } + else if (nDread >= 11) + { + ScabUses += 2; + FeatUsePerDay(oPC, FEAT_DN_SCABROUS_TOUCH, -1, ScabUses); + } + + else + { + ScabUses += 1; + FeatUsePerDay(oPC, FEAT_DN_SCABROUS_TOUCH, -1, ScabUses); + } + } + + SLAUses(oPC); + DomainUses(oPC); + BardSong(oPC); + EyeOfGruumsh(oPC); + BarbarianRage(oPC); + FeatVirtuoso(oPC); + ResetExtraStunfistUses(oPC); + DarkKnowledge(oPC); + FeatImbueArrow(oPC); + DragonDisciple(oPC); + Warlock(oPC); + KotMC(oPC); + Templar(oPC); + BlighterFeats(oPC); + MysteryFeats(oPC); + WildMage(oPC); + Factotum(oPC); + Sharess(oPC); + TotemistReshape(oPC); + CWSamurai(oPC); + CrusaderSmite(oPC); + AnimaMage(oPC); + MephlingBreath(oPC); + HathranFear(oPC); + Oozemaster(oPC); + DrowJudicator(oPC); + Ravager(oPC); +} + diff --git a/src/include/prc_inc_hextor.nss b/src/include/prc_inc_hextor.nss new file mode 100644 index 0000000..affe360 --- /dev/null +++ b/src/include/prc_inc_hextor.nss @@ -0,0 +1,61 @@ +#include "prc_feat_const" + +const string BRUTAL_STRIKE_MODE_VAR = "PRC_BRUTAL_STRIKE_MODE"; + +int _prc_inc_hextor_BrutalStrikeFeatCount(object oPC) +{ + if(GetHasFeat(FEAT_BSTRIKE_12, oPC)) + return 12; + else if (GetHasFeat(FEAT_BSTRIKE_11, oPC)) + return 11; + else if (GetHasFeat(FEAT_BSTRIKE_10, oPC)) + return 10; + else if (GetHasFeat(FEAT_BSTRIKE_9, oPC)) + return 9; + else if (GetHasFeat(FEAT_BSTRIKE_8, oPC)) + return 8; + else if (GetHasFeat(FEAT_BSTRIKE_7, oPC)) + return 7; + else if (GetHasFeat(FEAT_BSTRIKE_6, oPC)) + return 6; + else if (GetHasFeat(FEAT_BSTRIKE_5, oPC)) + return 5; + else if (GetHasFeat(FEAT_BSTRIKE_4, oPC)) + return 4; + else if (GetHasFeat(FEAT_BSTRIKE_3, oPC)) + return 3; + else if (GetHasFeat(FEAT_BSTRIKE_2, oPC)) + return 2; + else if (GetHasFeat(FEAT_BSTRIKE_1, oPC)) + return 1; + + return 0; +} + +void _prc_inc_hextor_ApplyBrutalStrike(object oPC, int nBonus) +{ + object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (!GetIsObjectValid(oWeap)) + { + oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + if (!GetIsObjectValid(oWeap)) + { + oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC); + if (!GetIsObjectValid(oWeap)) + oWeap = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oPC); + } + } + int nDamageType = (!GetIsObjectValid(oWeap)) ? DAMAGE_TYPE_BLUDGEONING : GetItemDamageType(oWeap); + + effect eBrutalStrike; + if (GetLocalInt(oPC, BRUTAL_STRIKE_MODE_VAR)) + eBrutalStrike = EffectAttackIncrease(nBonus); + else + eBrutalStrike = EffectDamageIncrease(nBonus, nDamageType); + eBrutalStrike = ExtraordinaryEffect(eBrutalStrike); + + PRCRemoveEffectsFromSpell(oPC, SPELL_HEXTOR_DAMAGE); + PRCRemoveEffectsFromSpell(oPC, SPELL_HEXTOR_MODE); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBrutalStrike, oPC); +} diff --git a/src/include/prc_inc_itmrstr.nss b/src/include/prc_inc_itmrstr.nss new file mode 100644 index 0000000..156f530 --- /dev/null +++ b/src/include/prc_inc_itmrstr.nss @@ -0,0 +1,558 @@ +/* + + This include governs all the new itemproperties + Both restrictions and features + +*/ +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string PLAYER_SPEED_INCREASE = "player_speed_increase"; +const string PLAYER_SPEED_DECREASE = "player_speed_decrease"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int DoUMDCheck(object oItem, object oPC, int nDCMod); + +int CheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID); + +/** + * Non-returning wrapper for CheckPRCLimitations. + */ +void VoidCheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID); + +void CheckForPnPHolyAvenger(object oItem); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_utility" +#include "prc_inc_newip" + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/*void _prc_inc_itmrstr_ApplySpeedModification(object oPC, int nEffectType, int nSpeedMod) +{ + if(DEBUG) DoDebug("_prc_inc_itmrstr_ApplySpeedModification(" + DebugObject2Str(oPC) + ", " + IntToString(nEffectType) + ", " + IntToString(nSpeedMod) + ")"); + // The skin object should be OBJECT_SELF here + // Clean up existing speed modification + effect eTest = GetFirstEffect(oPC); + while(GetIsEffectValid(eTest)) + { + if(GetEffectCreator(eTest) == OBJECT_SELF && + GetEffectType(eTest) == nEffectType && + GetEffectSubType(eTest) == SUBTYPE_SUPERNATURAL + ) + RemoveEffect(oPC, eTest); + eTest = GetNextEffect(oPC); + } + + // Apply speed mod if there is any + if(nSpeedMod > 0) + { + effect eSpeedMod = SupernaturalEffect(nEffectType == EFFECT_TYPE_MOVEMENT_SPEED_INCREASE ? + EffectMovementSpeedIncrease(nSpeedMod) : + EffectMovementSpeedDecrease(nSpeedMod) + ); + /// @todo Determine if the delay is actually needed here + DelayCommand(0.5, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSpeedMod, oPC)); + } +} + +void _prc_inc_itmrstr_ApplySpeedIncrease(object oPC) +{ + // Get target speed modification value. Limit to 99, since that's the effect constructor maximum value + int nSpeedMod = PRCMin(99, GetLocalInt(oPC, PLAYER_SPEED_INCREASE)); + object oSkin = GetPCSkin(oPC); + + AssignCommand(oSkin, _prc_inc_itmrstr_ApplySpeedModification(oPC, EFFECT_TYPE_MOVEMENT_SPEED_INCREASE, nSpeedMod)); +} + + +void _prc_inc_itmrstr_ApplySpeedDecrease(object oPC) +{ + // Get target speed modification value. Limit to 99, since that's the effect constructor maximum value + int nSpeedMod = GetLocalInt(oPC, PLAYER_SPEED_DECREASE); + object oSkin = GetPCSkin(oPC); + + AssignCommand(oSkin, _prc_inc_itmrstr_ApplySpeedModification(oPC, EFFECT_TYPE_MOVEMENT_SPEED_DECREASE, nSpeedMod)); +}*/ + +void _prc_inc_itmrstr_ApplyAoE(object oPC, object oItem, int nSubType, int nCost) +{ + int nAoEID = StringToInt(Get2DACache("iprp_aoe", "AoEID", nSubType)); + string sTag = Get2DACache("vfx_persistent", "LABEL", nAoEID); + effect eAoE = EffectAreaOfEffect(nAoEID, + Get2DACache("iprp_aoe", "EnterScript", nSubType), + Get2DACache("iprp_aoe", "HBScript", nSubType), + Get2DACache("iprp_aoe", "ExitScript", nSubType)); + + // The item applies the AoE effect + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAoE, oPC); + + // Get an object reference to the newly created AoE + location lLoc = GetLocation(oPC); + object oAoE = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lLoc, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + while(GetIsObjectValid(oAoE)) + { + // Test if we found the correct AoE + if(GetTag(oAoE) == sTag && + !GetLocalInt(oAoE, "PRC_AoE_IPRP_Init") + ) + { + SetLocalInt(oAoE, "PRC_AoE_IPRP_Init", TRUE); + break; + } + // Didn't find, get next + oAoE = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lLoc, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + } + if(!GetIsObjectValid(oAoE)) DoDebug("ERROR: _prc_inc_itmrstr_ApplyAoE: Can't find AoE created by " + DebugObject2Str(oItem)); + + // Set caster level override on the AoE + SetLocalInt(oAoE, PRC_CASTERLEVEL_OVERRIDE, nCost); + //if(DEBUG) DoDebug("_prc_inc_itmrstr_ApplyAoE: AoE level: " + IntToString(nCost)); +} + +void _prc_inc_itmrstr_ApplyWizardry(object oPC, object oItem, int nSpellLevel, string sType) +{ + //properties were already applied - happens when loading a saved game + if(GetLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel))) + return; + + SetLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel), TRUE); + int nClass, nSlots, i; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oPC); + if((sType == "A" && GetIsArcaneClass(nClass)) || (sType == "D" && GetIsDivineClass(nClass))) + { + if(GetAbilityScoreForClass(nClass, oPC) < nSpellLevel + 10) + continue; + + int nSpellSlotLevel = GetSpellslotLevel(nClass, oPC) - 1; + string sFile = Get2DACache("classes", "SpellGainTable", nClass); + nSlots = StringToInt(Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nSpellSlotLevel)); + //if(DEBUG) DoDebug("Adding "+IntToString(nSlots)" bonus slots for "+IntToString(nClass)" class."); + + if(nSlots) + { + string sVar = "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); + int j = 0; + while(j < nSlots) + { + //DoDebug(IntToString(j)); + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusLevelSpell(nClass, nSpellLevel), oItem); + //nsb compatibility + SetLocalInt(oPC, sVar, (GetLocalInt(oPC, sVar) + 1)); + j++; + } + } + } + } + SetPlotFlag(oItem, TRUE); +} + +void _prc_inc_itmrstr_RemoveWizardry(object oPC, object oItem, int nSpellLevel, string sType) +{ + DeleteLocalInt(oItem, "PRC_Wizardry"+IntToString(nSpellLevel)); + SetPlotFlag(oItem, FALSE); + itemproperty ipTest = GetFirstItemProperty(oItem); + string sVar; + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + if(GetItemPropertyCostTableValue(ipTest) == nSpellLevel) + { + int nClass = GetItemPropertySubType(ipTest); + if((sType == "A" && GetIsArcaneClass(nClass)) || (sType == "D" && GetIsDivineClass(nClass))) + { + RemoveItemProperty(oItem, ipTest); + //remove bonus slots from nsb classes + sVar = "PRC_IPRPBonSpellSlots_" + IntToString(nClass) + "_" + IntToString(nSpellLevel); + SetLocalInt(oPC, sVar, (GetLocalInt(oPC, sVar) - 1)); + int nCount, nSpellbook = GetSpellbookTypeForClass(nClass); + string sArray = "NewSpellbookMem_"+IntToString(nClass); + if(nSpellbook == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oPC, sArray, nSpellLevel); + if(nCount) + { + nCount--; + persistant_array_set_int(oPC, sArray, nSpellLevel, nCount); + } + } + else if(nSpellbook == SPELLBOOK_TYPE_PREPARED) + { + string sIDX = "SpellbookIDX" + IntToString(nSpellLevel) + "_" + IntToString(nClass); + int i, nSpellbookID, nMax = persistant_array_get_size(oPC, sIDX) - 1; + for(i = nMax; i >= 0; i--) + { + nSpellbookID = persistant_array_get_int(oPC, sIDX, i); + nCount = persistant_array_get_int(oPC, sArray, nSpellbookID); + if(nCount) + { + nCount--; + persistant_array_set_int(oPC, sArray, nSpellbookID, nCount); + break; + } + } + } + } + } + } + ipTest = GetNextItemProperty(oItem); + } +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetUMDForItemCost(object oItem) +{ + string s2DAEntry; + int nValue = GetGoldPieceValue(oItem); + int n2DAValue = StringToInt(s2DAEntry); + int i; + while(n2DAValue < nValue) + { + s2DAEntry = Get2DACache("skillvsitemcost", "DeviceCostMax", i); + n2DAValue = StringToInt(s2DAEntry); + i++; + } + i--; + string s2DAReqSkill = Get2DACache("skillvsitemcost", "SkillReq_Class", i); + if(s2DAReqSkill == "") + return -1; + return StringToInt(s2DAReqSkill); +} + +//this is a scripted version of the bioware UMD check for using restricted items +//this also applies effects relating to new itemproperties +int DoUMDCheck(object oItem, object oPC, int nDCMod) +{ + + //doesnt have UMD + if(!GetHasSkill(SKILL_USE_MAGIC_DEVICE, oPC)) + return FALSE; + + int nSkill = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oPC); + int nReqSkill = GetUMDForItemCost(oItem); + //class is a dc20 test + nReqSkill = nReqSkill - 20 + nDCMod; + if(nReqSkill > nSkill) + return FALSE; + else + return TRUE; +} + +void VoidCheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID) +{ + CheckPRCLimitations(oItem, oPC); +} + +//tests for use restrictions +//also appies effects for those IPs tat need them +/// @todo Rename. It's not just limitations anymore +int CheckPRCLimitations(object oItem, object oPC = OBJECT_INVALID) +{ + // Sanity check - the item needs to be valid + if(!GetIsObjectValid(oItem)) + return FALSE; /// @todo Might be better to auto-pass the limitation aspect in case of invalid item + + // In case no item owner was given, find it out + if(!GetIsObjectValid(oPC)) + oPC = GetItemPossessor(oItem); + + // Sanity check - the item needs to be in some creature's possession for this function to make sense + if(!GetIsObjectValid(oPC)) + return FALSE; + + // Equip and Unequip events need some special handling + int bUnequip = GetItemLastUnequipped() == oItem && GetLocalInt(oPC, "ONEQUIP") == 1; + int bEquip = GetItemLastEquipped() == oItem && GetLocalInt(oPC, "ONEQUIP") == 2; + + // Use restriction and UMD use + int bPass = TRUE; + int nUMDDC = 0; + + // Speed modification. Used to determine if effects need to be applied + int nSpeedIncrease = GetLocalInt(oPC, PLAYER_SPEED_INCREASE); + int nSpeedDecrease = GetLocalInt(oPC, PLAYER_SPEED_DECREASE); + + // Loop over all itemproperties on the item + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + int ipType = GetItemPropertyType(ipTest); + /* Use restrictions. All of these can be skipped when unequipping */ + if(!bUnequip) + { + if (ipType == ITEM_PROPERTY_USE_LIMITATION_ABILITY_SCORE) + { + int nValue = GetItemPropertyCostTableValue(ipTest); + if(GetAbilityScore(oPC, GetItemPropertySubType(ipTest), TRUE) < nValue) + bPass = FALSE; + nUMDDC += nValue - 15; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SKILL_RANKS) + { + int nValue = GetItemPropertyCostTableValue(ipTest); + if(GetSkillRank(GetItemPropertySubType(ipTest), oPC) < nValue) + bPass = FALSE; + nUMDDC += nValue - 10; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SPELL_LEVEL) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_AllSpell" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_ARCANE_SPELL_LEVEL) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_ArcSpell" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_DIVINE_SPELL_LEVEL) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_DivSpell" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_SNEAK_ATTACK) + { + int nLevel = GetItemPropertyCostTableValue(ipTest); + if(GetLocalInt(oPC, "PRC_SneakLevel" + IntToString(nLevel))) + bPass = FALSE; + nUMDDC += (nLevel * 2) - 20; + } + else if(ipType == ITEM_PROPERTY_USE_LIMITATION_GENDER) + { + if(GetGender(oPC) != GetItemPropertySubType(ipTest)) + bPass = FALSE; + nUMDDC += 5; + } + } + + /* Properties that apply effects. Unequip should cause cleanup here */ + if(ipType == ITEM_PROPERTY_SPEED_INCREASE) + { + int iItemAdjust; + switch(GetItemPropertyCostTableValue(ipTest)) + { + case 0: iItemAdjust = 10; break; + case 1: iItemAdjust = 20; break; + case 2: iItemAdjust = 30; break; + case 3: iItemAdjust = 40; break; + case 4: iItemAdjust = 50; break; + case 5: iItemAdjust = 60; break; + case 6: iItemAdjust = 70; break; + case 7: iItemAdjust = 80; break; + case 8: iItemAdjust = 90; break; + case 9: iItemAdjust = 100; break; + } + if(bUnequip) + nSpeedIncrease -= iItemAdjust; + else if(bEquip) + nSpeedIncrease += iItemAdjust; + } + else if(ipType == ITEM_PROPERTY_SPEED_DECREASE) + { + int iItemAdjust; + switch(GetItemPropertyCostTableValue(ipTest)) + { + case 0: iItemAdjust = 10; break; + case 1: iItemAdjust = 20; break; + case 2: iItemAdjust = 30; break; + case 3: iItemAdjust = 40; break; + case 4: iItemAdjust = 50; break; + case 5: iItemAdjust = 60; break; + case 6: iItemAdjust = 70; break; + case 7: iItemAdjust = 80; break; + case 8: iItemAdjust = 90; break; + case 9: iItemAdjust = 99; break; + } + if(bUnequip) + nSpeedDecrease -= iItemAdjust; + else if(bEquip) + nSpeedDecrease += iItemAdjust; + } + else if(ipType == ITEM_PROPERTY_PNP_HOLY_AVENGER) + { + if(bEquip) + { + int nPaladinLevels = GetLevelByClass(CLASS_TYPE_PALADIN, oPC); + if(!nPaladinLevels) + { + //not a paladin? fake it + //not really a true PnP test + //instead it sets the paladin level + //to the UMD ranks minus the amount required + //to use a class restricted item of that value + int nSkill = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oPC); + if(nSkill) + { + int nReqSkill = GetUMDForItemCost(oItem); + nSkill -= nReqSkill; + if(nSkill > 0) + nPaladinLevels = nSkill; + } + } + + // Add Holy Avenger specials for Paladins (or successfull fake-Paladins) + if(nPaladinLevels) + { + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyEnhancementBonus(5), 99999.9)); + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, + IP_CONST_DAMAGETYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), 99999.9)); + //this is a normal dispel magic useage, should be specific + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyCastSpell(IP_CONST_CASTSPELL_DISPEL_MAGIC_5, + IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE), 99999.9)); + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyCastSpellCasterLevel(SPELL_DISPEL_MAGIC, + nPaladinLevels), 99999.9)); + } + // Non-Paladin's get +2 enhancement bonus + else + { + DelayCommand(0.1, IPSafeAddItemProperty(oItem, + ItemPropertyEnhancementBonus(2), 99999.9)); + + // Remove Paladin specials + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, DURATION_TYPE_TEMPORARY, -1); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP, DURATION_TYPE_TEMPORARY, IP_CONST_ALIGNMENTGROUP_EVIL); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL, DURATION_TYPE_TEMPORARY); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL, DURATION_TYPE_TEMPORARY); + } + } + else if(bUnequip) + { + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, + DURATION_TYPE_TEMPORARY, -1); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP, + DURATION_TYPE_TEMPORARY, IP_CONST_ALIGNMENTGROUP_EVIL); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL, + DURATION_TYPE_TEMPORARY); + IPRemoveMatchingItemProperties(oItem, ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL, + DURATION_TYPE_TEMPORARY); + } + } + else if(ipType == ITEM_PROPERTY_AREA_OF_EFFECT) + { + + // This should only happen on equip or unequip + if(bEquip || bUnequip) + { + // Remove existing AoE + effect eTest = GetFirstEffect(oPC); + while(GetIsEffectValid(eTest)) + { + if(GetEffectCreator(eTest) == oItem + && GetEffectType(eTest) == EFFECT_TYPE_AREA_OF_EFFECT) + { + RemoveEffect(oPC, eTest); + if(DEBUG) DoDebug("CheckPRCLimitations: Removing old AoE effect"); + } + eTest = GetNextEffect(oPC); + } + + // Create new AoE - Only when equipping + if(bEquip) + { + AssignCommand(oItem, _prc_inc_itmrstr_ApplyAoE(oPC, oItem, GetItemPropertySubType(ipTest), GetItemPropertyCostTable(ipTest))); + }// end if - Equip event + }// end if - Equip or Unequip event + }// end if - AoE iprp + else if(ipType == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + // Only equippable items can provide bonus spell slots + if(bEquip || bUnequip) + { + int nSubType = GetItemPropertySubType(ipTest); + int nCost = GetItemPropertyCostTable(ipTest); + SetLocalInt(oPC, + "PRC_IPRPBonSpellSlots_" + IntToString(nSubType) + "_" + IntToString(nCost), + GetLocalInt(oPC, + "PRC_IPRPBonSpellSlots_" + IntToString(nSubType) + "_" + IntToString(nCost) + ) + + (bEquip ? 1 : -1) + ); + } + } + else if(ipType == ITEM_PROPERTY_WIZARDRY) + { + int nCost = GetItemPropertyCostTableValue(ipTest); + if(bEquip) + AssignCommand(oItem, _prc_inc_itmrstr_ApplyWizardry(oPC, oItem, nCost, "A")); + else if(bUnequip) + AssignCommand(oItem, _prc_inc_itmrstr_RemoveWizardry(oPC, oItem, nCost, "A")); + } + else if(ipType == ITEM_PROPERTY_DIVINITY) + { + int nCost = GetItemPropertyCostTableValue(ipTest); + if(bEquip) + AssignCommand(oItem, _prc_inc_itmrstr_ApplyWizardry(oPC, oItem, nCost, "D")); + else if(bUnequip) + AssignCommand(oItem, _prc_inc_itmrstr_RemoveWizardry(oPC, oItem, nCost, "D")); + } + + ipTest = GetNextItemProperty(oItem); + }// end while - Loop over all itemproperties + + // Determine if speed modification totals had changed + if(nSpeedDecrease != GetLocalInt(oPC, PLAYER_SPEED_DECREASE)) + { + SetLocalInt(oPC, PLAYER_SPEED_DECREASE, nSpeedDecrease); + //_prc_inc_itmrstr_ApplySpeedDecrease(oPC); + } + if(nSpeedIncrease != GetLocalInt(oPC, PLAYER_SPEED_INCREASE)) + { + SetLocalInt(oPC, PLAYER_SPEED_INCREASE, nSpeedIncrease); + //_prc_inc_itmrstr_ApplySpeedIncrease(oPC); + } + + // If some restriction would prevent item use, perform UMD skill check + // Skip in case of unequip + if(!bUnequip && !bPass) + bPass = DoUMDCheck(oItem, oPC, nUMDDC); + + return bPass; +} + +void CheckForPnPHolyAvenger(object oItem) +{ + if(!GetPRCSwitch(PRC_PNP_HOLY_AVENGER_IPROP)) + return; + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_HOLY_AVENGER) + { + DelayCommand(0.1, RemoveItemProperty(oItem, ipTest)); + DelayCommand(0.1, IPSafeAddItemProperty(oItem, ItemPropertyPnPHolyAvenger())); + } + ipTest = GetNextItemProperty(oItem); + } +} + +//:: Test Void +//void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_leadersh.nss b/src/include/prc_inc_leadersh.nss new file mode 100644 index 0000000..32b67a5 --- /dev/null +++ b/src/include/prc_inc_leadersh.nss @@ -0,0 +1,1061 @@ +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string COHORT_DATABASE = "PRCCOHORTS"; +const string COHORT_TAG = "prc_cohort"; + +//in the database there is the folloxing data structures: +/* + int CohortCount (total number of cohorts) + object Cohort_X_obj (cohort itself) + string Cohort_X_name (cohort name) + int Cohort_X_race (cohort race) + int Cohort_X_class1 (cohort class pos1) + int Cohort_X_class2 (cohort class pos2) + int Cohort_X_class3 (cohort class pos3) + int Cohort_X_class4 (cohort class pos4) + int Cohort_X_class5 (cohort class pos5) + int Cohort_X_class6 (cohort class pos6) + int Cohort_X_class7 (cohort class pos7) + int Cohort_X_class8 (cohort class pos8) + int Cohort_X_order (cohort law/chaos measure) + int Cohort_X_moral (cohort good/evil measure) + int Cohort_X_ethran (cohort has ethran feat) + string Cohort_X_cdkey (cdkey of owning player) +*/ + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +int GetMaximumCohortCount(object oPC); +object GetCohort(int nID, object oPC); +int GetCurrentCohortCount(object oPC); +int GetCohortMaxLevel(int nLeadership, object oPC); +void RegisterAsCohort(object oPC); +object AddCohortToPlayer(int nCohortID, object oPC); +void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE); +void RemoveCohortFromPlayer(object oCohort, object oPC); +int GetLeadershipScore(object oPC = OBJECT_SELF); +void CheckHB(object oPC); +void AddPremadeCohortsToDB(); +void StoreCohort(object oCohort); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_feat_const" +#include "nw_o2_coninclude" +//#include "inc_utility" +#include "inc_ecl" +#include "inc_nwnx_funcs" +//#include "pnp_shft_poly" //for DoRandomAppearance + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +object AddCohortToPlayer(int nCohortID, object oPC) +{ + object oCohort = RetrieveCampaignObject(COHORT_DATABASE, "Cohort_"+IntToString(nCohortID)+"_obj", GetLocation(oPC)); + //give it a tag + AssignCommand(oCohort, SetIsDestroyable(TRUE, FALSE, FALSE)); + DestroyObject(oCohort); + oCohort = CopyObject(oCohort, GetLocation(oPC), OBJECT_INVALID, COHORT_TAG); + SetLocalInt(oCohort, "CohortID", nCohortID); + //pass it to the next function + AddCohortToPlayerByObject(oCohort, oPC); + return oCohort; +} + +//changes portrait, head, and appearance +//based on the target race with a degree of randomization. +//This should only be used on NPCs, not players. +void DoRandomAppearance(int nRace, object oTarget = OBJECT_SELF) +{ + //store current appearance to be safe + int nAppearance; //appearance to change into + int nHeadMax; //max head ID, changed to random 1-max + int nGender = GetGender(oTarget); + int nPortraitMin;//minimum row in portraits.2da + int nPortraitMax;//maximum row in portraits.2da + switch(nRace) + { + case RACIAL_TYPE_DWARF: + nAppearance = APPEARANCE_TYPE_DWARF; + if(nGender == GENDER_MALE) + { nHeadMax = 10; nPortraitMin = 9; nPortraitMax = 17; } + else + { nHeadMax = 12; nPortraitMin = 1; nPortraitMax = 8; } + break; + case RACIAL_TYPE_ELF: + nAppearance = APPEARANCE_TYPE_ELF; + if(nGender == GENDER_MALE) + { nHeadMax = 10; nPortraitMin = 31; nPortraitMax = 40; } + else + { nHeadMax = 16; nPortraitMin = 18; nPortraitMax = 30; } + break; + case RACIAL_TYPE_HALFELF: + nAppearance = APPEARANCE_TYPE_HALF_ELF; + if(nGender == GENDER_MALE) + { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } + else + { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } + break; + case RACIAL_TYPE_HALFORC: + nAppearance = APPEARANCE_TYPE_HALF_ORC; + if(nGender == GENDER_MALE) + { nHeadMax = 11; nPortraitMin = 134; nPortraitMax = 139; } + else + { nHeadMax = 1; nPortraitMin = 130; nPortraitMax = 133; } + break; + case RACIAL_TYPE_HUMAN: + nAppearance = APPEARANCE_TYPE_HUMAN; + if(nGender == GENDER_MALE) + { nHeadMax = 18; nPortraitMin = 93; nPortraitMax = 112; } + else + { nHeadMax = 15; nPortraitMin = 67; nPortraitMax = 92; } + break; + case RACIAL_TYPE_HALFLING: + nAppearance = APPEARANCE_TYPE_HALFLING; + if(nGender == GENDER_MALE) + { nHeadMax = 8; nPortraitMin = 61; nPortraitMax = 66; } + else + { nHeadMax = 11; nPortraitMin = 54; nPortraitMax = 59; } + break; + case RACIAL_TYPE_GNOME: + nAppearance = APPEARANCE_TYPE_GNOME; + if(nGender == GENDER_MALE) + { nHeadMax = 11; nPortraitMin = 47; nPortraitMax = 53; } + else + { nHeadMax = 9; nPortraitMin = 41; nPortraitMax = 46; } + break; + default: //not a normal race, abort + return; + } + //change the appearance + SetCreatureAppearanceType(oTarget, nAppearance); + + //need to be delayed a bit otherwise you get "supergnome" and "exploded elf" effects + DelayCommand(1.1, SetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN, d2(), oTarget)); + DelayCommand(1.2, SetCreatureBodyPart(CREATURE_PART_LEFT_SHIN, d2(), oTarget)); + DelayCommand(1.3, SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, d2(), oTarget)); + DelayCommand(1.4, SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, d2(), oTarget)); + DelayCommand(1.5, SetCreatureBodyPart(CREATURE_PART_TORSO, d2(), oTarget)); + DelayCommand(1.6, SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, d2(), oTarget)); + DelayCommand(1.7, SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, d2(), oTarget)); + DelayCommand(1.8, SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, d2(), oTarget)); + DelayCommand(1.9, SetCreatureBodyPart(CREATURE_PART_LEFT_BICEP, d2(), oTarget)); + + //dont do these body parts, they dont have tattoos and weird things could happen + //SetCreatureBodyPart(CREATURE_PART_BELT, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_NECK, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_HAND, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_PELVIS, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, d2(), oTarget); + //SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, d2(), oTarget); + //randomise the head + DelayCommand(2.0, SetCreatureBodyPart(CREATURE_PART_HEAD, Random(nHeadMax)+1, oTarget)); + + //remove any wings/tails + SetCreatureWingType(CREATURE_WING_TYPE_NONE, oTarget); + SetCreatureTailType(CREATURE_TAIL_TYPE_NONE, oTarget); + + int nPortraitID = Random(nPortraitMax-nPortraitMin+1)+nPortraitMin; + string sPortraitResRef = Get2DACache("portraits", "BaseResRef", nPortraitID); + sPortraitResRef = GetStringLeft(sPortraitResRef, GetStringLength(sPortraitResRef)-1); //trim the trailing _ + SetPortraitResRef(oTarget, sPortraitResRef); + SetPortraitId(oTarget, nPortraitID); +} + +void CancelGreatFeats(object oSpawn) +{ + //store how many Great X feats they have + //this is to fix a bioware bug where de-leveling doesnt remove the stat bonus + int nGreatStr; + int nGreatDex; + int nGreatCon; + int nGreatInt; + int nGreatWis; + int nGreatCha; + if (GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_10, oSpawn)) nGreatStr = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_9, oSpawn)) nGreatStr = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_8, oSpawn)) nGreatStr = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_7, oSpawn)) nGreatStr = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_6, oSpawn)) nGreatStr = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_5, oSpawn)) nGreatStr = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_4, oSpawn)) nGreatStr = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_3, oSpawn)) nGreatStr = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_2, oSpawn)) nGreatStr = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_STRENGTH_1, oSpawn)) nGreatStr = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_10, oSpawn)) nGreatDex = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_9, oSpawn)) nGreatDex = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_8, oSpawn)) nGreatDex = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_7, oSpawn)) nGreatDex = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_6, oSpawn)) nGreatDex = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_5, oSpawn)) nGreatDex = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_4, oSpawn)) nGreatDex = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_3, oSpawn)) nGreatDex = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_2, oSpawn)) nGreatDex = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_DEXTERITY_1, oSpawn)) nGreatDex = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_10, oSpawn)) nGreatCon = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_9, oSpawn)) nGreatCon = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_8, oSpawn)) nGreatCon = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_7, oSpawn)) nGreatCon = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_6, oSpawn)) nGreatCon = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_5, oSpawn)) nGreatCon = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_4, oSpawn)) nGreatCon = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_3, oSpawn)) nGreatCon = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_2, oSpawn)) nGreatCon = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_CONSTITUTION_1, oSpawn)) nGreatCon = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_10, oSpawn)) nGreatInt = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_9, oSpawn)) nGreatInt = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_8, oSpawn)) nGreatInt = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_7, oSpawn)) nGreatInt = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_6, oSpawn)) nGreatInt = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_5, oSpawn)) nGreatInt = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_4, oSpawn)) nGreatInt = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_3, oSpawn)) nGreatInt = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_2, oSpawn)) nGreatInt = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_INTELLIGENCE_1, oSpawn)) nGreatInt = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_WISDOM_10, oSpawn)) nGreatWis = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_9, oSpawn)) nGreatWis = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_8, oSpawn)) nGreatWis = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_7, oSpawn)) nGreatWis = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_6, oSpawn)) nGreatWis = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_5, oSpawn)) nGreatWis = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_4, oSpawn)) nGreatWis = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_3, oSpawn)) nGreatWis = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_2, oSpawn)) nGreatWis = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_WISDOM_1, oSpawn)) nGreatWis = 1; + if (GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_10, oSpawn)) nGreatCha = 10; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_9, oSpawn)) nGreatCha = 9; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_8, oSpawn)) nGreatCha = 8; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_7, oSpawn)) nGreatCha = 7; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_6, oSpawn)) nGreatCha = 6; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_5, oSpawn)) nGreatCha = 5; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_4, oSpawn)) nGreatCha = 4; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_3, oSpawn)) nGreatCha = 3; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_2, oSpawn)) nGreatCha = 2; + else if(GetHasFeat(FEAT_EPIC_GREAT_CHARISMA_1, oSpawn)) nGreatCha = 1; + + //apply penalties to counter the GreatX feats + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + { + if(nGreatStr) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_STRENGTH, -nGreatStr); + if(nGreatDex) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_DEXTERITY, -nGreatDex); + if(nGreatCon) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_CONSTITUTION, -nGreatCon); + if(nGreatInt) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_INTELLIGENCE, -nGreatInt); + if(nGreatWis) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_WISDOM, -nGreatWis); + if(nGreatCha) PRC_Funcs_ModAbilityScore(oSpawn, ABILITY_CHARISMA, -nGreatCha); + } + else + { + if(nGreatStr) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_STRENGTH, nGreatStr)), + oSpawn); + if(nGreatDex) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_DEXTERITY, nGreatDex)), + oSpawn); + if(nGreatCon) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_CONSTITUTION, nGreatCon)), + oSpawn); + if(nGreatInt) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nGreatInt)), + oSpawn); + if(nGreatWis) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_WISDOM, nGreatWis)), + oSpawn); + if(nGreatCha) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, + SupernaturalEffect(EffectAbilityDecrease(ABILITY_CHARISMA, nGreatCha)), + oSpawn); + } +} + +void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE) +{ + //add it to the pc + int nMaxHenchmen = GetMaxHenchmen(); + SetMaxHenchmen(99); + AddHenchman(oPC, oCohort); + SetMaxHenchmen(nMaxHenchmen); + object oSkin = GetPCSkin(oCohort); + + if(bDoSetup) + { + //if it was a premade one, give it a random name + //randomize its appearance using DoRandomAppearance + if(GetResRef(oCohort) != "") + { + string sName; + //first name + switch(MyPRCGetRacialType(oCohort)) + { + case RACIAL_TYPE_DWARF: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_DWARF_FEMALE); + else + sName += RandomName(NAME_FIRST_DWARF_MALE); + break; + case RACIAL_TYPE_ELF: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_ELF_FEMALE); + else + sName += RandomName(NAME_FIRST_ELF_MALE); + break; + case RACIAL_TYPE_GNOME: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_GNOME_FEMALE); + else + sName += RandomName(NAME_FIRST_GNOME_MALE); + break; + case RACIAL_TYPE_HUMAN: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HUMAN_FEMALE); + else + sName += RandomName(NAME_FIRST_HUMAN_MALE); + break; + case RACIAL_TYPE_HALFELF: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HALFELF_FEMALE); + else + sName += RandomName(NAME_FIRST_HALFELF_MALE); + break; + case RACIAL_TYPE_HALFORC: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HALFORC_FEMALE); + else + sName += RandomName(NAME_FIRST_HALFORC_MALE); + break; + case RACIAL_TYPE_HALFLING: + if(GetGender(oCohort) == GENDER_FEMALE) + sName += RandomName(NAME_FIRST_HALFLING_FEMALE); + else + sName += RandomName(NAME_FIRST_HALFLING_MALE); + break; + } + sName += " "; + //surname + switch(MyPRCGetRacialType(oCohort)) + { + case RACIAL_TYPE_DWARF: + sName += RandomName(NAME_LAST_DWARF); + break; + case RACIAL_TYPE_ELF: + sName += RandomName(NAME_LAST_ELF); + break; + case RACIAL_TYPE_GNOME: + sName += RandomName(NAME_LAST_GNOME); + break; + case RACIAL_TYPE_HUMAN: + sName += RandomName(NAME_LAST_HUMAN); + break; + case RACIAL_TYPE_HALFELF: + sName += RandomName(NAME_LAST_HALFELF); + break; + case RACIAL_TYPE_HALFORC: + sName += RandomName(NAME_LAST_HALFORC); + break; + case RACIAL_TYPE_HALFLING: + sName += RandomName(NAME_LAST_HALFLING); + break; + } + //sanity check + if(sName == " ") + sName = ""; + //change the name + AssignCommand(oCohort, SetName(oCohort, sName)); + + //use disguise code to alter head etc + DoRandomAppearance(MyPRCGetRacialType(oCohort), oCohort); + + //DoRandomAppearance removed wings/tails need to re-add + if(GetRacialType(oCohort) == RACIAL_TYPE_FEYRI) + SetCreatureWingType(CREATURE_WING_TYPE_DEMON, oCohort); + else if(GetRacialType(oCohort) == RACIAL_TYPE_AVARIEL) + SetCreatureWingType(CREATURE_WING_TYPE_BIRD, oCohort); + else if(GetRacialType(oCohort) == RACIAL_TYPE_GLOURA) + SetCreatureWingType(CREATURE_WING_TYPE_BUTTERFLY, oCohort); + } + //if its a custom made cohort, need to cancel GreatX feats + else + CancelGreatFeats(oCohort); + + //set it to the pcs level + int nLevel = GetCohortMaxLevel(GetLeadershipScore(oPC), oPC); + SetXP(oCohort, nLevel*(nLevel-1)*500); + SetLocalInt(oCohort, "MastersXP", GetXP(oPC)); + DelayCommand(1.0, AssignCommand(oCohort, SetIsDestroyable(FALSE, TRUE, TRUE))); + DelayCommand(1.0, AssignCommand(oCohort, SetLootable(oCohort, TRUE))); + //set its maximum level lag + if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)) + { + //bonus cohort, no cap + } + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1) + { + //thrallherd with switch, 1 level lag + SetLocalInt(oCohort, "CohortLevelLag", 1); + } + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10 + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2) + { + //twofold master with switch, 2 level lag + SetLocalInt(oCohort, "CohortLevelLag", 2); + } + else + { + //other cohort have a 2 level lag + SetLocalInt(oCohort, "CohortLevelLag", 2); + } + + //strip its equipment & inventory + object oTest = GetFirstItemInInventory(oCohort); + object oToken = GetHideToken(oCohort); + while(GetIsObjectValid(oTest)) + { + if(GetHasInventory(oTest)) + { + object oTest2 = GetFirstItemInInventory(oTest); + while(GetIsObjectValid(oTest2)) + { + // Avoid blowing up the hide and token that just had the eventscripts stored on them + if(oTest2 != oSkin && oTest2 != oToken) + DestroyObject(oTest2); + oTest2 = GetNextItemInInventory(oTest); + } + } + // Avoid blowing up the hide and token that just had the eventscripts stored on them + if(oTest != oSkin && oTest != oToken) + DestroyObject(oTest); + oTest = GetNextItemInInventory(oCohort); + } + int nSlot; + for(nSlot = 0;nSlot<14;nSlot++) + { + oTest = GetItemInSlot(nSlot, oCohort); + DestroyObject(oTest); + } + //get rid of any gold it has + TakeGoldFromCreature(GetGold(oCohort), oCohort, TRUE); + } + //clean up any leftovers on the skin + ScrubPCSkin(oCohort, oSkin); + DeletePRCLocalInts(oSkin); + + //turn on its scripts + //normal MoB set + AddEventScript(oCohort, EVENT_VIRTUAL_ONPHYSICALATTACKED, "prc_ai_mob_attck", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONBLOCKED, "prc_ai_mob_block", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONCOMBATROUNDEND, "prc_ai_mob_combt", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONDAMAGED, "prc_ai_mob_damag", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONDISTURBED, "prc_ai_mob_distb", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONPERCEPTION, "prc_ai_mob_percp", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONSPAWNED, "prc_ai_mob_spawn", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONSPELLCASTAT, "prc_ai_mob_spell", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONDEATH, "prc_ai_mob_death", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONRESTED, "prc_ai_mob_rest", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONUSERDEFINED, "prc_ai_mob_userd", TRUE, FALSE); + //dont run this, cohort-specific script replaces it + //AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_mob_conv", TRUE, TRUE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_mob_heart", TRUE, FALSE); + //cohort specific ones + AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_coh_conv", TRUE, FALSE); + AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_coh_hb", TRUE, FALSE); + + //mark the master on the cohort + SetLocalObject(oCohort, "MasterObject", oPC); + + //DEBUG + //various tests + if (DEBUG) DoDebug("Cohort Name="+GetName(oCohort)); + if (DEBUG) DoDebug("Cohort HD="+IntToString(GetHitDice(oCohort))); + if (DEBUG) DoDebug("Cohort XP="+IntToString(GetXP(oCohort))); + if (DEBUG) DoDebug("Cohort GetIsPC="+IntToString(GetIsPC(oCohort))); + + // And now gear it up + if (!GetPRCSwitch(PRC_DISABLE_COHORT_STARTING_GEAR)) + { + int i; + int nHD = GetHitDice(oCohort); + for(i = 0;i= 3) nLeadership += GetLevelByClass(CLASS_TYPE_SHADOW_THIEF_AMN, oPC) - 2; + //without epic leadership its capped at 25 + if(!GetHasFeat(FEAT_EPIC_LEADERSHIP, oPC) && nLeadership > 25) + nLeadership = 25; + + return nLeadership; +} + +void StoreCohort(object oCohort) +{ + int nCohortCount = GetCampaignInt(COHORT_DATABASE, "CohortCount"); + int i; + for(i=0;i 1) + return; + SetLocalInt(oPC, "CohortCheckHB", GetLocalInt(oPC, "CohortCheckHB")+1); + DelayCommand(0.99, + SetLocalInt(oPC, "CohortCheckHB", GetLocalInt(oPC, "CohortCheckHB")-1)); + SetCommandable(FALSE, oPC); + if(GetHitDice(oPC) == 40) + { + StoreCohort(oPC); + //restore previous xp amound + SetXP(oPC, GetLocalInt(oPC, "OriginalXP")); + //tell the player what was done + SendMessageToPC(oPC, "Character registered as cohort."); + //remove the non-commandabiltiy + SetCommandable(TRUE, oPC); + // Clean up + DeletePersistantLocalInt(oPC, "RegisteringAsCohort"); + DeleteLocalInt(oPC, "OriginalXP"); + //stop the psuedoHB + return; + } + DelayCommand(1.0, CheckHB(oPC)); +} + +void RegisterAsCohort(object oPC) +{ + string sMessage; + sMessage += "This will register you character to be selected as a cohort.\n"; + sMessage += "As part of this process, you have to levelup to level 40.\n"; + sMessage += "Once you reach level 40, your character will be stored.\n"; + sMessage += "Then when the character is used as a cohort, it will follow that levelup path.\n"; + sMessage += "Any changes to the cohort will not apply to the original character.\n"; + //SendMessageToPC(oPC, sMessage); + FloatingTextStringOnCreature(sMessage, oPC); + + SetLocalInt(oPC, "OriginalXP", GetXP(oPC)); + SetXP(oPC, 40*(40-1)*500); + SetPersistantLocalInt(oPC, "RegisteringAsCohort", TRUE); + AssignCommand(GetModule(), CheckHB(oPC)); +} + +int LeadershipScore2CohortLevel(int nLeadership) +{ + switch(nLeadership) + { + case 1: return 0; + case 2: return 1; + case 3: return 2; + case 4: + case 5: return 3; + case 6: return 4; + case 7: + case 8: return 5; + case 9: return 6; + case 10: + case 11: return 7; + case 12: return 8; + case 13: return 9; + case 14: + case 15: return 10; + case 16: return 11; + case 17: + case 18: return 12; + case 19: return 13; + case 20: return 14; + case 21: + case 22: return 15; + case 23: return 16; + case 24: + case 25: return 17; + case 26: + case 27: return 18; + case 28: + case 29: return 19; + case 30: + case 31: return 20; + case 32: + case 33: return 21; + case 34: + case 35: return 22; + case 36: + case 37: return 23; + case 38: + case 39: return 24; + case 40: + case 41: return 25; + case 42: + case 43: return 26; + case 44: + case 45: return 27; + case 46: + case 47: return 28; + case 48: + case 49: return 29; + case 50: + case 51: return 30; + case 52: + case 53: return 31; + case 54: + case 55: return 32; + case 56: + case 57: return 33; + case 58: + case 59: return 34; + case 60: return 35; + case 61: + case 62: return 36; + case 63: + case 64: return 37; + case 65: + case 66: return 38; + case 67: + case 68: return 39; + case 69: + case 70: return 40; + } + return 0; +} + +int GetCohortMaxLevel(int nLeadership, object oPC) +{ + //if its a bonus cohort, use the players ECL + int nMasterLevel = GetECL(oPC); + if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)) + return nMasterLevel; + int nLevel = LeadershipScore2CohortLevel(nLeadership); + //apply a level lag + if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1) + { + //thrallherd with switch, 1 level lag + if(nLevel > nMasterLevel-1) + nLevel = nMasterLevel-1; + } + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP) + && GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10 + && GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2) + { + //twofold master with switch, 2 level lag + if(nLevel > nMasterLevel-2) + nLevel = nMasterLevel-2; + } + else + { + //other cohort have a 2 level lag + if(nLevel > nMasterLevel-2) + nLevel = nMasterLevel-2; + if (GetHasFeat(FEAT_IMPROVED_COHORT, oPC)) nLevel += 1; + } + //really, leadership should be capped at 25 / 17HD + //but this is a sanity check + if(nLevel > 20 + && !GetHasFeat(FEAT_EPIC_LEADERSHIP, oPC)) + nLevel = 20; + return nLevel; +} + +int GetCurrentCohortCount(object oPC) +{ + int nCount; + object oTest; + object oOldTest; + int i = 1; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + while(GetIsObjectValid(oTest) && oTest != oOldTest) + { + if(GetTag(oTest) == COHORT_TAG) + nCount++; + i++; + oOldTest = oTest; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + } + return nCount; +} + +object GetCohort(int nID, object oPC) +{ + int nCount; + object oTest; + object oOldTest; + int i = 1; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + while(GetIsObjectValid(oTest) && oTest != oOldTest) + { + if(GetTag(oTest) == COHORT_TAG) + nCount++; + if(nCount == nID) + return oTest; + i++; + oOldTest = oTest; + oTest = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, i); + } + return OBJECT_INVALID; +} + +int GetMaximumCohortCount(object oPC) +{ + int nCount; + if(!GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC)) + { + if(GetHasFeat(FEAT_LEADERSHIP, oPC)) + nCount++; + if(GetHasFeat(FEAT_LEGENDARY_COMMANDER, oPC)) + nCount++; + } + //thrallherd with switch + else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP)) + { + nCount++; + //twofold masteer + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) > 9) + nCount++; + } + //hathran class + if(GetHasFeat(FEAT_HATH_COHORT, oPC)) + nCount++; + //orc warlord with switch + if(GetHasFeat(FEAT_GATHER_HORDE_I, oPC) + && GetPRCSwitch(PRC_ORC_WARLORD_COHORT)) + nCount++; + nCount += GetPRCSwitch(PRC_BONUS_COHORTS); + return nCount; +} + +int GetIsCohortChoiceValid(string sName, int nRace, int nClass1, int nClass2, int nClass3, int nClass4, int nClass5, int nClass6, int nClass7, int nClass8, int nOrder, int nMoral, int nEthran, string sKey, int nDeleted, object oPC) +{ + //has been deleted + if(nDeleted) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because cohort had been deleted"); + return FALSE; + } + + int bIsValid = TRUE; + int nCohortCount = GetMaximumCohortCount(oPC); + int i; + //another players cohort + if(GetPCPublicCDKey(oPC) != "" + && GetPCPublicCDKey(oPC) != sKey) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because cdkey is incorrect"); + bIsValid = FALSE; + } + //is character + if(bIsValid + && GetName(oPC) == sName) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because name is in use"); + bIsValid = FALSE; + } + //is already a cohort + if(bIsValid && sName != "") + { + for(i=1;i<=nCohortCount;i++) + { + object oCohort = GetCohort(i, oPC); + if(GetName(oCohort) == sName) + { + DoDebug("GetIsCohortChoiceValid() is FALSE because cohort is already in use."); + bIsValid = FALSE; + } + } + } + //hathran + if(bIsValid + && GetHasFeat(FEAT_HATH_COHORT, oPC)) + { + int nEthranBarbarianCount = 0; + for(i=1;i<=nCohortCount;i++) + { + object oCohort = GetCohort(i, oPC); + if(GetIsObjectValid(oCohort) + &&(GetHasFeat(FEAT_HATH_COHORT, oCohort) + || GetLevelByClass(CLASS_TYPE_BARBARIAN, oCohort))) + nEthranBarbarianCount++; + } + //must have at least one ethran or barbarian + if(!nEthranBarbarianCount + && GetCurrentCohortCount(oPC) >= GetMaximumCohortCount(oPC)-1 + && !nEthran + && nClass1 != CLASS_TYPE_BARBARIAN + && nClass2 != CLASS_TYPE_BARBARIAN + && nClass3 != CLASS_TYPE_BARBARIAN + && nClass4 != CLASS_TYPE_BARBARIAN + && nClass5 != CLASS_TYPE_BARBARIAN + && nClass6 != CLASS_TYPE_BARBARIAN + && nClass7 != CLASS_TYPE_BARBARIAN + && nClass8 != CLASS_TYPE_BARBARIAN) + bIsValid = FALSE; + } + //OrcWarlord + if(bIsValid + && GetHasFeat(FEAT_GATHER_HORDE_I, oPC) + && GetPRCSwitch(PRC_ORC_WARLORD_COHORT)) + { + int nOrcCount = 0; + for(i=1;i<=nCohortCount;i++) + { + object oCohort = GetCohort(i, oPC); + if(GetIsObjectValid(oCohort) + && (MyPRCGetRacialType(oCohort) == RACIAL_TYPE_HUMANOID_ORC + || MyPRCGetRacialType(oCohort) == RACIAL_TYPE_HALFORC)) + nOrcCount++; + } + //must have at least one orc + if(!nOrcCount + && GetCurrentCohortCount(oPC) >= GetMaximumCohortCount(oPC)-1 + && nRace != RACIAL_TYPE_HUMANOID_ORC + && nRace != RACIAL_TYPE_HALFORC + && nRace != RACIAL_TYPE_GRAYORC + && nRace != RACIAL_TYPE_OROG + && nRace != RACIAL_TYPE_TANARUKK + && nRace != RACIAL_TYPE_FROSTBLOOD_ORC + ) + bIsValid = FALSE; + } + //Undead Leadership + //Wild Cohort + //not implemented yet + //return result + return bIsValid; +} + +int GetIsCohortChoiceValidByID(int nID, object oPC) +{ + string sID = IntToString(nID); + string sName = GetCampaignString( COHORT_DATABASE, "Cohort_"+sID+"_name"); + int nRace = GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_race"); + int nClass1=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class1"); + int nClass2=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class2"); + int nClass3=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class3"); + int nClass4=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class4"); + int nClass5=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class5"); + int nClass6=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class6"); + int nClass7=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class7"); + int nClass8=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_class8"); + int nOrder= GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_order"); + int nMoral= GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_moral"); + int nEthran=GetCampaignInt( COHORT_DATABASE, "Cohort_"+sID+"_ethran"); + string sKey = GetCampaignString( COHORT_DATABASE, "Cohort_"+sID+"_cdkey"); + int nDeleted = GetCampaignInt(COHORT_DATABASE, "Cohort_"+sID+"_deleted"); + return GetIsCohortChoiceValid(sName, nRace, nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8, nOrder, nMoral, nEthran, sKey, nDeleted, oPC); +} + +int GetCanRegister(object oPC) +{ + int bReturn = TRUE; + int i; + int nCohortCount = GetCampaignInt(COHORT_DATABASE, "CohortCount"); + for(i=0;i 0), the listener + will destroy itself once that time has run out. + It will also destroy itself if it has been set to listen + to a particular object and that object ceases being valid. + + + Regarding the patterns used, taken from NWN Lexicon: + + >From Noel (Bioware): + >** will match zero or more characters + >*w one or more whitespace + >*n one or more numeric + >*p one or more punctuation + >*a one or more alphabetic + >| is or + >( and ) can be used for blocks + > + >- setting a creature to listen for "**" will match any string + >- telling him to listen for "**funk**" will match any string that contains the word "funk". + >- "**(bash|open|unlock)**(chest|door)**" will match strings like "open the door please" or "he just bashed that chest!" + + If several patterns would match the same string, the one + added first using AddPattern will be the one matched. +*/ +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 19.06.2005 +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +//const string LISTENER_STORE_VARIABLE = "PRC_SGListener_Last_Heard_String"; +//const int PRC_GENERIC_LISTENER_FIRST_PATTERN_NUMBER = 0xffff; +const string PRC_GENERIC_LISTENER_SCRIPT_NAME = "prc_glist_onhb"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Creates a new listener. See prc_inc_listener header for details. + * + * @param sScriptToCall The name of the script to call when the listener hears something matching it's pattern. + * @param lSpawnAt Location to spawnt the listener at. This is ignored if oListenTo is a valid object. + * @param sPattern A regular expression defining the pattern of strings the listener listens to. + * Defaults to everything. + * @param oListenTo Object to listen to. If this is specified, the listener will be spawned by it instead of at + * lSpawnAt and will attempt to follow it around. It will also only fire the script for + * strings spoken by this object. + * @param fTTL How long the listener will exist. Once this runs out, the listener will self-destruct. A + * value of 0.0f or less means the listener is permanent (unless the object it is listening + * to ceases being valid if one was specified). + * NOTE: Due to some weirdness in when the listener actually starts listening, it won't + * actually hear anything for the first 3 or so seconds of it's existence. + * seconds of this duration + * @param bNotify If this is TRUE, the listener will give a notice when it's spawned to either the target it's + * listening to, or everyone in the area it's listening in. + * + * @return The newly created listener. + */ +object SpawnListener(string sScriptToCall, location lSpawnAt, + string sPattern = "**", object oListenTo = OBJECT_INVALID, float fTTL = 0.0f, int bNotify = TRUE, string sNewTag = ""); + +/** + * Adds a new pattern for the specified listener to listen to. Not recommended for use with + * creatures other than those created with SpawnListener(). + * + * @param oListener The listener object to add the pattern to. + * @param sPattern A regular expression defining the pattern of strings the listener listens to. + * @param sScriptToCall The name of the script to call when the listener hears something matching the + * given pattern. + */ +void AddPattern(object oListener, string sPattern, string sScriptToCall); + +/** + * Nulls the listener's listening patterns and destroys it. Use this instead of + * DestroyObject(), because + * 1) Destruction is not immediate, so nulling the listening patterns is required + * to make sure it does not register any more strings in the meantime. + * 2) The listener is normally set undestroyable, which this function undoes + * automatically. + * + * @param oListener The listener object to destroy. + * @param bFirst Whether the function is being run for the first time, + * or it has already recuresed. When calling, always leave + * this to default (ie, TRUE). + */ +void DestroyListener(object oListener, int bFirst = TRUE); + +//#include "inc_utility" +#include "prc_alterations" + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +object SpawnListener(string sScriptToCall, location lSpawnAt, + string sPattern = "**", object oListenTo = OBJECT_INVALID, float fTTL = 0.0f, int bNotify = TRUE, string sNewTag = "") +{ + // Check for whether we are listening to a single object or at a location + if(GetIsObjectValid(oListenTo)) // Use GetIsObjectValid instead of direct comparison to OBJECT_INVALID, because using an invalid location may crash the game. + lSpawnAt = GetLocation(oListenTo); + + object oListener = CreateObject(OBJECT_TYPE_CREATURE, "prc_gen_listener", lSpawnAt, FALSE, sNewTag); + // Paranoia check + if(!GetIsObjectValid(oListener)) + { + string sErr = "prc_inc_listener: SpawnListener(): ERROR: created listener is invalid!\n" + + "sScriptToCall = '" + sScriptToCall + "'\n" + + "lSpawnAt = " + DebugLocation2Str(lSpawnAt) + "\n" + + "sPattern = '" + sPattern + "'\n" + + "oListenTo = " + DebugObject2Str(oListenTo) + "\n" + + "fTTL = " + FloatToString(fTTL) + "\n" + + "bNotify = " + DebugBool2String(bNotify) + "\n"; + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + return OBJECT_INVALID; + } + + // A few more tricks to make sure the listener will will not be affected by anything + SetImmortal(oListener, TRUE); + SetPlotFlag(oListener, TRUE); + AssignCommand(oListener, SetIsDestroyable(FALSE, FALSE, FALSE)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneGhost(), oListener); + // Or seen + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oListener, 9999.0f); + + // Set the number of heartbeats until refreshing the invisbility VFX + SetLocalInt(oListener, "PRC_GenericListener_VFXRefreshTimer", 1500); + + // The actual listening part + SetListening(oListener, TRUE); + AddPattern(oListener, sPattern, sScriptToCall); + + // Store data for use in heartbeat depending on whether listening to a single creature or an area + if(GetIsObjectValid(oListenTo)) + { + SetLocalInt(oListener, "PRC_GenericListener_ListenToSingle", TRUE); + SetLocalObject(oListener, "PRC_GenericListener_ListeningTo", oListenTo); + + // Make the listener start following the target + AssignCommand(oListener, ActionForceFollowObject(oListenTo)); + } + else + SetLocalLocation(oListener, "PRC_GenericListener_ListeningLocation", lSpawnAt); + + // Has limited duration? + if(fTTL > 0.0f) + { + DelayCommand(fTTL, DestroyListener(oListener)); + + // Paranoia - also set a timer so that the listener destroys itself on hearbeat once it's time is up + SetLocalInt(oListener, "PRC_GenericListener_HasLimitedDuration", TRUE); + SetLocalInt(oListener, "PRC_GenericListener_DestroyTimer", FloatToInt(fTTL + 6.0f) / 6); + } + + // Should we send a notice that the listener is working + if(!bNotify) + SetLocalInt(oListener, "PRC_GenericListener_NoNotification", TRUE); + + AddEventScript(oListener, EVENT_ONHEARTBEAT, PRC_GENERIC_LISTENER_SCRIPT_NAME, TRUE, FALSE); + + return oListener; +} + +void AddPattern(object oListener, string sPattern, string sScriptToCall) +{ + // Get the index to store the pattern in + int nPattern = GetLocalInt(oListener, "PRC_GenericListener_FreePattern"); + + SetListenPattern(oListener, sPattern, nPattern); + SetLocalString(oListener, "PRC_GenericListener_ListenScript_" + IntToString(nPattern), sScriptToCall); + + SetLocalInt(oListener, "PRC_GenericListener_FreePattern", nPattern + 1); +} + +void DestroyListener(object oListener, int bFirst = TRUE) +{ + if(bFirst) + { + int nMax = GetLocalInt(oListener, "PRC_GenericListener_FreePattern"); + int i; + for(i = 0; i < nMax; i++) + { + SetListenPattern(oListener, "", i); + DeleteLocalString(oListener, "PRC_GenericListener_ListenScript_" + IntToString(i)); + } + + SetCommandable(TRUE, oListener); + AssignCommand(oListener, ClearAllActions()); + + RemoveEventScript(oListener, EVENT_ONHEARTBEAT, PRC_GENERIC_LISTENER_SCRIPT_NAME, TRUE, FALSE); + } + + AssignCommand(oListener, SetIsDestroyable(TRUE, FALSE, FALSE)); + AssignCommand(oListener, DestroyObject(oListener)); + + DestroyObject(oListener); + + DelayCommand(0.5f, DestroyListener(oListener, FALSE)); +} diff --git a/src/include/prc_inc_material.nss b/src/include/prc_inc_material.nss new file mode 100644 index 0000000..f69e26d --- /dev/null +++ b/src/include/prc_inc_material.nss @@ -0,0 +1,989 @@ +//:: // Material ItemProperty library // +//:://////////////////////////////////////////////////////////////////////////// +//:: prc_inc_material +//:: +//:: Contains constants and functions for use with material itemproperties +//:: +//:: Original by Axe Murderer +//:: +//:://////////////////////////////////////////////////////////////////////////// +#include "prc_x2_itemprop" + +//:: Material Type Constants +//:://////////////////////////////////////////////////////////////////////////// +const int MATERIAL_TYPE_INVALID = -1; +const int MATERIAL_TYPE_UNKNOWN = 0; +const int MATERIAL_TYPE_BONE = 1; +const int MATERIAL_TYPE_CERAMIC = 2; +const int MATERIAL_TYPE_CRYSTAL = 3; +const int MATERIAL_TYPE_FABRIC = 4; +const int MATERIAL_TYPE_LEATHER = 5; +const int MATERIAL_TYPE_METAL = 6; +const int MATERIAL_TYPE_PAPER = 7; +const int MATERIAL_TYPE_ROPE = 8; +const int MATERIAL_TYPE_STONE = 9; +const int MATERIAL_TYPE_WOOD = 10; + +const string MATERIAL_TYPE_NAME_INVALID = ""; +const string MATERIAL_TYPE_NAME_UNKNOWN = "Unknown"; +const string MATERIAL_TYPE_NAME_BONE = "Bone"; +const string MATERIAL_TYPE_NAME_CERAMIC = "Ceramic"; +const string MATERIAL_TYPE_NAME_CRYSTAL = "Crystal"; +const string MATERIAL_TYPE_NAME_FABRIC = "Fabric"; +const string MATERIAL_TYPE_NAME_LEATHER = "Leather"; +const string MATERIAL_TYPE_NAME_METAL = "Metal"; +const string MATERIAL_TYPE_NAME_PAPER = "Paper"; +const string MATERIAL_TYPE_NAME_ROPE = "Rope"; +const string MATERIAL_TYPE_NAME_STONE = "Stone"; +const string MATERIAL_TYPE_NAME_WOOD = "Wood"; + +//:: Material Itemproperty Constants +//:://////////////////////////////////////////////////////////////////////////////// +//:: Bioware Materials +const int IP_MATERIAL_INVALID = -1; +const int IP_MATERIAL_UNKNOWN = 0; +const int IP_MATERIAL_ADAMANTINE = 1; +const int IP_MATERIAL_BRASS = 2; +const int IP_MATERIAL_BRONZE = 3; +const int IP_MATERIAL_CARBON = 4; +const int IP_MATERIAL_COLD_IRON = 5; +const int IP_MATERIAL_COPPER = 6; +const int IP_MATERIAL_DARKSTEEL = 7; +const int IP_MATERIAL_GOLD = 8; +const int IP_MATERIAL_IRON = 9; +const int IP_MATERIAL_LEAD = 10; +const int IP_MATERIAL_MITHRAL = 11; +const int IP_MATERIAL_PLATINUM = 12; +const int IP_MATERIAL_SILVER = 13; +const int IP_MATERIAL_SILVER_ALCHEMICAL = 14; +const int IP_MATERIAL_STEEL = 15; +const int IP_MATERIAL_BONE = 16; +const int IP_MATERIAL_HIDE = 17; +const int IP_MATERIAL_HIDE_SALAMANDER = 18; +const int IP_MATERIAL_HIDE_UMBER_HULK = 19; +const int IP_MATERIAL_HIDE_WYVERN = 20; +const int IP_MATERIAL_HIDE_DRAGON_BLACK = 21; +const int IP_MATERIAL_HIDE_DRAGON_BLUE = 22; +const int IP_MATERIAL_HIDE_DRAGON_BRASS = 23; +const int IP_MATERIAL_HIDE_DRAGON_BRONZE = 24; +const int IP_MATERIAL_HIDE_DRAGON_COPPER = 25; +const int IP_MATERIAL_HIDE_DRAGON_GOLD = 26; +const int IP_MATERIAL_HIDE_DRAGON_GREEN = 27; +const int IP_MATERIAL_HIDE_DRAGON_RED = 28; +const int IP_MATERIAL_HIDE_DRAGON_SILVER = 29; +const int IP_MATERIAL_HIDE_DRAGON_WHITE = 30; +const int IP_MATERIAL_LEATHER = 31; +const int IP_MATERIAL_SCALE = 32; +const int IP_MATERIAL_CLOTH = 33; +const int IP_MATERIAL_COTTON = 34; +const int IP_MATERIAL_SILK = 35; +const int IP_MATERIAL_WOOL = 36; +const int IP_MATERIAL_WOOD = 37; +const int IP_MATERIAL_WOOD_IRONWOOD = 38; +const int IP_MATERIAL_WOOD_DUSKWOOD = 39; +const int IP_MATERIAL_WOOD_DARKWOOD_ZALANTAR = 40; +const int IP_MATERIAL_WOOD_ASH = 41; +const int IP_MATERIAL_WOOD_YEW = 42; +const int IP_MATERIAL_WOOD_OAK = 43; +const int IP_MATERIAL_WOOD_PINE = 44; +const int IP_MATERIAL_WOOD_CEDAR = 45; +const int IP_MATERIAL_ELEMENTAL = 46; +const int IP_MATERIAL_ELEMENTAL_AIR = 47; +const int IP_MATERIAL_ELEMENTAL_EARTH = 48; +const int IP_MATERIAL_ELEMENTAL_FIRE = 49; +const int IP_MATERIAL_ELEMENTAL_WATER = 50; +const int IP_MATERIAL_GEM = 51; +const int IP_MATERIAL_GEM_ALEXANDRITE = 52; +const int IP_MATERIAL_GEM_AMETHYST = 53; +const int IP_MATERIAL_GEM_AVENTURINE = 54; +const int IP_MATERIAL_GEM_BELJURIL = 55; +const int IP_MATERIAL_GEM_BLOODSTONE = 56; +const int IP_MATERIAL_GEM_BLUE_DIAMOND = 57; +const int IP_MATERIAL_GEM_CANARY_DIAMOND = 58; +const int IP_MATERIAL_GEM_DIAMOND = 59; +const int IP_MATERIAL_GEM_EMERALD = 60; +const int IP_MATERIAL_GEM_FIRE_AGATE = 61; +const int IP_MATERIAL_GEM_FIRE_OPAL = 62; +const int IP_MATERIAL_GEM_FLUORSPAR = 63; +const int IP_MATERIAL_GEM_GARNET = 64; +const int IP_MATERIAL_GEM_GREENSTONE = 65; +const int IP_MATERIAL_GEM_JACINTH = 66; +const int IP_MATERIAL_GEM_KINGS_TEAR = 67; +const int IP_MATERIAL_GEM_MALACHITE = 68; +const int IP_MATERIAL_GEM_OBSIDIAN = 69; +const int IP_MATERIAL_GEM_PHENALOPE = 70; +const int IP_MATERIAL_GEM_ROGUE_STONE = 71; +const int IP_MATERIAL_GEM_RUBY = 72; +const int IP_MATERIAL_GEM_SAPPHIRE = 73; +const int IP_MATERIAL_GEM_STAR_SAPPHIRE = 74; +const int IP_MATERIAL_GEM_TOPAZ = 75; +const int IP_MATERIAL_GEM_CRYSTAL_DEEP = 76; +const int IP_MATERIAL_GEM_CRYSTAL_MUNDANE = 77; +const int IP_MATERIAL_PAPER = 100; +const int IP_MATERIAL_GLASS = 101; +const int IP_MATERIAL_ICE = 102; +const int IP_MATERIAL_ROPE_HEMP = 103; +const int IP_MATERIAL_STONE = 104; +const int IP_MATERIAL_DEEP_CORAL = 105; +const int IP_MATERIAL_WOOD_LIVING = 106; +const int IP_MATERIAL_OBDURIUM = 107; +const int IP_MATERIAL_WOOD_BRONZE = 108; +const int IP_MATERIAL_BYESHK = 109; +const int IP_MATERIAL_CALOMEL = 110; +const int IP_MATERIAL_CRYSTEEL_RIEDRAN = 111; +const int IP_MATERIAL_DENSEWOOD = 112; +const int IP_MATERIAL_DRAGONSHARD = 113; +const int IP_MATERIAL_IRON_FLAMETOUCHED = 114; +const int IP_MATERIAL_LIVEWOOD = 115; +const int IP_MATERIAL_MOURNLODE_PURPLE = 116; +const int IP_MATERIAL_SOARWOOD = 117; +const int IP_MATERIAL_TARGATH = 118; +const int IP_MATERIAL_ASTRAL_DRIFTMETAL = 119; +const int IP_MATERIAL_ATANDUR = 120; +const int IP_MATERIAL_BLENDED_QUARTZ = 121; +const int IP_MATERIAL_CHITIN = 122; +const int IP_MATERIAL_DARKLEAF_ELVEN = 123; +const int IP_MATERIAL_DLARUN = 124; +const int IP_MATERIAL_DUSTWOOD = 125; +const int IP_MATERIAL_ELUKIAN_CLAY = 126; +const int IP_MATERIAL_ENTROPIUM = 127; +const int IP_MATERIAL_GREENSTEEL_BAATORIAN = 128; +const int IP_MATERIAL_HIZAGKUUR = 129; +const int IP_MATERIAL_IRON_FEVER = 130; +const int IP_MATERIAL_IRON_GEHENNAN_MORGHUTH = 131; +const int IP_MATERIAL_LEAFWEAVE = 132; +const int IP_MATERIAL_LIVING_METAL = 133; +const int IP_MATERIAL_MINDSTEEL_URDRUKAR = 134; +const int IP_MATERIAL_TRUESTEEL_SOLANIAN = 135; +const int IP_MATERIAL_WOOD_AGAFARI = 136; +const int IP_MATERIAL_CRYSTAL_DASL = 137; +const int IP_MATERIAL_DRAKE_IVORY = 138; +const int IP_MATERIAL_ROPE_GIANT_HAIR = 139; +const int IP_MATERIAL_OBSIDIAN = 140; +const int IP_MATERIAL_BAMBOO = 141; +const int IP_MATERIAL_POTTERY = 142; +const int IP_MATERIAL_GLASSTEEL = 143; +const int IP_NUM_MATERIALS = 143; + +const string IP_MATERIAL_NAME_INVALID = ""; +const string IP_MATERIAL_NAME_UNKNOWN = "Unknown"; +const string IP_MATERIAL_NAME_ADAMANTINE = "Adamantine"; +const string IP_MATERIAL_NAME_BRASS = "Brass"; +const string IP_MATERIAL_NAME_BRONZE = "Bronze"; +const string IP_MATERIAL_NAME_CARBON = "Carbon"; +const string IP_MATERIAL_NAME_COLD_IRON = "Cold Iron"; +const string IP_MATERIAL_NAME_COPPER = "Copper"; +const string IP_MATERIAL_NAME_DARKSTEEL = "Darksteel"; +const string IP_MATERIAL_NAME_GOLD = "Gold"; +const string IP_MATERIAL_NAME_IRON = "Iron"; +const string IP_MATERIAL_NAME_LEAD = "Lead"; +const string IP_MATERIAL_NAME_MITHRAL = "Mithral"; +const string IP_MATERIAL_NAME_PLATINUM = "Platinum"; +const string IP_MATERIAL_NAME_SILVER = "Silver"; +const string IP_MATERIAL_NAME_SILVER_ALCHEMICAL = "Alchemical Silver"; +const string IP_MATERIAL_NAME_STEEL = "Steel"; +const string IP_MATERIAL_NAME_BONE = "Bone"; +const string IP_MATERIAL_NAME_HIDE = "Hide"; +const string IP_MATERIAL_NAME_HIDE_SALAMANDER = "Salamander Hide"; +const string IP_MATERIAL_NAME_HIDE_UMBER_HULK = "Umber Hulk Hide"; +const string IP_MATERIAL_NAME_HIDE_WYVERN = "Wyvern Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_BLACK = "Black Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_BLUE = "Blue Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_BRASS = "Brass Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_BRONZE = "Bronze Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_COPPER = "Copper Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_GOLD = "Gold Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_GREEN = "Green Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_RED = "Red Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_SILVER = "Silver Dragon Hide"; +const string IP_MATERIAL_NAME_HIDE_DRAGON_WHITE = "White Dragon Hide"; +const string IP_MATERIAL_NAME_LEATHER = "Leather Hide"; +const string IP_MATERIAL_NAME_SCALE = "Scale"; +const string IP_MATERIAL_NAME_CLOTH = "Cloth"; +const string IP_MATERIAL_NAME_COTTON = "Cotton"; +const string IP_MATERIAL_NAME_SILK = "Silk"; +const string IP_MATERIAL_NAME_WOOL = "Wool"; +const string IP_MATERIAL_NAME_WOOD = "Wood"; +const string IP_MATERIAL_NAME_WOOD_IRONWOOD = "Ironwood"; +const string IP_MATERIAL_NAME_WOOD_DUSKWOOD = "Duskwood"; +const string IP_MATERIAL_NAME_WOOD_DARKWOOD_ZALANTAR = "Zalantar Darkwood"; +const string IP_MATERIAL_NAME_WOOD_ASH = "Ash"; +const string IP_MATERIAL_NAME_WOOD_YEW = "Yew"; +const string IP_MATERIAL_NAME_WOOD_OAK = "Oak"; +const string IP_MATERIAL_NAME_WOOD_PINE = "Pine"; +const string IP_MATERIAL_NAME_WOOD_CEDAR = "Cedar"; +const string IP_MATERIAL_NAME_ELEMENTAL = "Elemental"; +const string IP_MATERIAL_NAME_ELEMENTAL_AIR = "Air Elemental"; +const string IP_MATERIAL_NAME_ELEMENTAL_EARTH = "Earth Elemental"; +const string IP_MATERIAL_NAME_ELEMENTAL_FIRE = "Fire Elemental"; +const string IP_MATERIAL_NAME_ELEMENTAL_WATER = "Water Elemental"; +const string IP_MATERIAL_NAME_GEM = "Gem"; +const string IP_MATERIAL_NAME_GEM_ALEXANDRITE = "Alexandrite"; +const string IP_MATERIAL_NAME_GEM_AMETHYST = "Amethyst"; +const string IP_MATERIAL_NAME_GEM_AVENTURINE = "Aventurine"; +const string IP_MATERIAL_NAME_GEM_BELJURIL = "Beljuril"; +const string IP_MATERIAL_NAME_GEM_BLOODSTONE = "Bloodstone"; +const string IP_MATERIAL_NAME_GEM_BLUE_DIAMOND = "Blue Diamond"; +const string IP_MATERIAL_NAME_GEM_CANARY_DIAMOND = "Carary Diamond"; +const string IP_MATERIAL_NAME_GEM_DIAMOND = "Diamond"; +const string IP_MATERIAL_NAME_GEM_EMERALD = "Emerald"; +const string IP_MATERIAL_NAME_GEM_FIRE_AGATE = "Agate"; +const string IP_MATERIAL_NAME_GEM_FIRE_OPAL = "Opal"; +const string IP_MATERIAL_NAME_GEM_FLUORSPAR = "Fluorspar"; +const string IP_MATERIAL_NAME_GEM_GARNET = "Garnet"; +const string IP_MATERIAL_NAME_GEM_GREENSTONE = "Greenstone"; +const string IP_MATERIAL_NAME_GEM_JACINTH = "Jacinth"; +const string IP_MATERIAL_NAME_GEM_KINGS_TEAR = "King's Tear"; +const string IP_MATERIAL_NAME_GEM_MALACHITE = "Malachite"; +const string IP_MATERIAL_NAME_GEM_OBSIDIAN = "Obsidian"; +const string IP_MATERIAL_NAME_GEM_PHENALOPE = "Phenalope"; +const string IP_MATERIAL_NAME_GEM_ROGUE_STONE = "Rogue Stone"; +const string IP_MATERIAL_NAME_GEM_RUBY = "Ruby"; +const string IP_MATERIAL_NAME_GEM_SAPPHIRE = "Sapphire"; +const string IP_MATERIAL_NAME_GEM_STAR_SAPPHIRE = "Star Sapphire"; +const string IP_MATERIAL_NAME_GEM_TOPAZ = "Topaz"; +const string IP_MATERIAL_NAME_GEM_CRYSTAL_DEEP = "Deep Crystal"; +const string IP_MATERIAL_NAME_GEM_CRYSTAL_MUNDANE = "Mundane Crystal"; +const string IP_MATERIAL_NAME_PAPER = "Paper"; +const string IP_MATERIAL_NAME_GLASS = "Glass"; +const string IP_MATERIAL_NAME_ICE = "Ice"; +const string IP_MATERIAL_NAME_ROPE_HEMP = "Hemp Rope"; +const string IP_MATERIAL_NAME_STONE = "Stone"; +const string IP_MATERIAL_NAME_DEEP_CORAL = "Deep Coral"; +const string IP_MATERIAL_NAME_WOOD_LIVING = "Living Wood"; +const string IP_MATERIAL_NAME_OBDURIUM = "Obdurium"; +const string IP_MATERIAL_NAME_WOOD_BRONZE = "Bronze Wood"; +const string IP_MATERIAL_NAME_BYESHK = "Byeshk"; +const string IP_MATERIAL_NAME_CALOMEL = "Calomel"; +const string IP_MATERIAL_NAME_CRYSTEEL_RIEDRAN = "Riedran Crysteel"; +const string IP_MATERIAL_NAME_DENSEWOOD = "Densewood"; +const string IP_MATERIAL_NAME_DRAGONSHARD = "Dragonshard"; +const string IP_MATERIAL_NAME_IRON_FLAMETOUCHED = "Flametouched Iron"; +const string IP_MATERIAL_NAME_LIVEWOOD = "Livewood"; +const string IP_MATERIAL_NAME_MOURNLODE_PURPLE = "Purple Mournlode"; +const string IP_MATERIAL_NAME_SOARWOOD = "Soarwood"; +const string IP_MATERIAL_NAME_TARGATH = "Targath"; +const string IP_MATERIAL_NAME_ASTRAL_DRIFTMETAL = "Astral Driftmetal"; +const string IP_MATERIAL_NAME_ATANDUR = "Atandur"; +const string IP_MATERIAL_NAME_BLENDED_QUARTZ = "Blended Quartz"; +const string IP_MATERIAL_NAME_CHITIN = "Chitin"; +const string IP_MATERIAL_NAME_DARKLEAF_ELVEN = "Elven Darkleaf"; +const string IP_MATERIAL_NAME_DLARUN = "Dlarun"; +const string IP_MATERIAL_NAME_DUSTWOOD = "Dustwood"; +const string IP_MATERIAL_NAME_ELUKIAN_CLAY = "Elukian Clay"; +const string IP_MATERIAL_NAME_ENTROPIUM = "Entropium"; +const string IP_MATERIAL_NAME_GREENSTEEL_BAATORIAN = "Baatorian Greensteel"; +const string IP_MATERIAL_NAME_HIZAGKUUR = "Hizagkuur"; +const string IP_MATERIAL_NAME_IRON_FEVER = "Fever Iron"; +const string IP_MATERIAL_NAME_IRON_GEHENNAN_MORGHUTH = "Gehennan Morghuth Iron"; +const string IP_MATERIAL_NAME_LEAFWEAVE = "Leafweave"; +const string IP_MATERIAL_NAME_LIVING_METAL = "Living Metal"; +const string IP_MATERIAL_NAME_MINDSTEEL_URDRUKAR = "Urdrukar Mindsteel"; +const string IP_MATERIAL_NAME_TRUESTEEL_SOLANIAN = "Solanian Truesteel"; +const string IP_MATERIAL_NAME_WOOD_AGAFARI = "Agafari"; +const string IP_MATERIAL_NAME_CRYSTAL_DASL = "Dasl"; +const string IP_MATERIAL_NAME_DRAKE_IVORY = "Drake Ivory"; +const string IP_MATERIAL_NAME_ROPE_GIANT_HAIR = "Giant Hair Rope"; +const string IP_MATERIAL_NAME_OBSIDIAN = "Obsidian"; +const string IP_MATERIAL_NAME_BAMBOO = "Bamboo"; +const string IP_MATERIAL_NAME_POTTERY = "Pottery"; +const string IP_MATERIAL_NAME_GLASSTEEL = "Glassteel"; + +//:://///////////////////////////////////////////////////////////// +// GetMaterialName( int iMaterialType, int bLowerCase = FALSE) +// Given a material type this function returns its name as a string. +//:://///////////////////////////////////////////////////////////// +// Parameters: int iMaterialType - the material type number IP_MATERIAL_* +// int bLowerCase - if TRUE the returned string is all lower case +// if FALSE the returned string is first letter cap. +// +// Returns: the name of the material type as a string IP_MATERIAL_NAME_*. +// Returns IP_MATERIAL_NAME_INVALID if the material type is invalid. +//:://///////////////////////////////////////////////////////////// +string GetMaterialName( int iMaterialType, int bLowerCase = FALSE); +string GetMaterialName( int iMaterialType, int bLowerCase = FALSE) +{ if( iMaterialType == IP_MATERIAL_INVALID) return IP_MATERIAL_NAME_INVALID; + + string sName = ""; + switch( iMaterialType) + { case IP_MATERIAL_UNKNOWN: sName = IP_MATERIAL_NAME_UNKNOWN; break; + case IP_MATERIAL_ADAMANTINE: sName = IP_MATERIAL_NAME_ADAMANTINE; break; + case IP_MATERIAL_BRASS: sName = IP_MATERIAL_NAME_BRASS; break; + case IP_MATERIAL_BRONZE: sName = IP_MATERIAL_NAME_BRONZE; break; + case IP_MATERIAL_CARBON: sName = IP_MATERIAL_NAME_CARBON; break; + case IP_MATERIAL_COLD_IRON: sName = IP_MATERIAL_NAME_COLD_IRON; break; + case IP_MATERIAL_COPPER: sName = IP_MATERIAL_NAME_COPPER; break; + case IP_MATERIAL_DARKSTEEL: sName = IP_MATERIAL_NAME_DARKSTEEL; break; + case IP_MATERIAL_GOLD: sName = IP_MATERIAL_NAME_GOLD; break; + case IP_MATERIAL_IRON: sName = IP_MATERIAL_NAME_IRON; break; + case IP_MATERIAL_LEAD: sName = IP_MATERIAL_NAME_LEAD; break; + case IP_MATERIAL_MITHRAL: sName = IP_MATERIAL_NAME_MITHRAL; break; + case IP_MATERIAL_PLATINUM: sName = IP_MATERIAL_NAME_PLATINUM; break; + case IP_MATERIAL_SILVER: sName = IP_MATERIAL_NAME_SILVER; break; + case IP_MATERIAL_SILVER_ALCHEMICAL: sName = IP_MATERIAL_NAME_SILVER_ALCHEMICAL; break; + case IP_MATERIAL_STEEL: sName = IP_MATERIAL_NAME_STEEL; break; + case IP_MATERIAL_BONE: sName = IP_MATERIAL_NAME_BONE; break; + case IP_MATERIAL_HIDE: sName = IP_MATERIAL_NAME_HIDE; break; + case IP_MATERIAL_HIDE_SALAMANDER: sName = IP_MATERIAL_NAME_HIDE_SALAMANDER; break; + case IP_MATERIAL_HIDE_UMBER_HULK: sName = IP_MATERIAL_NAME_HIDE_UMBER_HULK; break; + case IP_MATERIAL_HIDE_WYVERN: sName = IP_MATERIAL_NAME_HIDE_WYVERN; break; + case IP_MATERIAL_HIDE_DRAGON_BLACK: sName = IP_MATERIAL_NAME_HIDE_DRAGON_BLACK; break; + case IP_MATERIAL_HIDE_DRAGON_BLUE: sName = IP_MATERIAL_NAME_HIDE_DRAGON_BLUE; break; + case IP_MATERIAL_HIDE_DRAGON_BRASS: sName = IP_MATERIAL_NAME_HIDE_DRAGON_BRASS; break; + case IP_MATERIAL_HIDE_DRAGON_BRONZE: sName = IP_MATERIAL_NAME_HIDE_DRAGON_BRONZE; break; + case IP_MATERIAL_HIDE_DRAGON_COPPER: sName = IP_MATERIAL_NAME_HIDE_DRAGON_COPPER; break; + case IP_MATERIAL_HIDE_DRAGON_GOLD: sName = IP_MATERIAL_NAME_HIDE_DRAGON_GOLD; break; + case IP_MATERIAL_HIDE_DRAGON_GREEN: sName = IP_MATERIAL_NAME_HIDE_DRAGON_GREEN; break; + case IP_MATERIAL_HIDE_DRAGON_RED: sName = IP_MATERIAL_NAME_HIDE_DRAGON_RED; break; + case IP_MATERIAL_HIDE_DRAGON_SILVER: sName = IP_MATERIAL_NAME_HIDE_DRAGON_SILVER; break; + case IP_MATERIAL_HIDE_DRAGON_WHITE: sName = IP_MATERIAL_NAME_HIDE_DRAGON_WHITE; break; + case IP_MATERIAL_LEATHER: sName = IP_MATERIAL_NAME_LEATHER; break; + case IP_MATERIAL_SCALE: sName = IP_MATERIAL_NAME_SCALE; break; + case IP_MATERIAL_COTTON: sName = IP_MATERIAL_NAME_COTTON; break; + case IP_MATERIAL_CLOTH: sName = IP_MATERIAL_NAME_CLOTH; break; + case IP_MATERIAL_SILK: sName = IP_MATERIAL_NAME_SILK; break; + case IP_MATERIAL_WOOL: sName = IP_MATERIAL_NAME_WOOL; break; + case IP_MATERIAL_WOOD: sName = IP_MATERIAL_NAME_WOOD; break; + case IP_MATERIAL_WOOD_IRONWOOD: sName = IP_MATERIAL_NAME_WOOD_IRONWOOD; break; + case IP_MATERIAL_WOOD_DUSKWOOD: sName = IP_MATERIAL_NAME_WOOD_DUSKWOOD; break; + case IP_MATERIAL_WOOD_DARKWOOD_ZALANTAR: sName = IP_MATERIAL_NAME_WOOD_DARKWOOD_ZALANTAR; break; + case IP_MATERIAL_WOOD_ASH: sName = IP_MATERIAL_NAME_WOOD_ASH; break; + case IP_MATERIAL_WOOD_YEW: sName = IP_MATERIAL_NAME_WOOD_YEW; break; + case IP_MATERIAL_WOOD_OAK: sName = IP_MATERIAL_NAME_WOOD_OAK; break; + case IP_MATERIAL_WOOD_PINE: sName = IP_MATERIAL_NAME_WOOD_PINE; break; + case IP_MATERIAL_WOOD_CEDAR: sName = IP_MATERIAL_NAME_WOOD_CEDAR; break; + case IP_MATERIAL_ELEMENTAL: sName = IP_MATERIAL_NAME_ELEMENTAL; break; + case IP_MATERIAL_ELEMENTAL_AIR: sName = IP_MATERIAL_NAME_ELEMENTAL_AIR; break; + case IP_MATERIAL_ELEMENTAL_EARTH: sName = IP_MATERIAL_NAME_ELEMENTAL_EARTH; break; + case IP_MATERIAL_ELEMENTAL_FIRE: sName = IP_MATERIAL_NAME_ELEMENTAL_FIRE; break; + case IP_MATERIAL_ELEMENTAL_WATER: sName = IP_MATERIAL_NAME_ELEMENTAL_WATER; break; + case IP_MATERIAL_GEM: sName = IP_MATERIAL_NAME_GEM; break; + case IP_MATERIAL_GEM_ALEXANDRITE: sName = IP_MATERIAL_NAME_GEM_ALEXANDRITE; break; + case IP_MATERIAL_GEM_AMETHYST: sName = IP_MATERIAL_NAME_GEM_AMETHYST; break; + case IP_MATERIAL_GEM_AVENTURINE: sName = IP_MATERIAL_NAME_GEM_AVENTURINE; break; + case IP_MATERIAL_GEM_BELJURIL: sName = IP_MATERIAL_NAME_GEM_BELJURIL; break; + case IP_MATERIAL_GEM_BLOODSTONE: sName = IP_MATERIAL_NAME_GEM_BLOODSTONE; break; + case IP_MATERIAL_GEM_BLUE_DIAMOND: sName = IP_MATERIAL_NAME_GEM_BLUE_DIAMOND; break; + case IP_MATERIAL_GEM_CANARY_DIAMOND: sName = IP_MATERIAL_NAME_GEM_CANARY_DIAMOND; break; + case IP_MATERIAL_GEM_DIAMOND: sName = IP_MATERIAL_NAME_GEM_DIAMOND; break; + case IP_MATERIAL_GEM_EMERALD: sName = IP_MATERIAL_NAME_GEM_EMERALD; break; + case IP_MATERIAL_GEM_FIRE_AGATE: sName = IP_MATERIAL_NAME_GEM_FIRE_AGATE; break; + case IP_MATERIAL_GEM_FIRE_OPAL: sName = IP_MATERIAL_NAME_GEM_FIRE_OPAL; break; + case IP_MATERIAL_GEM_FLUORSPAR: sName = IP_MATERIAL_NAME_GEM_FLUORSPAR; break; + case IP_MATERIAL_GEM_GARNET: sName = IP_MATERIAL_NAME_GEM_GARNET; break; + case IP_MATERIAL_GEM_GREENSTONE: sName = IP_MATERIAL_NAME_GEM_GREENSTONE; break; + case IP_MATERIAL_GEM_JACINTH: sName = IP_MATERIAL_NAME_GEM_JACINTH; break; + case IP_MATERIAL_GEM_KINGS_TEAR: sName = IP_MATERIAL_NAME_GEM_KINGS_TEAR; break; + case IP_MATERIAL_GEM_MALACHITE: sName = IP_MATERIAL_NAME_GEM_MALACHITE; break; + case IP_MATERIAL_GEM_OBSIDIAN: sName = IP_MATERIAL_NAME_GEM_OBSIDIAN; break; + case IP_MATERIAL_GEM_PHENALOPE: sName = IP_MATERIAL_NAME_GEM_PHENALOPE; break; + case IP_MATERIAL_GEM_ROGUE_STONE: sName = IP_MATERIAL_NAME_GEM_ROGUE_STONE; break; + case IP_MATERIAL_GEM_RUBY: sName = IP_MATERIAL_NAME_GEM_RUBY; break; + case IP_MATERIAL_GEM_SAPPHIRE: sName = IP_MATERIAL_NAME_GEM_SAPPHIRE; break; + case IP_MATERIAL_GEM_STAR_SAPPHIRE: sName = IP_MATERIAL_NAME_GEM_STAR_SAPPHIRE; break; + case IP_MATERIAL_GEM_TOPAZ: sName = IP_MATERIAL_NAME_GEM_TOPAZ; break; + case IP_MATERIAL_GEM_CRYSTAL_DEEP: sName = IP_MATERIAL_NAME_GEM_CRYSTAL_DEEP; break; + case IP_MATERIAL_GEM_CRYSTAL_MUNDANE: sName = IP_MATERIAL_NAME_GEM_CRYSTAL_MUNDANE; break; + case IP_MATERIAL_PAPER: sName = IP_MATERIAL_NAME_PAPER; break; + case IP_MATERIAL_GLASS: sName = IP_MATERIAL_NAME_GLASS; break; + case IP_MATERIAL_ICE: sName = IP_MATERIAL_NAME_ICE; break; + case IP_MATERIAL_ROPE_HEMP: sName = IP_MATERIAL_NAME_ROPE_HEMP; break; + case IP_MATERIAL_STONE: sName = IP_MATERIAL_NAME_STONE; break; + case IP_MATERIAL_DEEP_CORAL: sName = IP_MATERIAL_NAME_DEEP_CORAL; break; + case IP_MATERIAL_WOOD_LIVING: sName = IP_MATERIAL_NAME_WOOD_LIVING; break; + case IP_MATERIAL_OBDURIUM: sName = IP_MATERIAL_NAME_OBDURIUM; break; + case IP_MATERIAL_WOOD_BRONZE: sName = IP_MATERIAL_NAME_WOOD_BRONZE; break; + case IP_MATERIAL_BYESHK: sName = IP_MATERIAL_NAME_BYESHK; break; + case IP_MATERIAL_CALOMEL: sName = IP_MATERIAL_NAME_CALOMEL; break; + case IP_MATERIAL_CRYSTEEL_RIEDRAN: sName = IP_MATERIAL_NAME_CRYSTEEL_RIEDRAN; break; + case IP_MATERIAL_DENSEWOOD: sName = IP_MATERIAL_NAME_DENSEWOOD; break; + case IP_MATERIAL_DRAGONSHARD: sName = IP_MATERIAL_NAME_DRAGONSHARD; break; + case IP_MATERIAL_IRON_FLAMETOUCHED: sName = IP_MATERIAL_NAME_IRON_FLAMETOUCHED; break; + case IP_MATERIAL_LIVEWOOD: sName = IP_MATERIAL_NAME_LIVEWOOD; break; + case IP_MATERIAL_MOURNLODE_PURPLE: sName = IP_MATERIAL_NAME_MOURNLODE_PURPLE; break; + case IP_MATERIAL_SOARWOOD: sName = IP_MATERIAL_NAME_SOARWOOD; break; + case IP_MATERIAL_TARGATH: sName = IP_MATERIAL_NAME_TARGATH; break; + case IP_MATERIAL_ASTRAL_DRIFTMETAL: sName = IP_MATERIAL_NAME_ASTRAL_DRIFTMETAL; break; + case IP_MATERIAL_ATANDUR: sName = IP_MATERIAL_NAME_ATANDUR; break; + case IP_MATERIAL_BLENDED_QUARTZ: sName = IP_MATERIAL_NAME_BLENDED_QUARTZ; break; + case IP_MATERIAL_CHITIN: sName = IP_MATERIAL_NAME_CHITIN; break; + case IP_MATERIAL_DARKLEAF_ELVEN: sName = IP_MATERIAL_NAME_DARKLEAF_ELVEN; break; + case IP_MATERIAL_DLARUN: sName = IP_MATERIAL_NAME_DLARUN; break; + case IP_MATERIAL_DUSTWOOD: sName = IP_MATERIAL_NAME_DUSTWOOD; break; + case IP_MATERIAL_ELUKIAN_CLAY: sName = IP_MATERIAL_NAME_ELUKIAN_CLAY; break; + case IP_MATERIAL_ENTROPIUM: sName = IP_MATERIAL_NAME_ENTROPIUM; break; + case IP_MATERIAL_GREENSTEEL_BAATORIAN: sName = IP_MATERIAL_NAME_GREENSTEEL_BAATORIAN; break; + case IP_MATERIAL_HIZAGKUUR: sName = IP_MATERIAL_NAME_HIZAGKUUR; break; + case IP_MATERIAL_IRON_FEVER: sName = IP_MATERIAL_NAME_IRON_FEVER; break; + case IP_MATERIAL_IRON_GEHENNAN_MORGHUTH: sName = IP_MATERIAL_NAME_IRON_GEHENNAN_MORGHUTH; break; + case IP_MATERIAL_LEAFWEAVE: sName = IP_MATERIAL_NAME_LEAFWEAVE; break; + case IP_MATERIAL_LIVING_METAL: sName = IP_MATERIAL_NAME_LIVING_METAL; break; + case IP_MATERIAL_MINDSTEEL_URDRUKAR: sName = IP_MATERIAL_NAME_MINDSTEEL_URDRUKAR; break; + case IP_MATERIAL_TRUESTEEL_SOLANIAN: sName = IP_MATERIAL_NAME_TRUESTEEL_SOLANIAN; break; + case IP_MATERIAL_WOOD_AGAFARI: sName = IP_MATERIAL_NAME_WOOD_AGAFARI; break; + case IP_MATERIAL_CRYSTAL_DASL: sName = IP_MATERIAL_NAME_CRYSTAL_DASL; break; + case IP_MATERIAL_DRAKE_IVORY: sName = IP_MATERIAL_NAME_DRAKE_IVORY; break; + case IP_MATERIAL_ROPE_GIANT_HAIR: sName = IP_MATERIAL_NAME_ROPE_GIANT_HAIR; break; + case IP_MATERIAL_OBSIDIAN: sName = IP_MATERIAL_NAME_OBSIDIAN; break; + case IP_MATERIAL_BAMBOO: sName = IP_MATERIAL_NAME_BAMBOO; break; + case IP_MATERIAL_POTTERY: sName = IP_MATERIAL_NAME_POTTERY; break; + case IP_MATERIAL_GLASSTEEL: sName = IP_MATERIAL_NAME_GLASSTEEL; break; + + default: return ""; + } + + return (bLowerCase ? GetStringLowerCase( sName) : sName); +} + + +//:://///////////////////////////////////////////////////////////// +// int GetIPMaterial( string sMaterialName) +// Given a material name this function returns its type number (2da row) +//:://///////////////////////////////////////////////////////////// +// Parameters: string sMaterialName - the material name IP_MATERIAL_NAME_* +// +// Returns: the material type number IP_MATERIAL_* of the specified material +// name or IP_MATERIAL_INVALID if the type could not be determined +// from the specified name. +//:://///////////////////////////////////////////////////////////// +int GetIPMaterial( string sMaterialName); +int GetIPMaterial( string sMaterialName) +{ if( sMaterialName == IP_MATERIAL_NAME_INVALID) return IP_MATERIAL_INVALID; + + sMaterialName = GetStringUpperCase( sMaterialName); + if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_UNKNOWN)) return IP_MATERIAL_UNKNOWN; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_ADAMANTINE)) return IP_MATERIAL_ADAMANTINE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_BRASS)) return IP_MATERIAL_BRASS; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_BRONZE)) return IP_MATERIAL_BRONZE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_CARBON)) return IP_MATERIAL_CARBON; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_COLD_IRON)) return IP_MATERIAL_COLD_IRON; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_COPPER)) return IP_MATERIAL_COPPER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_DARKSTEEL)) return IP_MATERIAL_DARKSTEEL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GOLD)) return IP_MATERIAL_GOLD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_IRON)) return IP_MATERIAL_IRON; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_LEAD)) return IP_MATERIAL_LEAD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_MITHRAL)) return IP_MATERIAL_MITHRAL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_PLATINUM)) return IP_MATERIAL_PLATINUM; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_SILVER)) return IP_MATERIAL_SILVER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_SILVER_ALCHEMICAL)) return IP_MATERIAL_SILVER_ALCHEMICAL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_STEEL)) return IP_MATERIAL_STEEL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_BONE)) return IP_MATERIAL_BONE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE)) return IP_MATERIAL_HIDE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_SALAMANDER)) return IP_MATERIAL_HIDE_SALAMANDER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_UMBER_HULK)) return IP_MATERIAL_HIDE_UMBER_HULK; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_WYVERN)) return IP_MATERIAL_HIDE_WYVERN; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_BLACK)) return IP_MATERIAL_HIDE_DRAGON_BLACK; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_BLUE)) return IP_MATERIAL_HIDE_DRAGON_BLUE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_BRASS)) return IP_MATERIAL_HIDE_DRAGON_BRASS; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_BRONZE)) return IP_MATERIAL_HIDE_DRAGON_BRONZE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_COPPER)) return IP_MATERIAL_HIDE_DRAGON_COPPER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_GOLD)) return IP_MATERIAL_HIDE_DRAGON_GOLD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_GREEN)) return IP_MATERIAL_HIDE_DRAGON_GREEN; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_RED)) return IP_MATERIAL_HIDE_DRAGON_RED; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_SILVER)) return IP_MATERIAL_HIDE_DRAGON_SILVER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_HIDE_DRAGON_WHITE)) return IP_MATERIAL_HIDE_DRAGON_WHITE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_LEATHER)) return IP_MATERIAL_LEATHER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_SCALE)) return IP_MATERIAL_SCALE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_COTTON)) return IP_MATERIAL_COTTON; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_CLOTH)) return IP_MATERIAL_CLOTH; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_SILK)) return IP_MATERIAL_SILK; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOL)) return IP_MATERIAL_WOOL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD)) return IP_MATERIAL_WOOD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_IRONWOOD)) return IP_MATERIAL_WOOD_IRONWOOD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_DUSKWOOD)) return IP_MATERIAL_WOOD_DUSKWOOD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_DARKWOOD_ZALANTAR)) return IP_MATERIAL_WOOD_DARKWOOD_ZALANTAR; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_ASH)) return IP_MATERIAL_WOOD_ASH; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_YEW)) return IP_MATERIAL_WOOD_YEW; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_OAK)) return IP_MATERIAL_WOOD_OAK; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_PINE)) return IP_MATERIAL_WOOD_PINE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_WOOD_CEDAR)) return IP_MATERIAL_WOOD_CEDAR; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_ELEMENTAL)) return IP_MATERIAL_ELEMENTAL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_ELEMENTAL_AIR)) return IP_MATERIAL_ELEMENTAL_AIR; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_ELEMENTAL_EARTH)) return IP_MATERIAL_ELEMENTAL_EARTH; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_ELEMENTAL_FIRE)) return IP_MATERIAL_ELEMENTAL_FIRE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_ELEMENTAL_WATER)) return IP_MATERIAL_ELEMENTAL_WATER; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM)) return IP_MATERIAL_GEM; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_ALEXANDRITE)) return IP_MATERIAL_GEM_ALEXANDRITE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_AMETHYST)) return IP_MATERIAL_GEM_AMETHYST; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_AVENTURINE)) return IP_MATERIAL_GEM_AVENTURINE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_BELJURIL)) return IP_MATERIAL_GEM_BELJURIL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_BLOODSTONE)) return IP_MATERIAL_GEM_BLOODSTONE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_BLUE_DIAMOND)) return IP_MATERIAL_GEM_BLUE_DIAMOND; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_CANARY_DIAMOND)) return IP_MATERIAL_GEM_CANARY_DIAMOND; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_DIAMOND)) return IP_MATERIAL_GEM_DIAMOND; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_EMERALD)) return IP_MATERIAL_GEM_EMERALD; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_FIRE_AGATE)) return IP_MATERIAL_GEM_FIRE_AGATE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_FIRE_OPAL)) return IP_MATERIAL_GEM_FIRE_OPAL; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_FLUORSPAR)) return IP_MATERIAL_GEM_FLUORSPAR; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_GARNET)) return IP_MATERIAL_GEM_GARNET; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_GREENSTONE)) return IP_MATERIAL_GEM_GREENSTONE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_JACINTH)) return IP_MATERIAL_GEM_JACINTH; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_KINGS_TEAR)) return IP_MATERIAL_GEM_KINGS_TEAR; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_MALACHITE)) return IP_MATERIAL_GEM_MALACHITE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_OBSIDIAN)) return IP_MATERIAL_GEM_OBSIDIAN; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_PHENALOPE)) return IP_MATERIAL_GEM_PHENALOPE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_ROGUE_STONE)) return IP_MATERIAL_GEM_ROGUE_STONE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_RUBY)) return IP_MATERIAL_GEM_RUBY; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_SAPPHIRE)) return IP_MATERIAL_GEM_SAPPHIRE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_STAR_SAPPHIRE)) return IP_MATERIAL_GEM_STAR_SAPPHIRE; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_TOPAZ)) return IP_MATERIAL_GEM_TOPAZ; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_CRYSTAL_DEEP)) return IP_MATERIAL_GEM_CRYSTAL_DEEP; + else if( sMaterialName == GetStringUpperCase( IP_MATERIAL_NAME_GEM_CRYSTAL_MUNDANE)) return IP_MATERIAL_GEM_CRYSTAL_MUNDANE; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_PAPER)) return IP_MATERIAL_PAPER; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_GLASS)) return IP_MATERIAL_GLASS; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ICE)) return IP_MATERIAL_ICE; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ROPE_HEMP)) return IP_MATERIAL_ROPE_HEMP; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_STONE)) return IP_MATERIAL_STONE; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DEEP_CORAL)) return IP_MATERIAL_DEEP_CORAL; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_WOOD_LIVING)) return IP_MATERIAL_WOOD_LIVING; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_OBDURIUM)) return IP_MATERIAL_OBDURIUM; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_WOOD_BRONZE)) return IP_MATERIAL_WOOD_BRONZE; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_BYESHK)) return IP_MATERIAL_BYESHK; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_CALOMEL)) return IP_MATERIAL_CALOMEL; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_CRYSTEEL_RIEDRAN)) return IP_MATERIAL_CRYSTEEL_RIEDRAN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DENSEWOOD)) return IP_MATERIAL_DENSEWOOD; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DRAGONSHARD)) return IP_MATERIAL_DRAGONSHARD; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_IRON_FLAMETOUCHED)) return IP_MATERIAL_IRON_FLAMETOUCHED; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_LIVEWOOD)) return IP_MATERIAL_LIVEWOOD; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_MOURNLODE_PURPLE)) return IP_MATERIAL_MOURNLODE_PURPLE; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_SOARWOOD)) return IP_MATERIAL_SOARWOOD; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_TARGATH)) return IP_MATERIAL_TARGATH; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ASTRAL_DRIFTMETAL)) return IP_MATERIAL_ASTRAL_DRIFTMETAL; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ATANDUR)) return IP_MATERIAL_ATANDUR; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_BLENDED_QUARTZ)) return IP_MATERIAL_BLENDED_QUARTZ; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_CHITIN)) return IP_MATERIAL_CHITIN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DARKLEAF_ELVEN)) return IP_MATERIAL_DARKLEAF_ELVEN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DLARUN)) return IP_MATERIAL_DLARUN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DUSTWOOD)) return IP_MATERIAL_DUSTWOOD; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ELUKIAN_CLAY)) return IP_MATERIAL_ELUKIAN_CLAY; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ENTROPIUM)) return IP_MATERIAL_ENTROPIUM; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_GREENSTEEL_BAATORIAN)) return IP_MATERIAL_GREENSTEEL_BAATORIAN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_HIZAGKUUR)) return IP_MATERIAL_HIZAGKUUR; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_IRON_FEVER)) return IP_MATERIAL_IRON_FEVER; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_IRON_GEHENNAN_MORGHUTH)) return IP_MATERIAL_IRON_GEHENNAN_MORGHUTH; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_LEAFWEAVE)) return IP_MATERIAL_LEAFWEAVE; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_LIVING_METAL)) return IP_MATERIAL_LIVING_METAL; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_MINDSTEEL_URDRUKAR)) return IP_MATERIAL_MINDSTEEL_URDRUKAR; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_TRUESTEEL_SOLANIAN)) return IP_MATERIAL_TRUESTEEL_SOLANIAN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_WOOD_AGAFARI)) return IP_MATERIAL_WOOD_AGAFARI; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_CRYSTAL_DASL)) return IP_MATERIAL_CRYSTAL_DASL; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_DRAKE_IVORY)) return IP_MATERIAL_DRAKE_IVORY; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_ROPE_GIANT_HAIR)) return IP_MATERIAL_ROPE_GIANT_HAIR; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_OBSIDIAN)) return IP_MATERIAL_OBSIDIAN; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_BAMBOO)) return IP_MATERIAL_BAMBOO; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_POTTERY)) return IP_MATERIAL_POTTERY; + else if( sMaterialName == GetStringUpperCase(IP_MATERIAL_NAME_GLASSTEEL)) return IP_MATERIAL_GLASSTEEL; + + return IP_MATERIAL_INVALID; +} + + +//:://///////////////////////////////////////////////////////////// +// string IPGetMaterialName( itemproperty ipMaterial, int bLowerCase = FALSE) +// Given an itempropety this function returns the material name of the property +// as a string if it is a material property. If the specified itemproperty is +// not a material property it returns IP_MATERIAL_NAME_INVALID. +//:://///////////////////////////////////////////////////////////// +// Parameters: itemproperty ipMaterial - the itemproperty to check +// int bLowerCase - if TRUE the returned name is all lower case +// if FALSE the returned name is first letter cap +// +// Returns: the material name IP_MATERIAL_NAME_* of the material itemproperty +// or IP_MATERIAL_NAME_INVALID if the itemproperty is not a material +// itemproperty or is an invalid itemproperty. +//:://///////////////////////////////////////////////////////////// +string IPGetMaterialName( itemproperty ipMaterial, int bLowerCase = FALSE); +string IPGetMaterialName( itemproperty ipMaterial, int bLowerCase = FALSE) +{ + int iType = GetItemPropertyType( ipMaterial); + int iMaterial = GetItemPropertyCostTableValue( ipMaterial); + if( !GetIsItemPropertyValid( ipMaterial) || (iType != ITEM_PROPERTY_MATERIAL) || (iMaterial < IP_MATERIAL_UNKNOWN) || (iMaterial > IP_NUM_MATERIALS)) return IP_MATERIAL_NAME_INVALID; + + return GetMaterialName( iMaterial, bLowerCase); +} + + +//:://///////////////////////////////////////////////////////////// +// int IPGetMaterialType( itemproperty ipMaterial) +// Given an itempropety this function returns the material type of the property +// if it is a material itemproperty. If the specified itemproperty is invalid or +// not a material property it returns IP_MATERIAL_INVALID. +//:://///////////////////////////////////////////////////////////// +// Parameters: itemproperty ipMaterial - the itemproperty to check +// +// Returns: the material type IP_MATERIAL_* of the material itemproperty or +// IP_MATERIAL_INVALID if the itemproperty is not a material itemproperty +// or is an invalid itemproperty. +//:://///////////////////////////////////////////////////////////// +int IPGetMaterialType( itemproperty ipMaterial); +int IPGetMaterialType( itemproperty ipMaterial) +{ + int iType = GetItemPropertyType( ipMaterial); + int iMaterial = GetItemPropertyCostTableValue( ipMaterial); + if( !GetIsItemPropertyValid( ipMaterial) || (iType != ITEM_PROPERTY_MATERIAL) || (iMaterial < IP_MATERIAL_UNKNOWN) || (iMaterial > IP_NUM_MATERIALS)) return IP_MATERIAL_INVALID; + return (((iMaterial > IP_MATERIAL_INVALID) && (iMaterial <= IP_NUM_MATERIALS)) ? iMaterial : IP_MATERIAL_INVALID); +} + + +//:://///////////////////////////////////////////////////////////// +// itemproperty ItemPropertyMaterialByName( string sMaterialName) +// Given a valid material name this function returns a new material itemproperty of +// that type or and invalid itemproperty if the material name is not recognized. +//:://///////////////////////////////////////////////////////////// +// Parameters: string sMaterialName - the material name IP_MATERIAL_NAME_* +// +// Returns: a material itemproperty or an invalid itempropery if the specified +// material name is unrecognized. +//:://///////////////////////////////////////////////////////////// +itemproperty ItemPropertyMaterialByName( string sMaterialName); +itemproperty ItemPropertyMaterialByName( string sMaterialName) +{ + return ItemPropertyMaterial( GetIPMaterial( sMaterialName)); +} + + +//:://///////////////////////////////////////////////////////////// +// void IPAddMaterialProperty( int iMaterialType, int nDurationType, object oItem, float fDuration = 0.0f) +// Adds a material itempropery specified by material type to an item for a +// given duration type and duration. +//:://///////////////////////////////////////////////////////////// +// Parameters: int iMaterialType - the material type IP_MATERIAL_* +// int nDurationType - the duration type +// DURATION_TYPE_TEMPORARY, must also supply a duration +// DURATION_TYPE_PERMANENT, duration is ignored +// object oItem - the item to add the itempropery to. +// float fDuration - the duration in seconds that the itemproperty +// will stay on the item before being automatically +// removed. Ignored if duration type is permanent. +// Default = 0.0 seconds. +// +// Returns: none. Note this does not check to see if the material property already +// exists on the item and can add duplicate material properties to it. +//:://///////////////////////////////////////////////////////////// +void IPAddMaterialProperty( int iMaterialType, int nDurationType, object oItem, float fDuration = 0.0f); +void IPAddMaterialProperty( int iMaterialType, int nDurationType, object oItem, float fDuration = 0.0f) +{ + itemproperty ipMaterial = ItemPropertyMaterial( iMaterialType); + if( GetIsItemPropertyValid( ipMaterial)) AddItemProperty( nDurationType, ipMaterial, oItem, fDuration); +} + + +//:://///////////////////////////////////////////////////////////// +// void IPAddMaterialPropertyByName( string sMaterialName, int nDurationType, object oItem, float fDuration = 0.0f) +// Adds a material itempropery specified by material name to an item for a +// given duration type and duration. +//:://///////////////////////////////////////////////////////////// +// Parameters: string sMaterialName - the material name IP_MATERIAL_NAME_* +// int nDurationType - the duration type +// DURATION_TYPE_TEMPORARY, must also supply a duration +// DURATION_TYPE_PERMANENT, duration is ignored +// object oItem - the item to add the itempropery to. +// float fDuration - the duration in seconds that the itemproperty +// will stay on the item before being automatically +// removed. Ignored if duration type is permanent. +// Default = 0.0 seconds. +// +// Returns: none. Note this does not check to see if the material property already +// exists on the item and can add duplicate material properties to it. +//:://///////////////////////////////////////////////////////////// +void IPAddMaterialPropertyByName( string sMaterialName, int nDurationType, object oItem, float fDuration = 0.0f); +void IPAddMaterialPropertyByName( string sMaterialName, int nDurationType, object oItem, float fDuration = 0.0f) +{ + IPAddMaterialProperty( GetIPMaterial( sMaterialName), nDurationType, oItem, fDuration); +} + + +//:://///////////////////////////////////////////////////////////// +// void IPSafeAddMaterialProperty( int iMaterialType, object oItem, float fDuration = 0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE) +// Adds a material itempropery specified by material type to an item for a +// given duration. Checks to see if a material itemproperty of the same type +// already exists on the item and adds the new one based on the add/drop policy +// and ignore parameters specified. +//:://///////////////////////////////////////////////////////////// +// Parameters: int iMaterialType - the material type IP_MATERIAL_* +// object oItem - the item to add the itempropery to. +// float fDuration - 0.0 for permanent, anything else is temporary +// Default = 0.0 +// int nAddItemPropertyPolicy - the add/drop policy to use X2_IP_ADDPROP_POLICY_* +// Default = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING +// int bIgnoreDurationType - TRUE or FALSE to ignore existing itemproperty duration types. +// Default = FALSE +// int bIgnoreSubType - TRUE or FALSE to ignore existing itemproperty subtypes +// Default = FALSE +// +// Returns: none. +//:://///////////////////////////////////////////////////////////// +void IPSafeAddMaterialProperty( int iMaterialType, object oItem, float fDuration = 0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE); +void IPSafeAddMaterialProperty( int iMaterialType, object oItem, float fDuration = 0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE) +{ + itemproperty ipMaterial = ItemPropertyMaterial( iMaterialType); + if( GetIsItemPropertyValid( ipMaterial)) IPSafeAddItemProperty( oItem, ipMaterial, fDuration, nAddItemPropertyPolicy, bIgnoreDurationType, bIgnoreSubType); +} + + +//:://///////////////////////////////////////////////////////////// +// void IPSafeAddMaterialPropertyByName( string sMaterialName, object oItem, float fDuration = 0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE) +// Adds a material itempropery specified by material name to an item for a +// given duration. Checks to see if a material itemproperty of the same type +// already exists on the item and adds the new one based on the add/drop policy +// and ignore parameters specified. +//:://///////////////////////////////////////////////////////////// +// Parameters: string sMaterialName - the material name IP_MATERIAL_NAME_* +// object oItem - the item to add the itempropery to. +// float fDuration - 0.0 for permanent, anything else is temporary +// Default = 0.0 +// int nAddItemPropertyPolicy - the add/drop policy to use X2_IP_ADDPROP_POLICY_* +// Default = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING +// int bIgnoreDurationType - TRUE or FALSE to ignore existing itemproperty duration types. +// Default = FALSE +// int bIgnoreSubType - TRUE or FALSE to ignore existing itemproperty subtypes +// Default = FALSE +// +// Returns: none. +//:://///////////////////////////////////////////////////////////// +void IPSafeAddMaterialPropertyByName( string sMaterialName, object oItem, float fDuration = 0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE); +void IPSafeAddMaterialPropertyByName( string sMaterialName, object oItem, float fDuration = 0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE) +{ + IPSafeAddMaterialProperty( GetIPMaterial( sMaterialName), oItem, fDuration, nAddItemPropertyPolicy, bIgnoreDurationType, bIgnoreSubType); +} + + +//:://///////////////////////////////////////////////////////////// +// int GetItemHasMaterial( object oItem, int iMaterialType) +// Given a valid item and material type, this function returns TRUE if the +// item has an itemproperty of the specified type on it. +//:://///////////////////////////////////////////////////////////// +// Parameters: object oItem - the item to check. +// int iMaterialType - the material type to check for IP_MATERIAL_* +// +// Returns: TRUE if the item is a valid item and has a material itemproperty of +// the specified type on it. FALSE otherwise. +//:://///////////////////////////////////////////////////////////// +int GetItemHasMaterial( object oItem, int iMaterialType); +int GetItemHasMaterial( object oItem, int iMaterialType) +{ + if( !GetIsObjectValid( oItem) || (GetObjectType( oItem) != OBJECT_TYPE_ITEM) || + (iMaterialType <= IP_MATERIAL_INVALID) || (iMaterialType > IP_NUM_MATERIALS)) return FALSE; + + itemproperty ipMaterial = GetFirstItemProperty( oItem); + while( GetIsItemPropertyValid( ipMaterial)) + { + if( IPGetMaterialType( ipMaterial) == iMaterialType) return TRUE; + ipMaterial = GetNextItemProperty( oItem); + } + + return FALSE; +} + + +//:://///////////////////////////////////////////////////////////// +// int GetItemHasMaterialByName( object oItem, string sMaterialName) +// Given a valid item and material name, this function returns TRUE if the +// item has an itemproperty of the specified type on it. +//:://///////////////////////////////////////////////////////////// +// Parameters: object oItem - the item to check. +// string sMaterialName - the material Name to check for IP_MATERIAL_NAME_* +// +// Returns: TRUE if the item is a valid item and has a material itemproperty of +// the specified type on it. FALSE otherwise. +//:://///////////////////////////////////////////////////////////// +int GetItemHasMaterialByName( object oItem, string sMaterialName); +int GetItemHasMaterialByName( object oItem, string sMaterialName) +{ + return GetItemHasMaterial( oItem, GetIPMaterial( sMaterialName)); +} + + //:: Returns the general type of material nMaterial is. +int GetMaterialType(int nMaterial); +int GetMaterialType(int nMaterial) +{ + if ( nMaterial == IP_MATERIAL_INVALID ) + return MATERIAL_TYPE_INVALID; + + else if ( nMaterial == IP_MATERIAL_BONE + || nMaterial == IP_MATERIAL_SCALE + || nMaterial == IP_MATERIAL_CHITIN + || nMaterial == IP_MATERIAL_DRAKE_IVORY ) + return MATERIAL_TYPE_BONE; + + else if ( nMaterial == IP_MATERIAL_ELUKIAN_CLAY + || nMaterial == IP_MATERIAL_POTTERY ) + return MATERIAL_TYPE_CERAMIC; + + else if ( nMaterial == IP_MATERIAL_CLOTH + || nMaterial == IP_MATERIAL_COTTON + || nMaterial == IP_MATERIAL_SILK + || nMaterial == IP_MATERIAL_WOOL ) + return MATERIAL_TYPE_FABRIC; + + else if ( nMaterial == IP_MATERIAL_GEM + || nMaterial == IP_MATERIAL_GEM_ALEXANDRITE + || nMaterial == IP_MATERIAL_GEM_AMETHYST + || nMaterial == IP_MATERIAL_GEM_AVENTURINE + || nMaterial == IP_MATERIAL_GEM_BELJURIL + || nMaterial == IP_MATERIAL_GEM_BLOODSTONE + || nMaterial == IP_MATERIAL_GEM_BLUE_DIAMOND + || nMaterial == IP_MATERIAL_GEM_CANARY_DIAMOND + || nMaterial == IP_MATERIAL_GEM_DIAMOND + || nMaterial == IP_MATERIAL_GEM_EMERALD + || nMaterial == IP_MATERIAL_GEM_FIRE_AGATE + || nMaterial == IP_MATERIAL_GEM_FIRE_OPAL + || nMaterial == IP_MATERIAL_GEM_FLUORSPAR + || nMaterial == IP_MATERIAL_GEM_GARNET + || nMaterial == IP_MATERIAL_GEM_GREENSTONE + || nMaterial == IP_MATERIAL_GEM_JACINTH + || nMaterial == IP_MATERIAL_GEM_KINGS_TEAR + || nMaterial == IP_MATERIAL_GEM_MALACHITE + || nMaterial == IP_MATERIAL_GEM_OBSIDIAN + || nMaterial == IP_MATERIAL_GEM_PHENALOPE + || nMaterial == IP_MATERIAL_GEM_ROGUE_STONE + || nMaterial == IP_MATERIAL_GEM_RUBY + || nMaterial == IP_MATERIAL_GEM_SAPPHIRE + || nMaterial == IP_MATERIAL_GEM_STAR_SAPPHIRE + || nMaterial == IP_MATERIAL_GEM_TOPAZ + || nMaterial == IP_MATERIAL_GEM_CRYSTAL_DEEP + || nMaterial == IP_MATERIAL_GEM_CRYSTAL_MUNDANE + || nMaterial == IP_MATERIAL_GLASS + || nMaterial == IP_MATERIAL_ICE + || nMaterial == IP_MATERIAL_CRYSTAL_DASL + || nMaterial == IP_MATERIAL_OBSIDIAN + || nMaterial == IP_MATERIAL_GLASSTEEL) + return MATERIAL_TYPE_CRYSTAL; + + else if ( nMaterial == IP_MATERIAL_HIDE + || nMaterial == IP_MATERIAL_HIDE_SALAMANDER + || nMaterial == IP_MATERIAL_HIDE_UMBER_HULK + || nMaterial == IP_MATERIAL_HIDE_WYVERN + || nMaterial == IP_MATERIAL_HIDE_DRAGON_BLACK + || nMaterial == IP_MATERIAL_HIDE_DRAGON_BLUE + || nMaterial == IP_MATERIAL_HIDE_DRAGON_BRASS + || nMaterial == IP_MATERIAL_HIDE_DRAGON_BRONZE + || nMaterial == IP_MATERIAL_HIDE_DRAGON_COPPER + || nMaterial == IP_MATERIAL_HIDE_DRAGON_GOLD + || nMaterial == IP_MATERIAL_HIDE_DRAGON_GREEN + || nMaterial == IP_MATERIAL_HIDE_DRAGON_RED + || nMaterial == IP_MATERIAL_HIDE_DRAGON_SILVER + || nMaterial == IP_MATERIAL_HIDE_DRAGON_WHITE + || nMaterial == IP_MATERIAL_LEATHER + || nMaterial == IP_MATERIAL_LEAFWEAVE ) + return MATERIAL_TYPE_LEATHER; + + else if ( nMaterial == IP_MATERIAL_ADAMANTINE + || nMaterial == IP_MATERIAL_BRASS + || nMaterial == IP_MATERIAL_COLD_IRON + || nMaterial == IP_MATERIAL_DARKSTEEL + || nMaterial == IP_MATERIAL_MITHRAL + || nMaterial == IP_MATERIAL_SILVER_ALCHEMICAL + || nMaterial == IP_MATERIAL_STEEL + || nMaterial == IP_MATERIAL_BYESHK + || nMaterial == IP_MATERIAL_CALOMEL + || nMaterial == IP_MATERIAL_CRYSTEEL_RIEDRAN + || nMaterial == IP_MATERIAL_IRON_FLAMETOUCHED + || nMaterial == IP_MATERIAL_MOURNLODE_PURPLE + || nMaterial == IP_MATERIAL_TARGATH + || nMaterial == IP_MATERIAL_ASTRAL_DRIFTMETAL + || nMaterial == IP_MATERIAL_ATANDUR + || nMaterial == IP_MATERIAL_BLENDED_QUARTZ + || nMaterial == IP_MATERIAL_DLARUN + || nMaterial == IP_MATERIAL_ENTROPIUM + || nMaterial == IP_MATERIAL_GREENSTEEL_BAATORIAN + || nMaterial == IP_MATERIAL_HIZAGKUUR + || nMaterial == IP_MATERIAL_IRON_FEVER + || nMaterial == IP_MATERIAL_IRON_GEHENNAN_MORGHUTH + || nMaterial == IP_MATERIAL_LIVING_METAL + || nMaterial == IP_MATERIAL_MINDSTEEL_URDRUKAR + || nMaterial == IP_MATERIAL_TRUESTEEL_SOLANIAN + || nMaterial == IP_MATERIAL_BRONZE + || nMaterial == IP_MATERIAL_COPPER + || nMaterial == IP_MATERIAL_GOLD + || nMaterial == IP_MATERIAL_IRON + || nMaterial == IP_MATERIAL_LEAD + || nMaterial == IP_MATERIAL_PLATINUM + || nMaterial == IP_MATERIAL_SILVER + || nMaterial == IP_MATERIAL_OBDURIUM ) + return MATERIAL_TYPE_METAL; + + else if ( nMaterial == IP_MATERIAL_PAPER ) + return MATERIAL_TYPE_PAPER; + + else if ( nMaterial == IP_MATERIAL_ROPE_HEMP + || nMaterial == IP_MATERIAL_ROPE_GIANT_HAIR ) + return MATERIAL_TYPE_ROPE; + + else if ( nMaterial == IP_MATERIAL_CARBON + || nMaterial == IP_MATERIAL_ELEMENTAL + || nMaterial == IP_MATERIAL_ELEMENTAL_AIR + || nMaterial == IP_MATERIAL_ELEMENTAL_EARTH + || nMaterial == IP_MATERIAL_ELEMENTAL_FIRE + || nMaterial == IP_MATERIAL_ELEMENTAL_WATER + || nMaterial == IP_MATERIAL_STONE + || nMaterial == IP_MATERIAL_DEEP_CORAL + || nMaterial == IP_MATERIAL_DRAGONSHARD ) + return MATERIAL_TYPE_STONE; + + else if ( nMaterial == IP_MATERIAL_WOOD + || nMaterial == IP_MATERIAL_WOOD_IRONWOOD + || nMaterial == IP_MATERIAL_WOOD_DUSKWOOD + || nMaterial == IP_MATERIAL_WOOD_DARKWOOD_ZALANTAR + || nMaterial == IP_MATERIAL_WOOD_ASH + || nMaterial == IP_MATERIAL_WOOD_YEW + || nMaterial == IP_MATERIAL_WOOD_OAK + || nMaterial == IP_MATERIAL_WOOD_PINE + || nMaterial == IP_MATERIAL_WOOD_CEDAR + || nMaterial == IP_MATERIAL_WOOD_LIVING + || nMaterial == IP_MATERIAL_WOOD_BRONZE + || nMaterial == IP_MATERIAL_DENSEWOOD + || nMaterial == IP_MATERIAL_LIVEWOOD + || nMaterial == IP_MATERIAL_SOARWOOD + || nMaterial == IP_MATERIAL_DARKLEAF_ELVEN + || nMaterial == IP_MATERIAL_DUSTWOOD + || nMaterial == IP_MATERIAL_WOOD_AGAFARI + || nMaterial == IP_MATERIAL_BAMBOO ) + return MATERIAL_TYPE_WOOD; + + else { return MATERIAL_TYPE_UNKNOWN; } +} + + //:: Returns the name of nMaterialType as a string. +string GetMaterialTypeName(int nMaterialType, int bLowerCase = FALSE); +string GetMaterialTypeName(int nMaterialType, int bLowerCase = FALSE) +{ + string sName; + + sName = Get2DACache("prc_materialtype", "Name", nMaterialType); + + if (sName == "") + sName = "Invalid"; + + return (bLowerCase ? GetStringLowerCase( sName) : sName); +} + +//:: Returns the Hardness of a material. +int GetIPMaterialHardness(int nMaterial); +int GetIPMaterialHardness(int nMaterial) +{ + int nHardness; + nHardness = StringToInt(Get2DACache("prc_material", "Hardness", nMaterial)); + + if (nHardness < 0) + nHardness = 1000; + + return nHardness; +} + +//:: Returns the Hit Points Per Inch of a material. +int GetIPMaterialHitPointMod(int nMaterial); +int GetIPMaterialHitPointMod(int nMaterial) +{ + int nHPMod; + nHPMod = StringToInt(Get2DACache("prc_material", "HitPointsPerInch", nMaterial)); + + if (nHPMod < 1) + nHPMod = 1000; + + return nHPMod; +} + + + +//:: void main(){} diff --git a/src/include/prc_inc_nat_hb.nss b/src/include/prc_inc_nat_hb.nss new file mode 100644 index 0000000..c8f7c27 --- /dev/null +++ b/src/include/prc_inc_nat_hb.nss @@ -0,0 +1,470 @@ +void DoNaturalWeaponHB(object oPC = OBJECT_SELF); + +#include "prc_inc_combat" +#include "prc_inc_template" + +object GetProperTarget(object oPC, object oTarget) +{ + location lTarget = GetLocation(oPC); + // Use the function to get the closest creature as a target + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget)) + { + if(oAreaTarget != oPC && // Not you. + GetIsEnemy(oPC, oAreaTarget) && // Enemies only, please + GetIsInMeleeRange(oPC, oAreaTarget)) // They must be in melee range + { + return oAreaTarget; + } + //Select the next target within the spell shape. + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + + return oTarget; +} + +void DoNaturalAttack(object oWeapon) +{ + //if weapon is not valid, abort + if(!GetIsObjectValid(oWeapon)) + return; + + // object oTarget = GetAttackTarget(); + object oTarget = GetAttemptedAttackTarget(); + + // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant + + /* + //no point attacking plot + if(GetPlotFlag(oTarget)) + return; + */ + if (DEBUG) DoDebug("DoNaturalAttack oTarget1 "+GetName(oTarget)); + object oPC = OBJECT_SELF; + + // Make sure you don't hit yourself. Some idiot didn't check that. + if (GetIsFriend(oPC, oTarget) || oPC == oTarget) + oTarget = GetProperTarget(oPC, oTarget); + + if (DEBUG) DoDebug("DoNaturalAttack oTarget2 "+GetName(oTarget)); + + // if not melee/ranged fighting, abort + if (!GetIsObjectValid(oTarget)) + return; + + // natural attacks are (usually) not ranged attacks, so PerformAttack() will move to a target, if it is out of melee range + // However, we do not want to run to the target if we are in the midst of doing ranged attacks, e.g. if we have a ranged + // weapon equipped (and are still out of melee range, so we can effectively do ranged attacks) + // so check for a ranged weapon in the right hand slot and abort if we have one and are out of melee range + object oWeaponR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if(GetIsObjectValid(oWeaponR) + && GetIsRangedWeaponType(GetBaseItemType(oWeaponR)) + && !GetIsInMeleeRange(oTarget, oPC)) + return; + /* + //if not fighting, abort + // motu99: PRCGetIsFighting() not only checks. whether GetAttemptedAttackTarget() (relevant for melee attacks) returns a valid object + // it also checks whether we have attempted to attack a valid target with a spell + // but spell attacks don't make sense for natural attacks (which are pure melee), so commented out + if(!PRCGetIsFighting(oPC)) + { + DoDebug(COLOR_WHITE + "DoNaturalAttack(): not fighting any more - aborting"); + return; + } + */ + + //null effect + effect eInvalid; + string sMessageSuccess; + string sMessageFailure; + if(DEBUG) + { + sMessageSuccess += GetName(oWeapon); + //add attack + sMessageSuccess += " natural attack"; + //copy it to failure + sMessageFailure = sMessageSuccess; + //add hit/miss + sMessageSuccess += " hit"; + sMessageFailure += " miss"; + //add stars around messages + sMessageSuccess = "*"+sMessageSuccess+"*"; + sMessageFailure = "*"+sMessageFailure+"*"; + } + + //secondary attacks are -5 to hit + int nAttackMod = -5; + if (GetHasTemplate(TEMPLATE_GRAVETOUCHED_GHOUL, oPC)) nAttackMod = -2; // This is a standin for multiattack + /* + //check for (Improved) Multiattack + if(GetHasFeat(FEAT_IMPROVED_MULTIATTACK, oPC)) + nAttackMod = 0; + else if(GetHasFeat(FEAT_MULTIATTACK, oPC)) + nAttackMod = -2; + */ + //secondary attacks are half strength (we use offhand for this) + + // set the prc combat mode + int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; + + int nEssentia = GetEssentiaInvested(oPC, MELD_GIRALLON_ARMS); + if (nEssentia) IPSafeAddItemProperty(oWeapon, ItemPropertyEnhancementBonus(nEssentia), 9999.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE); + int nStrHalf = GetAbilityModifier(ABILITY_STRENGTH, oPC)/2; + nStrHalf = nStrHalf * -1; + + if(DEBUG) DoDebug(PRC_TEXT_WHITE + "initiating a secondary natural attack with "+GetName(oWeapon)+", attack mod " + IntToString(nAttackMod) + ", nStrHalf " + IntToString(nStrHalf)); + + PerformAttack(oTarget, + oPC, // + eInvalid, //effect eSpecialEffect, + 0.0, //float eDuration = 0.0 + nAttackMod, //int iAttackBonusMod = 0 + nStrHalf, //int iDamageModifier = Half strength + DAMAGE_TYPE_SLASHING, //int iDamageType = DAMAGE_TYPE_SLASHING, otherwise it uses magical damage. + sMessageSuccess, //sMessageSuccess + sMessageFailure, //sMessageFailure + FALSE, //int iTouchAttackType = FALSE + oWeaponR, // we should have something in the right hand (might need it for some calculations) + oWeapon, // we put the creature weapon in the left hand slot + FALSE, //offhand override (for half strength) + bCombatMode // prc scripted combat mode + ); +} + +void DoOffhandAttack(int nAttackMod) +{ + object oPC = OBJECT_SELF; + object oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + + + // check for offhand or double sided weapon, if not - return + if (!GetIsOffhandWeapon(oWeapon)) + { + object oWeaponR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (!GetIsDoubleSidedWeapon(oWeaponR)) + return; + oWeapon = oWeaponR; + } + + // object oTarget = GetAttackTarget(); + object oTarget = GetAttemptedAttackTarget(); + + // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant + /* + //no point attacking plot + if(GetPlotFlag(oTarget)) + return; + */ + // Make sure you don't hit yourself. Some idiot didn't check that. + if (GetIsFriend(oPC, oTarget) || oPC == oTarget) + oTarget = GetProperTarget(oPC, oTarget); + + // if not melee fighting, abort + if (!GetIsObjectValid(oTarget)) + return; + + /* + //if not fighting, abort + if(!PRCGetIsFighting(oPC)) + { + DoDebug(COLOR_WHITE + "DoOffhandAttack(): not fighting any more - aborting"); + return; + } + */ + string sMessageSuccess; + string sMessageFailure; + if (DEBUG) + { + sMessageSuccess += GetName(oWeapon); + //add attack + sMessageSuccess += " scripted offhand"; + //copy it to failure + sMessageFailure = sMessageSuccess; + //add hit/miss + sMessageSuccess += " hit"; + sMessageFailure += " miss"; + //add stars around messages + sMessageSuccess = "*"+sMessageSuccess+"*"; + sMessageFailure = "*"+sMessageFailure+"*"; + } + + //null effect + effect eInvalid; + + // set the prc combat mode + int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; + + if (DEBUG) DoDebug(PRC_TEXT_WHITE + "initiating an overflow offhand attack with "+GetName(oWeapon)+" and attack mod "+IntToString(nAttackMod)); + + PerformAttack(oTarget, + oPC, // + eInvalid, //effect eSpecialEffect, + 0.0, //float eDuration = 0.0 + nAttackMod, //int iAttackBonusMod = 0 + 0, //int iDamageModifier = 0 + DAMAGE_TYPE_SLASHING, //int iDamageType = DAMAGE_TYPE_SLASHING, otherwise it uses magical damage. + sMessageSuccess, //sMessageSuccess, //string sMessageSuccess = "" + sMessageFailure, //sMessageFailure, //string sMessageFailure = "" + FALSE, //int iTouchAttackType = FALSE + oWeapon, //object oRightHandOverride = OBJECT_INVALID, + oWeapon, //object oLeftHandOverride = OBJECT_INVALID, + 1, // offhand attack + bCombatMode // prc combat mode + ); +} + +void DoOverflowOnhandAttack(int nAttackMod) +{ + object oPC = OBJECT_SELF; + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + + //if weapon is not valid, abort + if(!GetIsObjectValid(oWeapon)) + return; + + // object oTarget = GetAttackTarget(); + object oTarget = GetAttemptedAttackTarget(); + + // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant + /* + //no point attacking plot + if(GetPlotFlag(oTarget)) + return; + */ + // Make sure you don't hit yourself. Some idiot didn't check that. + if (GetIsFriend(oPC, oTarget) || oPC == oTarget) + oTarget = GetProperTarget(oPC, oTarget); + + // if not melee fighting, abort + if (!GetIsObjectValid(oTarget)) + return; + + /* + //if not fighting, abort + if(!PRCGetIsFighting(oPC)) + { + DoDebug(COLOR_WHITE + "DoOverflowOnhandAttack(): not fighting any more - aborting"); + return; + } + */ + string sMessageSuccess; + string sMessageFailure; + if (DEBUG) + { + sMessageSuccess += GetName(oWeapon); + //add attack + sMessageSuccess += " scripted overflow"; + //copy it to failure + sMessageFailure = sMessageSuccess; + //add hit/miss + sMessageSuccess += " hit"; + sMessageFailure += " miss"; + //add stars around messages + sMessageSuccess = "*"+sMessageSuccess+"*"; + sMessageFailure = "*"+sMessageFailure+"*"; + } + + //null effect + effect eInvalid; + + // set the prc combat mode + int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; + + if (DEBUG) DoDebug(PRC_TEXT_WHITE+"initiating an overflow onhand attack with "+GetName(oWeapon)+" and attack mod "+IntToString(nAttackMod)); + + PerformAttack(oTarget, + oPC, // + eInvalid, //effect eSpecialEffect, + 0.0, //float eDuration = 0.0 + nAttackMod, //int iAttackBonusMod = 0 + 0, //int iDamageModifier = 0 + DAMAGE_TYPE_SLASHING, //int iDamageType = DAMAGE_TYPE_SLASHING, otherwise it uses magical damage. + sMessageSuccess,//sMessageSuccess, //string sMessageSuccess = "" + sMessageFailure,//sMessageFailure, //string sMessageFailure = "" + FALSE, //int iTouchAttackType = FALSE + oWeapon, //object oRightHandOverride = OBJECT_INVALID, + OBJECT_INVALID, //object oLeftHandOverride = OBJECT_INVALID + 0, + bCombatMode + ); +} + +void DoNaturalWeaponHB(object oPC = OBJECT_SELF) +{ + //not in combat, abort + if(!GetIsInCombat(oPC)) + return; + +// if(DEBUG) DoDebug("entered DoNaturalWeaponHB"); + + float fDelay = 0.1 + IntToFloat(Random(10))/100.0; + + //no natural weapons, abort + //in a different form, abort for now fix it later + if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) + && !GetIsPolyMorphedOrShifted(oPC)) + { + // DoDebug("DoNaturalWeaponHB: creature has natural secondary weapons"); + UpdateSecondaryWeaponSizes(oPC); + int i; + while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) + { + //get the resref to use + string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); + //if null, move to next + if(sResRef != "") + { + //get the created item + object oWeapon = GetObjectByTag(sResRef); + if(!GetIsObjectValid(oWeapon)) + { + object oLimbo = GetObjectByTag("HEARTOFCHAOS"); + location lLimbo = GetLocation(oLimbo); + if(!GetIsObjectValid(oLimbo)) + lLimbo = GetStartingLocation(); + oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo); + } + + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); + //do the attack within a delay + /* + // motu99: commented this out; AssignCommand ist not needed, because OBJECT_SELF is oPC - using AssignCommand will only degrade performance + AssignCommand(oPC, DelayCommand(fDelay, DoNaturalAttack(oWeapon))); + */ + + DelayCommand(fDelay, DoNaturalAttack(oWeapon)); + + //calculate the delay to use next time + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + } + i++; + } + } + + int iMod = 5; // motu99: added check for monk weapon + if(GetHasMonkWeaponEquipped(oPC)) iMod = 3; + + // check for overflow (main hand) attacks + int nOverflowAttackCount = GetLocalInt(oPC, "OverflowBaseAttackCount"); + if(nOverflowAttackCount) + { + int i; + // the first overflow attack would be the seventh main hand attack, at an AB of -30 + int nAttackPenalty = -6 * iMod; // -30 for normal bab, -18 for monks + // DoDebug("DoNaturalWeaponHB(): number of scripted overflow attacks: "+IntToString(nOverflowAttackCount)); + for(i = 0; i < nOverflowAttackCount; i++) + { + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted overflow attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); + /* + // motu99: see comment above why this is commented out + AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); + */ + DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty)); + + //calculate the delay to use + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + //calculate new attack penalty + nAttackPenalty -= iMod; // motu99: usually -5, for monks -3 (unarmed or kama) + } + } + + // motu99: this is only here for debugging in order to test PerformAttackRound() + // must be deleted after debugging!!! + //if (GetPRCSwitch(PRC_PNP_TRUESEEING)) DelayCommand(0.01, DoOffhandAttackRound()); + + + // check for overflow offhand attacks + int nOffhandAttackCount = GetLocalInt(oPC, "OffhandOverflowAttackCount"); +// if (DEBUG) DoDebug("DoNaturalWeaponHB: number of scripted offhand attacks = "+IntToString(nOffhandAttackCount)); + if(nOffhandAttackCount) + { + int i; + int nAttackPenalty = -2 * iMod; // offhand attacks always come at -5 per additional attack (but for monks we assume -3) + for(i = 0; i < nOffhandAttackCount; i++) + { + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted offhand attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); + + DelayCommand(fDelay, DoOffhandAttack(nAttackPenalty)); + + //calculate the delay to use + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + //calculate new attack penalty + nAttackPenalty -= iMod; + } + } +} + +/* + * motu99's test functions. Not actually used by PRC scripts + */ + +// motu99: This is only for test purposes (in order to test PerformAttackRound()) +void DoOffhandAttackRound(object oPC = OBJECT_SELF) +{ + // object oTarget = GetAttackTarget(); + object oTarget = GetAttemptedAttackTarget(); + + // motu99: The following checks should be made by PerformAttack(), so they are somewhat redundant +/* + //no point attacking plot + if(GetPlotFlag(oTarget)) + return; +*/ + // if not melee fighting, abort + if (!GetIsObjectValid(oTarget)) + return; +/* + //if not fighting, abort + if(!PRCGetIsFighting(oPC)) + { + DoDebug(COLOR_WHITE + "DoOffhandAttack(): not fighting any more - aborting"); + return; + } +*/ + string sMessageSuccess; + string sMessageFailure; + if (DEBUG) + { +// sMessageSuccess += GetName(oWeapon); + //add attack +// sMessageSuccess += " scripted offhand"; + //copy it to failure +// sMessageFailure = sMessageSuccess; + //add hit/miss + sMessageSuccess += "s hit"; + sMessageFailure += "s miss"; + //add stars around messages + sMessageSuccess = "*"+sMessageSuccess+"*"; + sMessageFailure = "*"+sMessageFailure+"*"; + } + + //null effect + effect eInvalid; + + // set the prc combat mode + int bCombatMode = PRC_COMBATMODE_HB & PRC_COMBATMODE_ALLOW_TARGETSWITCH & PRC_COMBATMODE_ABORT_WHEN_OUT_OF_RANGE; + + DoDebug(PRC_TEXT_WHITE + "initiating an overflow offhand attack round"); + + PerformAttackRound(oTarget, // object oDefender + oPC, // object oAttacker, + eInvalid, // effect eSpecialEffect, + 0.0, // float eDuration = 0.0, + 0, // int iAttackBonusMod = 0, + 0, // int iDamageModifier = 0, + 0, // int iDamageType = 0, + TRUE, // int bEffectAllAttacks = FALSE, + sMessageSuccess, // string sMessageSuccess = "", + sMessageFailure, // string sMessageFailure = "", + 0, // int bApplyTouchToAll = FALSE, + 0, // int iTouchAttackType = FALSE, + 0, //int bInstantAttack = FALSE); + bCombatMode // CombatMode + ); +} diff --git a/src/include/prc_inc_natweap.nss b/src/include/prc_inc_natweap.nss new file mode 100644 index 0000000..8d3668b --- /dev/null +++ b/src/include/prc_inc_natweap.nss @@ -0,0 +1,425 @@ +/* + + prc_inc_natweap.nss + + Natural Weapon include + + This include controlls natural weapons. + These are different to unarmed weapons. + + From the SRD: + + Natural Weapons + + Natural weapons are weapons that are physically a part of a creature. A creature making a melee attack with + a natural weapon is considered armed and does not provoke attacks of opportunity. Likewise, it threatens any + space it can reach. Creatures do not receive additional attacks from a high base attack bonus when using + natural weapons. The number of attacks a creature can make with its natural weapons depends on the type of + the attackl generally, a creature can make one bite attack, one attack per claw or tentacle, one gore attack, + one sting attack, or one slam attack (although Large creatures with arms or arm-like limbs can make a slam + attack with each arm). Refer to the individual monster descriptions. + + Unless otherwise noted, a natural weapon threatens a critical hit on a natural attack roll of 20. + + When a creature has more than one natural weapon, one of them (or sometimes a pair or set of them) is the + primary weapon. All the creature’s remaining natural weapons are secondary. + + The primary weapon is given in the creature’s Attack entry, and the primary weapon or weapons is given first + in the creature’s Full Attack entry. A creature’s primary natural weapon is its most effective natural attack, + usually by virtue of the creature’s physiology, training, or innate talent with the weapon. An attack with a + primary natural weapon uses the creature’s full attack bonus. Attacks with secondary natural weapons are less + effective and are made with a -5 penalty on the attack roll, no matter how many there are. (Creatures with the + Multiattack feat take only a -2 penalty on secondary attacks.) This penalty applies even when the creature + makes a single attack with the secondary weapon as part of the attack action or as an attack of opportunity. + + Natural weapons have types just as other weapons do. The most common are summarized below. + + Bite + The creature attacks with its mouth, dealing piercing, slashing, and bludgeoning damage. + + Claw or Talon + The creature rips with a sharp appendage, dealing piercing and slashing damage. + + Gore + The creature spears the opponent with an antler, horn, or similar appendage, dealing piercing damage. + + Slap or Slam + The creature batters opponents with an appendage, dealing bludgeoning damage. + + Sting + The creature stabs with a stinger, dealing piercing damage. Sting attacks usually deal damage from poison in + addition to hit point damage. + + Tentacle + The creature flails at opponents with a powerful tentacle, dealing bludgeoning (and sometimes slashing) damage. + + The main differences are: + + *) There are primary and secondary natural weapons. + + *) Natural weapons do not get additional attacks at higher BAB + + *) Primary natural weapon is at full BAB with full str bonus + + *) Secondary natural weaponss are at -5 (or -2 with Multiattack, or no penalty for Improved Multiattack) + with half strength bonus + + *) If a creature has a weapon in its "hands", it may still use non-claw attacks. For example, a double axe + plus a bite. + + *) A creature with both natural claw weapons and unarmed attacks, for example a Monk Werewolf, can choose + to either use natural claw weapons or unarmed attacks, but not both. + + + Implementation notes: + + *) Primary natural weapons use the creature inventory slots so the animation works + + *) Secondary natural weapons use the heartbeat and the scripted combat engine + + *) Since bioware divides the 6 second combat round into 3 flurries, secondary weapons try to respect those + flurries. + + *) The target for secondary weapons GetAttackTarget() is used. + + *) Since initiative is hardcoded, we cant use that at all. Relies on heartbeat scripts instead. +*/ + +void AddNaturalSecondaryWeapon(object oPC, string sResRef, int nCount = 1); +int GetIsUsingPrimaryNaturalWeapons(object oPC); +void SetIsUsingPrimaryNaturalWeapons(object oPC, int nNatural); +void ClearNaturalWeapons(object oPC); +void UpdateSecondaryWeaponSizes(object oPC, int nSize = -1); +string GetAffixForSize(int nSize); +void SetPrimaryNaturalWeapon(object oPC, int nIndex); +void RemoveNaturalPrimaryWeapon(object oPC, string sResRef); +void NaturalSecondaryWeaponTempCheck(object oManifester, object oTarget, int nSpellID, + int nBeatsRemaining, string sResref); +void NaturalPrimaryWeaponTempCheck(object oManifester, object oTarget, int nSpellID, + int nBeatsRemaining, string sResref); + +//the name of the array that the resrefs of the natural weapons are stored in +const string ARRAY_NAT_SEC_WEAP_RESREF = "ARRAY_NAT_SEC_WEAP_RESREF"; +const string ARRAY_NAT_PRI_WEAP_RESREF = "ARRAY_NAT_PRI_WEAP_RESREF"; +const string ARRAY_NAT_PRI_WEAP_ATTACKS = "ARRAY_NAT_PRI_WEAP_ATTACKS"; +const string NATURAL_WEAPON_ATTACK_COUNT = "NATURAL_WEAPON_ATTACK_COUNT"; + +//#include "prc_alterations" + +#include "inc_utility" +#include "prc_inc_spells" + +string GetAffixForSize(int nSize) +{ + switch(nSize) + { + case CREATURE_SIZE_FINE: return "f"; + case CREATURE_SIZE_DIMINUTIVE: return "d"; + case CREATURE_SIZE_SMALL: return "s"; + case CREATURE_SIZE_MEDIUM: return "m"; + case CREATURE_SIZE_LARGE: return "l"; + case CREATURE_SIZE_HUGE: return "h"; + case CREATURE_SIZE_GARGANTUAN: return "g"; + case CREATURE_SIZE_COLOSSAL: return "c"; + default: return "l"; + } + return ""; +} + +void EquipNaturalWeaponCheck(object oPC, object oItem) +{ + if(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC) != oItem) + MyDestroyObject(oItem); + //else + // DelayCommand(10.0, + // EquipNaturalWeaponCheck(oPC, oItem)); +} + +object EquipNaturalWeapon(object oPC, string sResRef) +{ + object oObject = GetItemPossessedBy(oPC, sResRef); + if (DEBUG) DoDebug("EquipNaturalWeapon Step #1 ResRef "+sResRef); + if(GetIsObjectValid(oObject)) + { + if(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC) == oObject) + return oObject; + + MyDestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC)); + } + else + { + oObject = CreateItemOnObject(sResRef, oPC); + SetIdentified(oObject, TRUE); + } + if (DEBUG) DoDebug("EquipNaturalWeapon Step #2"); + ForceEquip(oPC, oObject, INVENTORY_SLOT_CWEAPON_L); + //AssignCommand(oPC, DelayCommand(10.0, EquipNaturalWeaponCheck(oPC, oObject))); + FloatingTextStringOnCreature("Equipped "+GetName(oObject)+" as Primary Natural Weapon", oPC, FALSE); + return oObject; +} + +void UpdateNaturalWeaponSizes(object oPC) +{ + int nSize = PRCGetCreatureSize(oPC); + int nLastSize = GetLocalInt(oPC, "NaturalWeaponCreatureSize"); + if(nSize == nLastSize) + return; + SetLocalInt(oPC, "NaturalWeaponCreatureSize", nSize); + string sCurrent = "_"+GetAffixForSize(nSize); + //secondary + UpdateSecondaryWeaponSizes(oPC, nSize); + //primary + int i; + for(i=0; i 1) + { + //get the object thats going to apply the effect + //this strips previous effects too + object oWP = GetObjectToApplyNewEffect("WP_PrimaryNaturalWeapEffect", oPC, TRUE); + AssignCommand(oWP, ActionDoCommand(ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectModifyAttacks(nAttackCount-1)), oPC))); + } +} + +void SetPrimaryNaturalWeapon(object oPC, int nIndex) +{ + if(!array_exists(oPC, ARRAY_NAT_PRI_WEAP_RESREF)) + array_create(oPC, ARRAY_NAT_PRI_WEAP_RESREF); + if(!array_exists(oPC, ARRAY_NAT_PRI_WEAP_ATTACKS)) + array_create(oPC, ARRAY_NAT_PRI_WEAP_ATTACKS); + if (DEBUG) DoDebug("SetPrimaryNaturalWeapon Step #1"); + int nOverride = GetPersistantLocalInt(oPC, "NaturalWeaponPlayerOverride"); + if (DEBUG) DoDebug("SetPrimaryNaturalWeapon nIndex "+IntToString(nIndex)+", nOverride "+IntToString(nOverride)); + if(nOverride) nIndex = nOverride; + if(nIndex == -1) + { + //remove natural weapons + DestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC)); + DestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC)); + DestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oPC)); + DeleteLocalInt(oPC, NATURAL_WEAPON_ATTACK_COUNT); + return; + } + string sResRef = array_get_string(oPC, ARRAY_NAT_PRI_WEAP_RESREF, nIndex); + if (DEBUG) DoDebug("SetPrimaryNaturalWeapon Step #2 resref is "+sResRef); + if(sResRef == "") + return; + object oNaturalWeapon = EquipNaturalWeapon(oPC, sResRef); + SetPrimaryNaturalWeaponAttacks(oPC, nIndex); +} + +int GetPrimaryNaturalWeaponCount(object oPC) +{ + return array_get_size(oPC, ARRAY_NAT_PRI_WEAP_RESREF); +} + + +void NaturalPrimaryWeaponTempCheck(object oManifester, object oTarget, int nSpellID, + int nBeatsRemaining, string sResRef) +{ + if(!((nBeatsRemaining-- == 0) || // Has the power ended since the last beat, or does the duration run out now + PRCGetDelayedSpellEffectsExpired(nSpellID, oTarget, oManifester) || // Has the power been dispelled + PRCGetHasEffect(EFFECT_TYPE_POLYMORPH, oTarget) // Has the target been polymorphed + ) + ) + { + // Schedule next HB + DelayCommand(6.0f, NaturalPrimaryWeaponTempCheck(oManifester, oTarget, nSpellID, nBeatsRemaining, sResRef)); + } + // Power expired + else + { + if(DEBUG) DoDebug(sResRef+": Power expired, exiting HB"); + PRCRemoveSpellEffects(nSpellID, oManifester, oTarget); + RemoveNaturalPrimaryWeapon(oTarget, sResRef); + } +} \ No newline at end of file diff --git a/src/include/prc_inc_newip.nss b/src/include/prc_inc_newip.nss new file mode 100644 index 0000000..ea4bc98 --- /dev/null +++ b/src/include/prc_inc_newip.nss @@ -0,0 +1,491 @@ +//:: New itemproperties +const int ITEM_PROPERTY_USE_LIMITATION_ABILITY_SCORE = 95; +const int ITEM_PROPERTY_USE_LIMITATION_SKILL_RANKS = 96; +const int ITEM_PROPERTY_USE_LIMITATION_SPELL_LEVEL = 88; +const int ITEM_PROPERTY_USE_LIMITATION_ARCANE_SPELL_LEVEL = 89; +const int ITEM_PROPERTY_USE_LIMITATION_DIVINE_SPELL_LEVEL = 90; +const int ITEM_PROPERTY_USE_LIMITATION_SNEAK_ATTACK = 91; +const int ITEM_PROPERTY_USE_LIMITATION_GENDER = 150; +const int ITEM_PROPERTY_SPEED_INCREASE = 133; +const int ITEM_PROPERTY_SPEED_DECREASE = 134; +const int ITEM_PROPERTY_AREA_OF_EFFECT = 100; +const int ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL = 94; +const int ITEM_PROPERTY_CAST_SPELL_METAMAGIC = 92; +const int ITEM_PROPERTY_CAST_SPELL_DC = 93; +const int ITEM_PROPERTY_PNP_HOLY_AVENGER = 101; +const int ITEM_PROPERTY_WIZARDRY = 102; +const int ITEM_PROPERTY_DIVINITY = 103; +const int ITEM_PROPERTY_ECHOBLADE = 104; + +//:: AoE itemproperties +const int IP_CONST_AOE_DARKNESS = 0; +const int IP_CONST_AOE_DEEPER_DARKNESS = 1; +const int IP_CONST_AOE_CIRCLE_VS_EVIL = 2; +const int IP_CONST_AOE_CIRCLE_VS_GOOD = 3; +const int IP_CONST_AOE_CIRCLE_VS_LAW = 4; +const int IP_CONST_AOE_CIRCLE_VS_CHAOS = 5; +const int IP_CONST_AOE_DAMNING_DARKNESS = 6; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +//new function to return a PRC caster level itemproperty +//will putput to log file if it doesnt work +//relys on blueprints containing these itemproperties +itemproperty ItemPropertyCastSpellCasterLevel(int nSpell, int nLevel); + +//new function to return a PRC metamagic itemproperty +//will putput to log file if it doesnt work +//relys on blueprints containing these itemproperties +//nMetamagic should be a METAMAGIC_* constant +itemproperty ItemPropertyCastSpellMetamagic(int nSpell, int nMetamagic); + +//new function to return a PRC DC itemproperty +//will putput to log file if it doesnt work +//relys on blueprints containing these itemproperties +itemproperty ItemPropertyCastSpellDC(int nSpell, int nDC); + +//new function to return a PRC AoE itemproperty +//will putput to log file if it doesnt work +//relys on blueprints containing these itemproperties +//nIPAoEID is defined in iprp_aoe & IP_CONST_AOE_* +itemproperty ItemPropertyAreaOfEffect(int nIPAoEID, int nLevel); + +//returns the PRCs new PnP Holy Avenger +//for paladins, +5 +2d6 divine vs evil, castspell:dispel magic @ casterlevel = paladinlevels +//or non paladins, +2 +itemproperty ItemPropertyPnPHolyAvenger(); + +//new function to return a PRC wizardry itemproperty +//will putput to log file if it doesnt work +//relys on blueprints containing these itemproperties +itemproperty ItemPropertyWizardry(int nSpellLevel); + +//new function to return a PRC Divinity itemproperty +//will putput to log file if it doesnt work +//relys on blueprints containing these itemproperties +itemproperty ItemPropertyDivinity(int nSpellLevel); + +//returns Echoblade itemproperty +itemproperty ItemPropertyEchoblade(); + +//not implemented +itemproperty ItemPropertyLimitUseByAbility(int nAbility, int nMinScore); +//not implemented +itemproperty ItemPropertyLimitUseBySkill(int nSkill, int nMinScore); +//not implemented +itemproperty ItemPropertyLimitUseBySpellcasting(int nLevel); +//not implemented +itemproperty ItemPropertyLimitUseByArcaneSpellcasting(int nLevel); +//not implemented +itemproperty ItemPropertyLimitUseByDivineSpellcasting(int nLevel); +//not implemented +itemproperty ItemPropertyLimitUseBySneakAttackDice(int nDice); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_x2_itemprop" +//#include "prc_alterations" + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +itemproperty ItemPropertyCastSpellMetamagic(int nSpell, int nMetamagic) +{ + //convert nSpell into reference to iprip_spells.2da + nSpell = IPGetIPConstCastSpellFromSpellID(nSpell); + + itemproperty ipReturn; + string sResRef = "prc_ip" + IntToString(ITEM_PROPERTY_CAST_SPELL_METAMAGIC) + "_" + IntToString(nSpell); + object oChest = GetObjectByTag("HEARTOFCHAOS");//use the crafting chest + object oItem = CreateItemOnObject(sResRef, oChest); + DestroyObject(oItem); + switch(nMetamagic) + { + case METAMAGIC_NONE: + return ipReturn;//doenst work as an IP + nMetamagic = 0; + break; + case METAMAGIC_EMPOWER: + nMetamagic = 2; + break; + case METAMAGIC_EXTEND: + nMetamagic = 3; + break; + case METAMAGIC_MAXIMIZE: + nMetamagic = 4; + break; + case METAMAGIC_QUICKEN: + return ipReturn;//doenst work as an IP + nMetamagic = 1; + break; + case METAMAGIC_SILENT: + return ipReturn;//doenst work as an IP + nMetamagic = 5; + break; + case METAMAGIC_STILL: + return ipReturn;//doenst work as an IP + nMetamagic = 6; + break; + } + ipReturn = GetFirstItemProperty(oItem); + int i; + for(i=0;i 16029) //not newspellbook spell or psionic power + { + //does not apply to spellscripts triggered by feats + if(Get2DACache("spells", "FeatID", nSpellID) != "") + return oBWTarget; + + //either a feat, or a spell, check to make doubly sure, in the case of monster abilities + if( + (Get2DACache("spells", "Wiz_Sorc", nSpellID) == "") && + (Get2DACache("spells", "Cleric", nSpellID) == "") && + (Get2DACache("spells", "Bard", nSpellID) == "") && + (Get2DACache("spells", "Druid", nSpellID) == "") && + (Get2DACache("spells", "Paladin", nSpellID) == "") && + (Get2DACache("spells", "Ranger", nSpellID) == "") + ) + return oBWTarget; //we shouldn't be checking feats or other spellbooks + } + + // Force missile mage reflects all magic missiles + if (GetLevelByClass(CLASS_TYPE_FMM, oBWTarget) >= 4 && nSpellID == SPELL_MAGIC_MISSILE) + return oCaster; + + int bTouch = GetStringUpperCase(Get2DACache("spells", "Range", nSpellID)) == "T"; + // Reddopsi power causes spells and powers to rebound onto the caster. + if(GetLocalInt(oBWTarget, "PRC_Power_Reddopsi_Active") && // Reddopsi is active on the target + !GetLocalInt(oCaster, "PRC_Power_Reddopsi_Active") && // And not on the manifester + !(nSpellID == SPELL_LESSER_DISPEL || // And the spell/power is not a dispelling one + nSpellID == SPELL_DISPEL_MAGIC || + nSpellID == SPELL_GREATER_DISPELLING || + nSpellID == SPELL_MORDENKAINENS_DISJUNCTION || + nSpellID == POWER_DISPELPSIONICS + ) && + !bTouch // And the spell/power is not touch range + ) + return oCaster; + + if(GetLocalInt(oBWTarget, "PRC_SPELL_TURNING") && + !(nSpellID == SPELL_LESSER_DISPEL || // And the spell/power is not a dispelling one + nSpellID == SPELL_DISPEL_MAGIC || + nSpellID == SPELL_GREATER_DISPELLING || + nSpellID == SPELL_MORDENKAINENS_DISJUNCTION || + nSpellID == POWER_DISPELPSIONICS) && + !bTouch + ) + { + int nSpellLevel = StringToInt(Get2DACache("spells", "Innate", nSpellID));//lookup_spell_innate(nSpellID)); + object oTarget = oBWTarget; + int nLevels = GetLocalInt(oTarget, "PRC_SPELL_TURNING_LEVELS"); + int bCasterTurning = GetLocalInt(oCaster, "PRC_SPELL_TURNING"); + int nCasterLevels = GetLocalInt(oCaster, "PRC_SPELL_TURNING_LEVELS"); + if(!bCasterTurning) + { + if(nSpellLevel > nLevels) + { + if((Random(nSpellLevel) + 1) <= nLevels) + oTarget = oCaster; + } + else + oTarget = oCaster; + } + else + { + if((Random(nCasterLevels + nLevels) + 1) <= nLevels) + oTarget = oCaster; + nCasterLevels -= nSpellLevel; + if(nCasterLevels < 0) nCasterLevels = 0; + SetLocalInt(oCaster, "PRC_SPELL_TURNING_LEVELS", nCasterLevels); + } + nLevels -= nSpellLevel; + if(nLevels < 0) nLevels = 0; + SetLocalInt(oBWTarget, "PRC_SPELL_TURNING_LEVELS", nLevels); + return oTarget; + } + } + + // 50% chance of this happening + if(GetHasSpellEffect(SPELL_BLINK, oBWTarget) && d2() == 2) + return OBJECT_INVALID; + + // The rune/gem/skull always targets the one who activates it. + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && (GetResRef(oItem) == "prc_rune_1" || + GetResRef(oItem) == "prc_skulltalis" || GetTag(oItem) == "prc_attunegem")) + { + if(DEBUG) DoDebug(GetName(oCaster) + " has cast a spell using a rune"); + // Making sure that the owner of the item is correct + if (GetIsObjectValid(GetItemPossessor(oItem))) + { + if(DEBUG) DoDebug(GetName(oCaster) + " is the owner of the Spellcasting item"); + return GetItemPossessor(oItem); + } + } + + + // return Bioware's target + return oBWTarget; +} + +/** + * PRCGetSpellCastItem(object oCaster = OBJECT_SELF) + * wrapper function for GetSpellCastItem() + * + * Note that we are giving preference for the local object, "PRC_SPELLCASTITEM_OVERRIDE", stored on oCaster + * Therefore it is absolutely essential, in order to have this variable not interfere with "normal" spell casting, + * to delete it *immediately after* the spell script executed. All of this is taken care of in the function + * ExecuteSpellScript(), which should be used instead of any direct calls to the spell scripts. + * In particular, NEVER MANUALLY set the overrides. You might ruin the whole spell casting system! + * + * Another possibility would have been, to give preference to the GetSpellCastItem() call and only fetch the + * local object "PRC_SPELLCASTITEM_OVERRIDE" when GetSpellCastItem() returns an invalid object. + * This is how it is was done in the PRC 3.1c version of prc_onhitcast (lines 58-61), and in psi_sk_onhit, prc_evnt_bonebld, prc_evnt_strmtl + * [In those scripts the local (override) object was called "PRC_CombatSystem_OnHitCastSpell_Item". In order to be consistent with + * the naming conventions of the other override variables, I changed the name of the override object to PRC_SPELLCASTITEM_OVERRIDE + * and provided the wrapper PRCGetSpellCastItem for an easy use of the onhitcast system] + * However, that approach DOES NOT WORK, because Bioware didn't bother to implement GetSpellCastItem() properly. + * In a proper implementation GetSpellCastItem() word return OBJECT_INVALID, when called outside of an item spell script, + * But this is not the case. GetSpellCastItem() will always return the item, from which (according to Bioware's knowledge) + * the last item spell was cast. As long as the item still exists, the call to GetSpellCastItem() will always return a valid item, + * even if the item spell long expired and we are casting a completely differnt spell. So GetSpellCastItem() practically + * NEVER returns an invalid object. [We only get an invalid object, when we didn't yet cast any item spell at all] + * + * Possible caveats: + * You should never cast spells as an action, when the local override object "PRC_SPELLCASTITEM_OVERRIDE" + * is set (and subsequently deleted) *outside* the action script. This also pertains to other override variables, such as + * PRC_SPELL_TARGET_OBJECT_OVERRIDE, PRC_METAMAGIC_OVERRIDE, etc. + * If you set (and delete) a local override (object or int) *within* one single action, thats ok. For instance putting + * ExecuteSpellScript() into an ActionDoCommand, an AssignCommand or a DelayCommand will work. + * But (manually) setting "PRC_SPELLCASTITEM_OVERRIDE", then calling ActionCastSpellAt* + * (which will insert the spell cast action into the action queue) and after that trying to delete the overrides + * via a DelayCommand or an AssignCommand(), often just guessing how long it takes the spell cast action to run, + * will most likely break any other spell casting that is done between manually setting the override and deleting it. + * So please follow the advise to never MANUALLY set the override variables. Use the functions provided here + * (ExecuteSpellScript, CastSpellAtObject, CastSpellAtLocation, etc. ) or - if you must - build your own + * functions by using the provided functions either directly or as templates (they show you how to do things right) + */ +object PRCGetSpellCastItem(object oCaster = OBJECT_SELF) +{ + // if the local object "PRC_SPELLCASTITEM_OVERRIDE" is valid, we take it without even looking for anything else + object oItem = GetLocalObject(oCaster, PRC_SPELLCASTITEM_OVERRIDE); + if (GetIsObjectValid(oItem)) + { + // OBJECT_SELF counts as invalid item + if (oItem == OBJECT_SELF) + { + oItem = OBJECT_INVALID; + } + + if (DEBUG) DoDebug("PRCGetSpellCastItem: found override spell cast item = "+GetName(oItem)+", original item = " + GetName(GetSpellCastItem())); + return oItem; + } + + // otherwise simply return Bioware's GetSpellCastItem + oItem = GetSpellCastItem(); + if (DEBUG) DoDebug("PRCGetSpellCastItem: no override, returning bioware spell cast item = "+GetName(oItem)); + return oItem; + /* + // motu99: disabled the old stuff; was only used in three scripts (changed them) + // and couldn't work anyway (because of Bioware's improper implementation of GetSpellCastItem) + // if Bioware's functions doesn't return a valid object, maybe the scripted combat system will + if(!GetIsObjectValid(oItem)) + oItem = GetLocalObject(oPC, "PRC_CombatSystem_OnHitCastSpell_Item"); + */ +} + +itemproperty PRCItemPropertyBonusFeat(int nBonusFeatID) +{ + string sTag = "PRC_IPBF_"+IntToString(nBonusFeatID); + object oTemp = GetObjectByTag(sTag); + if(!GetIsObjectValid(oTemp)) + { + if(DEBUG) DoDebug("PRCItemPropertyBonusFeat() : Cache object " + sTag + " is not valid, creating"); + location lLimbo; + object oLimbo = GetObjectByTag("HEARTOFCHAOS"); + if(GetIsObjectValid(oLimbo)) + lLimbo = GetLocation(oLimbo); + else + lLimbo = GetStartingLocation(); + oTemp = CreateObject(OBJECT_TYPE_ITEM, "base_prc_skin", lLimbo, FALSE, sTag); + } + itemproperty ipReturn = GetFirstItemProperty(oTemp); + if(!GetIsItemPropertyValid(ipReturn)) + { + if(DEBUG) DoDebug("PRCItemPropertyBonusFeat() : Itemproperty was not present on cache object, adding"); + ipReturn = ItemPropertyBonusFeat(nBonusFeatID); + AddItemProperty(DURATION_TYPE_PERMANENT, ipReturn, oTemp); + } + return ipReturn; +} + +int GetPRCIsSkillSuccessful(object oCreature, int nSkill, int nDifficulty, int nRollOverride = -1) +{ + int nRanks = GetSkillRank(nSkill, oCreature); + if(nRollOverride > 20) + { + nRollOverride = 20; + if(DEBUG) DoDebug("GetPRCIsSkillSuccessful: nRollOverride > 20"); + } + if(nRollOverride < 0 || (nSkill + nRollOverride) < nDifficulty) + return GetIsSkillSuccessful(oCreature, nSkill, nDifficulty); + else + { //we're going to fake a skill check here + SendMessageToPC(oCreature, + PRC_TEXT_LIGHT_BLUE + GetName(oCreature) + PRC_TEXT_DARK_BLUE + " : " + + GetStringByStrRef(StringToInt(Get2DACache("skills", "Name", nSkill))) + " : *" + + (((nRollOverride + nRanks) >= nDifficulty) ? GetStringByStrRef(5352) : ((nDifficulty > nRanks + 20) ? GetStringByStrRef(8101) : GetStringByStrRef(5353))) + "* : " + + "(" + IntToString(nRollOverride) + " + " + IntToString(nRanks) + " = " + IntToString(nRollOverride + nRanks) + " vs. DC: " + IntToString(nDifficulty) + ")" + ); + } + return (nRollOverride + nRanks >= nDifficulty); +} + +int PRCGetCreatureSize(object oObject = OBJECT_SELF, int nSizeMask = PRC_SIZEMASK_ALL) +{ + //int nSize = GetCreatureSize(oObject); + int nSize = StringToInt(Get2DAString("appearance", "SizeCategory", GetAppearanceType(oObject))); + if (DEBUG) DoDebug("Appearance-based GetCreatureSize, returning size: "+IntToString(nSize)); + if (DEBUG) DoDebug("Bioware GetCreatureSize, returning size: "+IntToString(GetCreatureSize(oObject))); + //CEP adds other sizes, take them into account too + if(nSize == 20) + nSize = CREATURE_SIZE_DIMINUTIVE; + else if(nSize == 21) + nSize = CREATURE_SIZE_FINE; + else if(nSize == 22) + nSize = CREATURE_SIZE_GARGANTUAN; + else if(nSize == 23) + nSize = CREATURE_SIZE_COLOSSAL; + + if(nSizeMask & PRC_SIZEMASK_NORMAL) + { + if(GetHasFeat(FEAT_SIZE_DECREASE_6, oObject)) + nSize += -6; + else if(GetHasFeat(FEAT_SIZE_DECREASE_5, oObject)) + nSize += -5; + else if(GetHasFeat(FEAT_SIZE_DECREASE_4, oObject)) + nSize += -4; + else if(GetHasFeat(FEAT_SIZE_DECREASE_3, oObject)) + nSize += -3; + else if(GetHasFeat(FEAT_SIZE_DECREASE_2, oObject)) + nSize += -2; + else if(GetHasFeat(FEAT_SIZE_DECREASE_1, oObject)) + nSize += -1; + + if(GetHasFeat(FEAT_SIZE_INCREASE_6, oObject)) + nSize += 6; + else if(GetHasFeat(FEAT_SIZE_INCREASE_5, oObject)) + nSize += 5; + else if(GetHasFeat(FEAT_SIZE_INCREASE_4, oObject)) + nSize += 4; + else if(GetHasFeat(FEAT_SIZE_INCREASE_3, oObject)) + nSize += 3; + else if(GetHasFeat(FEAT_SIZE_INCREASE_2, oObject)) + nSize += 2; + else if(GetHasFeat(FEAT_SIZE_INCREASE_1, oObject)) + nSize += 1; + } + + if(nSizeMask & PRC_SIZEMASK_NOABIL + || ((nSizeMask & PRC_SIZEMASK_NORMAL) && GetPRCSwitch(PRC_DRAGON_DISCIPLE_SIZE_CHANGES))) + { + if(GetHasFeat(FEAT_DRACONIC_SIZE_INCREASE_2, oObject)) + nSize += 2; + else if(GetHasFeat(FEAT_DRACONIC_SIZE_INCREASE_1, oObject)) + nSize += 1; + } + + if(nSizeMask & PRC_SIZEMASK_SIMPLE) + { + // Size changing powers + // Compression: Size decreased by one or two categories, depending on augmentation + if(GetLocalInt(oObject, "PRC_Power_Compression_SizeReduction")) + nSize -= GetLocalInt(oObject, "PRC_Power_Compression_SizeReduction"); + // Expansion: Size increase by one or two categories, depending on augmentation + if(GetLocalInt(oObject, "PRC_Power_Expansion_SizeIncrease")) + nSize += GetLocalInt(oObject, "PRC_Power_Expansion_SizeIncrease"); + } + + if(nSize < CREATURE_SIZE_FINE) + nSize = CREATURE_SIZE_FINE; + if(nSize > CREATURE_SIZE_COLOSSAL) + nSize = CREATURE_SIZE_COLOSSAL; + if (DEBUG) DoDebug("PRCGetCreatureSize, returning size: "+IntToString(nSize)); + return nSize; +} + +int GetIsChakraBound(object oMeldshaper, int nChakra) +{ + int nTest = GetLocalInt(oMeldshaper, "BoundMeld"+IntToString(nChakra)); + + if (DEBUG) DoDebug("GetIsChakraBound is "+IntToString(nTest)); + return nTest; +} + +int GetMaxEssentiaCapacity(object oMeldshaper, int nClass, int nMeld) +{ + int nMax = 1; // Always can invest one + int nHD = GetHitDice(oMeldshaper); + if (nHD >= 31) nMax = 5; + else if (nHD >= 18) nMax = 4; + else if (nHD >= 12) nMax = 3; + else if (nHD >= 6) nMax = 2; + + if (nClass == CLASS_TYPE_INCARNATE && GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 3) nMax++; + if (nClass == CLASS_TYPE_INCARNATE && GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) >= 15) nMax++; + + if (nClass == CLASS_TYPE_TOTEMIST && GetIsMeldBound(oMeldshaper, nMeld) == CHAKRA_TOTEM) nMax++; + if (nClass == CLASS_TYPE_TOTEMIST && GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) >= 15 && GetIsMeldBound(oMeldshaper, nMeld) == CHAKRA_TOTEM) nMax++; + + if (nClass == CLASS_TYPE_TOTEMIST && GetLevelByClass(CLASS_TYPE_TOTEM_RAGER, oMeldshaper) >= 10 && GetIsMeldBound(oMeldshaper, nMeld) == CHAKRA_TOTEM) nMax++; + + if (GetIsNecrocarnumMeld(nMeld) && GetLevelByClass(CLASS_TYPE_NECROCARNATE, oMeldshaper) >= 9) nMax++; + if (GetLocalInt(oMeldshaper, "DivineSoultouch")) nMax += 1; + if (GetLocalInt(oMeldshaper, "IncandescentOverload")) + { + if (GetAbilityModifier(ABILITY_CHARISMA, oMeldshaper) > 1) + nMax += GetAbilityModifier(ABILITY_CHARISMA, oMeldshaper); + else + nMax += 1; + } + if (GetExpandedSoulmeld(oMeldshaper, nMeld)) nMax += 1; + + if (DEBUG) DoDebug("GetMaxEssentiaCapacity: nHD "+IntToString(nHD)+" nMax "+IntToString(nMax)); + return nMax; +} + +int GetEssentiaInvested(object oMeldshaper, int nMeld = -1) +{ + if (nMeld == -1) nMeld = PRCGetSpellId(); + + if (GetLocalInt(oMeldshaper, "PerfectMeldshaper")) return GetMaxEssentiaCapacity(oMeldshaper, CLASS_TYPE_INCARNATE, -1); + + int nReturn = GetLocalInt(oMeldshaper, "MeldEssentia"+IntToString(nMeld)); + if (GetLocalInt(oMeldshaper, "TotemEmbodiment") == nMeld) nReturn = nReturn * 2; + if (GetLocalInt(oMeldshaper, "TotemEmbodiment2") == nMeld) nReturn = nReturn * 2; + if (DEBUG) DoDebug("GetEssentiaInvested nMeld "+IntToString(nMeld)+" nReturn "+IntToString(nReturn)); + return nReturn; +} + +int GetIsMeldBound(object oMeldshaper, int nMeld = -1) +{ + if (nMeld == -1) nMeld = PRCGetSpellId(); + int i, nBind, nTest; + for (i = 1; i <= 22; i++) + { + nTest = GetLocalInt(oMeldshaper, "BoundMeld"+IntToString(i)); + if (nTest == nMeld) // If it's been marked as bound + nBind = i; + } + //FloatingTextStringOnCreature("GetIsMeldBound: nMeld "+IntToString(nMeld)+" nBind "+IntToString(nBind), oMeldshaper); + if (DEBUG) DoDebug("GetIsMeldBound is "+IntToString(nBind)); + + return nBind; // Return which Chakra it's bound to +} + +int GetMeldshaperAbilityOfClass(int nClass, object oMeldshaper) +{ + // Incarnates use Wisdom for DC, everyone else uses Con + if (nClass == CLASS_TYPE_INCARNATE || GetHasFeat(FEAT_UNDEAD_MELDSHAPER, oMeldshaper)) + return ABILITY_WISDOM; + else + return ABILITY_CONSTITUTION; + + // Technically, never gets here but the compiler does not realise that + return -1; +} + +int GetMeldshaperDC(object oMeldshaper, int nClass, int nMeldId) +{ + int nAbi = GetAbilityModifier(GetMeldshaperAbilityOfClass(nClass, oMeldshaper), oMeldshaper) + GetEssentiaInvested(oMeldshaper, nMeldId); + + // DC is 10 + ability + int nDC = 10 + nAbi; + if (GetIsNecrocarnumMeld(nMeldId) && GetHasFeat(FEAT_NECROCARNUM_ACOLYTE, oMeldshaper)) nDC += 1; + if (DEBUG) DoDebug("GetMeldshaperDC: nAbi "+IntToString(nAbi)+" nMeldId "+IntToString(nMeldId)); + return nDC; +} + +int GetEssentiaInvestedFeat(object oMeldshaper, int nFeat) +{ + int nReturn = GetLocalInt(oMeldshaper, "FeatEssentia"+IntToString(nFeat)); + if (DEBUG) DoDebug("GetEssentiaInvestedFeat nFeat "+IntToString(nFeat)+" nReturn "+IntToString(nReturn)); + return nReturn; +} + +int GetIsNecrocarnumMeld(int nMeld) +{ + int nReturn = FALSE; + + if (nMeld == MELD_NECROCARNUM_CIRCLET || + nMeld == MELD_NECROCARNUM_MANTLE || + nMeld == MELD_NECROCARNUM_SHROUD || + nMeld == MELD_NECROCARNUM_TOUCH || + nMeld == MELD_NECROCARNUM_VESTMENTS || + nMeld == MELD_NECROCARNUM_WEAPON) + nReturn = TRUE; + + if (DEBUG) DoDebug("GetIsNecrocarnumMeld nReturn "+IntToString(nReturn)); + return nReturn; +} + +int DoubleChakraToChakra(int nChakra) +{ + if (nChakra == CHAKRA_DOUBLE_CROWN ) return CHAKRA_CROWN; + if (nChakra == CHAKRA_DOUBLE_FEET ) return CHAKRA_FEET; + if (nChakra == CHAKRA_DOUBLE_HANDS ) return CHAKRA_HANDS; + if (nChakra == CHAKRA_DOUBLE_ARMS ) return CHAKRA_ARMS; + if (nChakra == CHAKRA_DOUBLE_BROW ) return CHAKRA_BROW; + if (nChakra == CHAKRA_DOUBLE_SHOULDERS) return CHAKRA_SHOULDERS; + if (nChakra == CHAKRA_DOUBLE_THROAT ) return CHAKRA_THROAT; + if (nChakra == CHAKRA_DOUBLE_WAIST ) return CHAKRA_WAIST; + if (nChakra == CHAKRA_DOUBLE_HEART ) return CHAKRA_HEART; + if (nChakra == CHAKRA_DOUBLE_SOUL ) return CHAKRA_SOUL; + if (nChakra == CHAKRA_DOUBLE_TOTEM ) return CHAKRA_TOTEM; + + return nChakra; +} + +int GetExpandedSoulmeld(object oMeldshaper, int nMeld) +{ + int i, nCount, nTest; + for (i = 1; i <= 5; i++) + { + nTest = GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(i)); + if (nTest == nMeld) + nCount = TRUE; + } + if (DEBUG) DoDebug("GetExpandedSoulmeld is "+IntToString(nCount)); + return nCount; +} + +int GetIsSoulmeldCapacityUsed(object oMeldshaper) +{ + // If we have the feat and it's not marked as used + if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_1, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(1))) return FALSE; + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_2, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(2))) return FALSE; + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_3, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(3))) return FALSE; + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_4, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(4))) return FALSE; + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_5, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(5))) return FALSE; + + return TRUE; +} + +void SetIsSoulmeldCapacityUsed(object oMeldshaper, int nMeld) +{ + // This is called from a place where we've just checked there was an empty slot + if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_1, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(1))) SetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(1), nMeld); + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_2, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(2))) SetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(2), nMeld); + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_3, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(3))) SetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(3), nMeld); + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_4, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(4))) SetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(4), nMeld); + else if (GetHasFeat(FEAT_EXPANDED_SOULMELD_CAPACITY_5, oMeldshaper) && !GetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(5))) SetLocalInt(oMeldshaper, "ExpandedSoulmeld"+IntToString(5), nMeld); +} + +int GetIsMeldShaped(object oMeldshaper, int nMeld, int nClass) +{ + int i, nCount, nTest; + for (i = 0; i <= 20; i++) + { + nTest = GetLocalInt(oMeldshaper, "ShapedMeld"+IntToString(nClass)+IntToString(i)); + if (nTest == nMeld) // If it's been marked as shaped for that class + nCount = TRUE; + } + if (DEBUG) DoDebug("GetIsMeldShaped is "+IntToString(nCount)); + return nCount; +} + +int GetMeldShapedClass(object oMeldshaper, int nMeld) +{ + int nClass; + if (GetIsMeldShaped(oMeldshaper, nMeld, CLASS_TYPE_INCARNATE)) nClass = CLASS_TYPE_INCARNATE; + else if (GetIsMeldShaped(oMeldshaper, nMeld, CLASS_TYPE_SOULBORN)) nClass = CLASS_TYPE_SOULBORN; + else if (GetIsMeldShaped(oMeldshaper, nMeld, CLASS_TYPE_TOTEMIST)) nClass = CLASS_TYPE_TOTEMIST; + else if (GetIsMeldShaped(oMeldshaper, nMeld, CLASS_TYPE_SPINEMELD_WARRIOR)) nClass = CLASS_TYPE_SPINEMELD_WARRIOR; + + return nClass; +} + +int GetIsIncarnumUser(object oMeldshaper) +{ + return 0!=(GetLevelByClass(CLASS_TYPE_INCARNATE, oMeldshaper) + || GetLevelByClass(CLASS_TYPE_SOULBORN, oMeldshaper) + || GetLevelByClass(CLASS_TYPE_TOTEMIST, oMeldshaper) + || GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oMeldshaper) + || GetHasFeat(FEAT_HEART_INCARNUM, oMeldshaper) + || GetHasFeat(FEAT_INCARNUM_FORTIFIED_BODY, oMeldshaper) + || GetHasFeat(FEAT_INVEST_ESSENTIA_CONV, oMeldshaper) + || GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oMeldshaper)); +} + +int GetIsBinder(object oBinder) +{ + return !(!(GetLevelByClass(CLASS_TYPE_BINDER, oBinder) + || GetHasFeat(FEAT_BIND_VESTIGE, oBinder) + || GetRacialType(oBinder) == RACIAL_TYPE_KARSITE)); +} + +int GetAberrantFeatCount(object oPC) +{ + int i, nCount; + for (i = 5387; i <= 5398; i++) + { + if (GetHasFeat(i, oPC)) + nCount++; + } + if (DEBUG) DoDebug("GetAberrantFeatCount is "+IntToString(nCount)); + return nCount; +} + +//:: Test Void +//:: void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_onhit.nss b/src/include/prc_inc_onhit.nss new file mode 100644 index 0000000..3f0da67 --- /dev/null +++ b/src/include/prc_inc_onhit.nss @@ -0,0 +1,1037 @@ +/* + ---------------- + prc_inc_onhit.nss + ---------------- + + functions for instant spell casting + and onhitcasting + + created April 15, 2007 by motu99 + */ + + +/** + * new functions for a highly flexible onhitcast system + * and for instant spell casting (without having to assign an action) + * added by motu99; April 15, 2007 + * + * most of this has been tested; some is still beta + * + * In principle these functions should allow us to + * i) instantly cast *any* type of spell listed in spells.2da + * ii) put any conceivable spell (and as many as we like) on an item + * + * i) instant spell casting + * The new functions CastSpellAtObject() and CastSpellAtLocation() will cast any spell listed in spells.2da + * instantaneously, without having to insert the spell into the action queue of the caster + * (and thus not knowing when the spell actually will be done, if ever). + * + * ii) onhit casting + * We can now "convert" any type of spell (listed in spells.2da) to an onhitcast spell + * we can put as many onhitcast spells on an item as we want + * all onhitcast spells on an item will be cast automatically, when the bearer of the item + * has scored a hit (item = weapon) or has received a hit (item= armor) + */ + +/** + * Making your spells compatible with PRC instant spell casting + * using the provided functions CastSpellAtObject() and CastSpellAtLocation() + * + * What you have to do: + * + * Insert PRC wrappers into the spell script + * + * How do you do it? + * + * Edit your spell script, replacing all calls to the spell "information" functions + * - GetSpellCastItem, + * - GetSpellTargetObject, + * - GetSpellTargetLocation, + * - GetMetaMagicFeat, + * - GetCasterLevel, + * - GetSpellID + * - GetLastSpellCastClass, +* - etc. + * with the respective PRC wrappers + * [just add "PRC" to the left of the name of the respective "information" function] + * + * Nothing else needs to be done + * + * However, you might want to check, whether you should use PRC functions for other things, + * as the Bioware functions don't take the special abilities of the PRC-classes into account. + * For instance you might want to use: + * - PRCDoResistSpell + * - PRCMySavingThrow + * - PRCGetHasSpell + * - PRCGetSpellLevel + * - MyFirstObjectInShape + * - MyNextObjectInShape + * - PRCGetSaveDC + * - PRCGetSaveDC + * - PRCDoResistSpell + * - PRCGetSpellResistance + * - etc. + * + */ + +/** + * Making your onhitcast spells compatible with PRC onhitcast: + * + * What you have to do: + * i) insert PRC wrappers into the impact spell script + * ii) route the impact script through prc_onhitcast + * iii) register your onhitcast spell in the (two) 2das + * iv) provide a way to place your onhitcast spells on the item + * v) provide a way to retrieve the caster level, save dc, etc. from the item + * + * How do you do it? + * + * i) insert PRC wrappers into the impact spell script + * see the isntructions for instant spell casting + * + * ii) route the impact script through prc_onhitcast + * [do this only for onhitcast spells! Not for "normal" spells!] + * + * if you want to convert a "normal" spell to an onhitcast spell, + * it is strongly advised to make a new spell script + * + * In order to circumvent the bioware bug, according to + * which only the first onhitcast spell on an item is executed, + * we must route all onhitcast spells through prc_onhitcast. + * + * This is done very conveniently by placing the following code + * into the body of the main function of your onhitcast impact spell script: + * + * if(!ContinueOnHitCastSpell()) return; + * + * Put this at the very beginning of the main() + * See "x2_s3_flamingd" for an example + * + * iii) register your spell in the 2das + * [if your spell is operational, you probably have done this before] + * + * add a line in the two 2da files + * - spells.2da + * - iprp_onhitspell.2da + * look up some onhitcast spells in the 2das to see what must be done + * see the instructions under iv) for further explanation + * + * iv) provide a way to place your onhitcast spells on the item + * [if your spell is operational you might have done this before] + * + * usually the placement of an onhitcast spell on an item is done by a "normal" spell. + * For instance the "normal" Flame Weapon spell will put a special onhitcast + * item property on the weapon you targeted with the "normal" spell. The item property + * has an integer valued subtye, that defines what spell is to be called on a hit. + * See "x2_s0_enhweap" for an example how to put the onhitcast spell item property + * on an item. + * + * How does the engine find the spell, that it is supposed to call on a hit, + * from the item property? + * + * The integer valued item property subtype specifies a line in iprp_onhitspell.2da. + * For the flame weapon spell it is line # 124. The column "SpellIndex" in + * iprp_onhitspell.2da points contains the spellID of the Impact Spell Script. This + * is the spell script that will be executed on a hit. For the flame weapon spell it is # 696. +* That number specifies a line in spells.2da. This line contains all information about + * the spell, in particular the name of the spell script that should be executed to actually + * apply the spell's onhit effects. You find the script's name in the column "ImpactScript". + * [Note that the flame weapon spell itself occupies a different line # 542 in spells.2da] + * The impact script for the "onhit" part of the flame weapon spell, as can be read + * off from the column "ImpactScript" on line #696 of spells.2da is "X2_S3_FlamingD" + * This is the string we must pass to ExecuteScript() in order to apply the onhitcast spell. + * [Note that the script name does not distinguish between lower and higher case letters] + * + * v) provide a way to retrieve the caster level, save dc, etc. from the item + * A "problem" in onhit casting is, that the item from which a spell is cast, must not + * necessarily be in the possession of the caster, who put the spell on the item. + * It might even be that the caster is not there any more (dead, exited the module) + * Therefore we must provide a way to retrieve important information about the + * spell (such as caster level, save DC, metamagics, etc.) from the item. This can + * be done (in a more or less standard way) via item properties, or (in a non-standard) + * way by storing the required information in local ints / objects attached to the item. + * If you place the relevant information as item properties on the item, you can retrieve + * the information with the standard PRC wrappers PRCGetCasterLevel, PRCGetMetaMagicFeat etc. + * in the impact spell script. If you place the relevant information in local ints / objects + * stored on the item, you must set up your impact spell script in a "non-standard" way, + * so that it retrieves the necessary information via the local variables on the item. + * The second route has been used for the flame weapon and darkfire impact spell + * scripts (x2_s3_flamingd, x2_s3_darkfire). The first route might be preferrable + * when you want to put a "normal" already existing spell on an item and don't want + * to modify the impact spell script extensively. +*/ + +////////////////////////////////////////////////// +/* Function Prototypes */ +////////////////////////////////////////////////// + +/** + * ExecuteSpellScript: + * + * instantly executes the spell script sScript via ExecuteScript in the context of oCaster + * (meaning that oCaster is considered to be the caster) + * does not perform any checks, whether oCaster can actually cast the spell + * [You can do the checks in the spell script, for instance by calling X2PreSpellCastCode] + * + * Remark motu99: + * no prespell code, no caster checks: This appears to be the general behavior of most onhitcast spells: + * As far as I could tell, the onhitcast spells on weapon and armor generally do not call X2PreSpellCastCode + * and - of course - the caster (or rather the item possessor) must not necessarily have to ability to cast spells + * (a fighter can use a weapon with "flame weapon" on hit, although it was not *he* who originally + * put the spell on the weapon) + * + * ExecuteSpellScript sets local override ints/objects/locations on oCaster just before execution + * and deletes them immediately after execution. + * Overrides are generally required, so that the PRC-wrapper functions in the spell scripts can determine + * - the target oTarget of the spell (accessed in the spell script via PRCGetSpellTargetObject), + * - the location lLocation for an AoE spell (accessed in the spell script via PRCGetSpellTargetLocation) + * - the metamagic feat of the spell nMetamagic (accessed in the spell script via PRCGetMetaMagicFeat) + * - the casterlevel nCasterLevel (accessed in the spell script via PRCGetCasterLevel) + * - the spell cast item oItem (accessed in the spell script via PRCGetSpellCastItem) + * - etc. + * + * If default values for the parameters oTarget, nMetaMagic, nCasterLevel, oItem etc. are passed to ExecuteSpellScript, + * it does not set any overrides for the PRC-wrapper functions. In this case you have to rely on the "standard" logic in + * the PRC wrapper functions to properly determine the correct values. + * The standard logic generally works fine for nCasterLevel, but might not work as expected for oTarget, nMetaMagic + * and oItem - depending from where you call ExecuteSpellScript. + * If you call ExecuteSpellScript from *within a spell script* (so that the PRC - or Bioware's - initialization + * codes had a chance to set up things nicely) then the spell "information" functions PRCGetSpellTargetObject, + * PRCGetMetaMagicFeat etc. will most likely return sensible values. (even the Bioware functions might return useful info) + * However, if you call ExecuteSpellScript *outside of a spell script*, nobody will have done any setup for you. + * In that case you are strongly advised to setup things manually, e.g. determine oTarget, nMetaMagic, nCasterLevel, + * oItem, etc. on your own and pass them to ExecuteSpellScript without relying on the "standard" logic to do the guessing for you. + * + * In principle ExecuteSpellScript(), in combination with the PRC-wrappers, is the only functions you really need for instantaneous spell casting. + * Most other functions, such as CastSpellAtObject, CastSpellAtLocation are provided as a convenience. + * They mimic the behavior of Bioware's ActionCastSpell* commands. They all eventually call ExecuteSpellScript + */ +void ExecuteSpellScript(string sScript, location lTargetLocation, object oTarget = OBJECT_INVALID, int nSpellID = 0, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +/** + * CastSpellAtObject: + * + * similar to Bioware's ActionCastSpellAtObject + * + * Will instantaneously cast the spell with index iSpellNr at oTarget + * The spell is not inserted into the action queue of oCaster, no checks whether oCaster can actually cast the spell + * oTarget is the spell's target, oItem (if required) the item from which the spell is cast, oCaster (or OBJECT_SELF) is considered to be the caster + * tested for flame weapon and darkfire impact spell scripts, but should eventually work for all spells (see below) + * + * in order to work, the spell scripts associated with iSpellNr (via spells.2da) must use the PRC-wrapper functions!!! + * Besides the established wrappers (PRCGetSpellTargetObject, PRCGetCasterLevel, PRCGetMetaMagicFeat, PRCGetSpellId etc.) + * we need a new PRC-wrapper function to replace SpellCastItem(). Not surprising the wrapper is named PRCGetSpellCastItem() + * It would be a good idea for any spell coder, who wants to remain compatible with the PRC, to check his spell scripts, + * whether all calls to Bioware's spell information functions have been replaced by the respective PRC wrapper functions. + * [As far as I could tell, this is not done consistently] + * + * If default values for oTarget, nMetaMagic, nCasterLevel and oItem are supplied, no override variables are set + * In this case the PRC-wrapper functions (or Bioware's original functions) must determine the SpellTargetObject, + * the SpellCastItem etc. through their "standard" logic. This might or might not work. + * For more information see the description in ExecuteSpellScript() + */ +void CastSpellAtObject(int nSpellID, object oTarget = OBJECT_INVALID, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF); + +// instantaneously casts an area of effect spell at location lTargetLocation +// works similar to ActionCastSpellAtLocation, but casts the spell instantly +// See the description of CastSpellAtObject, how instant spell casting work +void CastSpellAtLocation(int nSpellID, location lTargetLocation, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF); + +// applies the onhitcast spell with subtype iSubType (situated on oItem ) to oTarget +// will look up the spell script that must be executed through "iprp_onhitspell.2da" and "spells.2da" +// if default values for oTarget, oItem are supplied, no override variables are set +// for more information see the description in ExecuteSpellScript() +void ApplyOnHitCastSpellSubType(int iSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// applies the onhitcast spell darkdire (situated on oItem) to oTarget +// will look up the spell script that must be executed through "iprp_onhitspell.2da" and "spells.2da" +// if default values for oTarget, oItem are supplied, no override variables are set +// for more information see the description in ExecuteSpellScript() +void ApplyOnHitDarkfire(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// applies the onhitcast spell flame weapon (situated on oItem) to oTarget +// will look up the spell script that must be executed through "iprp_onhitspell.2da" and "spells.2da" +// if default values for oTarget, oItem are supplied, no override variables are set +// for more information see the description in ExecuteSpellScript() +void ApplyOnHitFlameWeapon(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// applies the onhitcast spell unique power (situated on oItem) to oTarget +// actually this will call the script "prc_onhitcast" (hardcoded), e.g. it will *not* look up the 2das (as the other functions do) +// if default values for oTarget, oItem are supplied, no override variables are set +// for more information see the description in ExecuteSpellScript() +void ApplyOnHitUniquePower(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// applies all on hit cast spells on oItem to oTarget +// will look up the spell scripts that must be executed in "iprp_onhitspell.2da" and "spells.2da" +// Uses a "safe" to cycle through the item properties, without interfering with any other loops +// over item properties (that for instance could be initiated in the spell scripts) +// Always use this function to cycle through and execute several onhitcast spells on an item!! +// If default values for oTarget, oItem are supplied, no override variables are set +// for more information see the description in ExecuteSpellScript() +void ApplyAllOnHitCastSpellsOnItem(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// applies all on hit cast spells on oItem, excluding any spell-subtype given in iExcludeSubType, to oTarget +// will look up the spell script that must be executed in "iprp_onhitspell.2da" and "spells.2da" +// Uses a "safe" to cycle through the item properties, without interfering with any other loops +// over item properties (that for instance could be initiated in the spell scripts) +// Always use this function to cycle through and execute several onhitcast spells on an item!! +// if default values for oTarget, oItem are supplied, no override variables are set +// for more information see the description in ExecuteSpellScript() +void ApplyAllOnHitCastSpellsOnItemExcludingSubType(int iExcludeSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// returns True, if oItem has at least one onhitcast spell on it (can be any subtype) +int GetHasOnHitCastSpell(object oItem); + +// returns True, if the oItem has an onhitcast spell with the given subtype +int GetHasOnHitCastSpellSubType(int iSubType, object oItem); + +// returns True, if we have the onhit flame weapon spell on oItem +int GetHasOnHitFlameWeapon(object oItem); + +// returns True, if we have the onhit darkfire spell on oItem +int GetHasOnHitDarkfire(object oItem); + +// returns True, if we have the onhit unique power spell on oItem +int GetHasOnHitUniquePower(object oItem); + +// returns true, when prc_onhitcast is running, e.g. the spell script is called from there +int GetIsPrcOnhitcastRunning(object oSpellOrigin = OBJECT_SELF); + +// to be used ONLY in "pure" on-hit cast spell scripts, in order to route all +// onhitcast spells through the unique power script (prc_onhitcast) +// This function should be placed at the beginning of any pure onhitcast spell script +// If the onhitcast spell script contains a call to X2PreSpellCastCode(), replace that call with a call to ContinueOnHitCastSpell() +// This code needs to be in the spell script, in order to neutralize a Biobug +// (Bioware only executes the first onhitcast spell found on an item) +// Checks the local int "prc_ohc"; if it does not find the int, it assumes the spell script was called +// directly by the aurora engine. In that case it will call prc_onhitcast and return FALSE, +// FALSE meaning that the spell script should be aborted +// if the function returns TRUE; the spell script was called from prc_onhitcast. In this case prc_onhitcast +// guarantees the execution of all onhitcast spells on an item, so the spell script should continue to run +/** + * Remark motu99: + * The behavior of ContinueOnHitCastSpell is very similar to that of X2PreSpellCastCode() + * In fact, it should replace any instances of X2PreSpellCastCode() for "pure" onhitcast spells. + * + * For spells that are not exclusively onhitcast spells (e.g. normal spells that some mages + * can put on a weapon), we should not use ContinueOnHitCastSpell, but rather call X2PreSpellCastCode. + * In this case X2PreSpellCastCode (in x2_inc_spellhook) will have to check, whether the spell script was + * called via an onhitcast event connected to a successful physical attack and route to prc_onhitcast in such a case + * (Such a general check from X2PreSpellCastCode is not 100% reliable, because of Biowares buggy implementation. + * However, the function GetIsOnHitCastSpell() provided here will do a very good job at guessing.) + * + * For pure onhitcast spells it is safer and much more efficient to use ContinueOnHitCastSpell instead + * of X2PreSpellCastCode. + */ +int ContinueOnHitCastSpell(object oSpellOrigin = OBJECT_SELF); + +// Checks, whether the spell just running is an onhitcast spell, cast from an item +// Function is to be used in X2PreSpellCastCode() for any onhitcast specific modifications +// (its main use is to fix the Bioware bug, that runs only the first onhitcast spell on a weapon or armor) +// The check is not 100% reliable, because Bioware does not delete the spellcastitem after a spell code has finished. +// So if GetSpellCastItem returns a valid item, we still don't know if we are running an onhitcast event, +// even if the item is a weapon or armor. The item could be an old item used in a previous spell script. +// (for instance, sequencer's robes count as armor) +// In order to find out, whether the spell was actually cast from a weapon/armor in a physical attack action +// we check whether the current action is ACTION_ATTACKOBJECT and whether the attempted attack +// target is equal to the spell's target +int GetIsOnHitCastSpell(object oSpellTarget = OBJECT_INVALID, object oSpellCastItem = OBJECT_SELF, object oSpellOrigin = OBJECT_SELF); + +// this will force the instantaneous execution of any onhitcast spell script, even if it is set up to be +// routed through prc_onhitcast (for more info on forced execution look at the code of ContinueOnHitCastSpell) +// For more info on functionality, see the description/code of ExecuteSpellScript() +void ForceExecuteSpellScript(string sScript, location lTargetLocation, object oTarget = OBJECT_INVALID, int nSpellID = 0, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// this will force the instantaneous execution of any onhitcast spell script with iSpellNr, as given in spells.2da +// The spell script will be executed, even if it has been set up to be routed through prc_onhitcast +// (for more info on forced execution look at the code of ContinueOnHitCastSpell) +// for more info on functionality, see the description/code of CastSpellAtObject +void ForceCastSpellAtObject(int nSpellID, object oTarget = OBJECT_INVALID, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF); +void ForceCastSpellAtLocation(int nSpellID, location lTargetLocation, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF); + +// forces the instantaneous application of the onhitcast spell with subtype iSubType (situated on oItem) to oTarget +// will force apply the spell scripts even if they have internally been set up to be routed through prc_onhitcast +// (for more info on forced execution look at the code of ContinueOnHitCastSpell) +// for more info on functionality, see the description/code of ApplyOnHitCastSpellSubType +void ForceApplyOnHitCastSpellSubType(int iSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// instantaneously applies all on hit cast spells on oItem to oTarget +// will force apply the spell scripts even if they have internally been set up to be routed through prc_onhitcast +// (for more info on forced execution look at the code of ContinueOnHitCastSpell) +// for more info on functionality, see the description/code of ApplyAllOnHitCastSpellsOnItem +// This is the safe way to loop through the item properties, without interfering with any other loops over item properties in the spell scripts +void ForceApplyAllOnHitCastSpellsOnItem(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +// most likely we will never need the following function: +// instantaneously applies all on hit cast spells on oItem, excluding any spell-subtype given in iExcludeSubType, to oTarget +// will force apply the spell scripts even if they have internally been set up to be routed through prc_onhitcast +// (for more info on forced execution look at the code of ContinueOnHitCastSpell) +// for more info on functionality, see the description/code of ApplyAllOnHitCastSpellsOnItemExcludingSubType +// Uses a "safe" way to loop through the item properties, without interfering with any other loops over item properties in the spell scripts +void ForceApplyAllOnHitCastSpellsOnItemExcludingSubType(int iExcludeSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_abil_damage" + + +////////////////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////////////////// + +/** + * ExecuteSpellScript + * executes the spell script sScript via ExecuteScript in the context of oCaster + * sets local override ints/objects on oCaster before execution (and deletes them immediately after execution) + * oTarget, nMetaMagic, nCasterLevel, oItem, nSpellID, nCasterClass, lTargetLocation + * can be accessed in the spell script via PRC-wrappers + * + * known caveats: + * Using calls to PRCGetMetaMagicFeat() in onhitcast spells can be dangerous, because PRCGetMetaMagicFeat() + * will cycle through the item properties in order to find out whether there are any properties + * of the type ITEM_PROPERTY_CAST_SPELL_METAMAGIC. This is problematic, because prc_onhitcast must also + * cycle through the item properties, in order to find out what onhitcast spells are on the item. + * If we are not careful, we might find ourselves with two nested loops over item properties. But the current implementation + * of GetFirstItemProperty and GetNextItemProperty does not allow nested loops. Nested loops generally result + * in unpredictable behavior (such as infinitely returning the same item property on GetNextItemProperty) + * + * Remark motu99: + * The above described problem ALWAYS occurs when there are nested loops over item properties or effects. + * Therefore generally it is a bad idea to call anything complicated (e.g. a spell script!) within a loop over item properties + * or effects, in particular if you don't know every detail of the so called function. If the called function contains just + * one single call to GetFirst* or GetNext* (this could happen deep in the function's code - possibly in a utility function + * several calling levels deeper), this will mess up your next call to GetNext*. + * I provided a "safe" way to cycle through all onhitcast spells on an item: ApplyAllOnHitCastSpellsOnItem + * + * known limitations: + * some spell scripts need the cast class (e.g. did we cast the spell as a cleric, a wizard, a druid etc.) + * in instantaneous casting we do not select the spell from a spellbook (which is always tied to a certain class), so that the aurora + * engine cannot set a sensible value for GetLastSpellCastClass(). Thus, if the spell script needs it, we must determine the cast + * class ourselves, pass it to ExecuteSpellScript and make sure we call the PRC wrapper PRCGetLastSpellCastClass in the spell script + * + * In the current implementation the spell's save DC is not calculated correctly. It generally returns the minimum DC. + * The reason is, that PRCGetSaveDC (in prc_add_spell_dc.nss) has not yet been updated to handle instant spell casting. + * In particular, PRCGetSaveDC calls Biowares GetSpellDC(), which returns the minimum DC when called *outside* a + * spell script. As instant spellcasting is usually called from outside a spell script, we get only get the minimum DC. + * @TODO: In order to determine the correct DC, we need to know the casting class [this will anable us to select the + * correct ability adjustments - for instance INT for Wizards, CHA for sorcerers, etc.]. So whenever there is a + * cast class override, we should not call Bioware's GetSpellDC, but rather calculate the DC directly. + * + * some spell scripts need the spell ID. It is automatically set by CastSpellAtObject or CastSpellAtLocation. We can set it in + * ExecuteSpellScript as well. + * + * some spell scripts make calls to GetLastSpell(). We might need a PRC wrapper to catch those. + * In any case, PRCGetSpellId will give us the correct spell ID. GetLastSpell seems to be used in OnSpellCastAt events + * From the NWNLexicon: GetLastSpell is for use in OnSpellCastAt script, it gets the ID (SPELL_*) of the spell that was cast. + * It will not return the true spell value, but whatever value is put into the EventSpellCastAt() - therefore, sub-dial spells like + * Polymorph will mearly return SPELL_POLYMORPH not the sub spell number + * + * some spell scripts make calls to GetLastSpellCaster(). In a spell script OBJECT_SELF should be the caster. Its not clear + * what a call to GetLastSpellCaster() will return. Might have to look into this + * + */ +void ExecuteSpellScript(string sScript, location lTargetLocation, object oTarget = OBJECT_INVALID, int nSpellID = 0, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + if(DEBUG) DoDebug("ExecuteSpellScript: executing script "+sScript); + + // create an invalid location by not assigning anything to it (hope this works) + location lInvalid; + + // tell the impact spell script where the target area is + if (lTargetLocation != lInvalid) + { + SetLocalInt(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE, TRUE); + SetLocalLocation(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE, lTargetLocation); + } + + // tell the impact spell script who the target is + if (oTarget != OBJECT_INVALID) + SetLocalObject(oCaster, PRC_SPELL_TARGET_OBJECT_OVERRIDE, oTarget); + + // tell the impact spell script what the spell cast item is + if (oItem != OBJECT_SELF) + { +//DoDebug("ExecuteSpellScript: setting override spell cast item = "+GetName(oItem)); + if (oItem == OBJECT_INVALID) + SetLocalObject(oCaster, PRC_SPELLCASTITEM_OVERRIDE, oCaster); + else + SetLocalObject(oCaster, PRC_SPELLCASTITEM_OVERRIDE, oItem); + } + + // override the caster level, but only if necessary + if (nCasterLevel) + SetLocalInt(oCaster, PRC_CASTERLEVEL_OVERRIDE, nCasterLevel); + + // tell the impact spell script the metamagic we want to use + if (nMetaMagic != METAMAGIC_ANY) + SetLocalInt(oCaster, PRC_METAMAGIC_OVERRIDE, nMetaMagic); + + // tell the impact spell script the spellID we want to use + // check for zero is problematic becauseSPELL_ACID_FOG == 0 + if (nSpellID) + SetLocalInt(oCaster, PRC_SPELLID_OVERRIDE, nSpellID); + + // tell the impact spell script the caster class we want to use + // check for zero is problematic, because CLASS_TYPE_BARBARIAN == 0 + // But Barbarians cannot cast spells (UMD? SPA?), so this should work + if (nCasterClass) + SetLocalInt(oCaster, PRC_CASTERCLASS_OVERRIDE, nCasterClass); + + if (nSaveDC) + SetLocalInt(oCaster, PRC_SAVEDC_OVERRIDE, nSaveDC); + + // execute the impact spell script in the context of oCaster + ExecuteScript(sScript, oCaster); + +/** + * motu99: If we were paranoid, we could delete all local ints and objects, regardless if we set them beforehand, + * as a "countermeasure" against any future scripter, who might disregard the given advise, set the overrides manually + * and then forget to delete them. + * However, as long as scripters follow the advise, and use ExecuteSpellScript and the other functions provided here, + * never manually setting the overrides, we can be sure, that these variables are not misused. + * In the unlikely case that somebody did not play by the rules, and managed to break "normal" spell casting by improperly + * using the overrides, and we cannot locate where and what he did, we might have to resort to the last measure, deleting + * all overrides on every call to ExecuteSpellScript() and ExecuteAoESpellScript() + * If we delete the overrides indiscriminately, we MUST ALWAYS call ExecuteSpellScript with NON-DEFAULT values. + * Otherwise we are in trouble when we do multiple calls to ExecuteScript from a loop: We might accidentally delete + * any overrides that we set on some previous call to ExecuteSpellScript. At that point we are at the mercy of + * Bioware's functions to provide the correct values, which does not always work (to put it mildly) + * Note that some measure of safety is provided through the fact, that we set the overrides on oCaster, and not on the module +*/ + // cleanup (we only delete those locals that we set before) + if (lTargetLocation != lInvalid) + { + DeleteLocalInt(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE); + // DeleteLocalLocation(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE); + } + + if (oItem != OBJECT_SELF) + DeleteLocalObject(oCaster, PRC_SPELLCASTITEM_OVERRIDE); + + if (oTarget != OBJECT_INVALID) + DeleteLocalObject(oCaster, PRC_SPELL_TARGET_OBJECT_OVERRIDE); + + if (nMetaMagic != METAMAGIC_ANY) + DeleteLocalInt(oCaster, PRC_METAMAGIC_OVERRIDE); + + if (nCasterLevel) + DeleteLocalInt(oCaster, PRC_CASTERLEVEL_OVERRIDE); + + if (nSpellID) + DeleteLocalInt(oCaster, PRC_SPELLID_OVERRIDE); + + if (nCasterClass) + DeleteLocalInt(oCaster, PRC_CASTERCLASS_OVERRIDE); + + if (nSaveDC) + DeleteLocalInt(oCaster, PRC_SAVEDC_OVERRIDE); + + if (DEBUG) DoDebug("ExecuteSpellScript: done executing script "+sScript); +} + +// ExecuteSpellScript will set PRC_SPELLID_OVERRIDE, so that we know what spell we are casting +// However, SPELL_ACID_FOG has a SpellID of zero, so we might have problems with this particular spell +void CastSpellAtObject(int nSpellID, object oTarget = OBJECT_INVALID, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF) +{ + // get the name of the impact spell script (for ExecuteScript) + // nSpellID points to a row in spells.2da + string sScript = Get2DACache("spells", "ImpactScript", nSpellID); + + if(sScript == "" || sScript == "****") + return; + + // create an invalid location + location lInvalid; + + // execute the spell script + ExecuteSpellScript(sScript, lInvalid, oTarget, nSpellID, nMetaMagic, nCasterLevel, nCasterClass, nSaveDC, oItem, oCaster); +} + +// works similar to ActionCastSpellAtLocation, only casts spell instantly (without saving throw etc. +// See description of CastSpellAtObject, how instant spells work +void CastSpellAtLocation(int nSpellID, location lTargetLocation, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF) +{ + // get the name of the impact spell script (for ExecuteScript) + string sScript = Get2DACache("spells", "ImpactScript", nSpellID); + + if(sScript == "" || sScript == "****") + return; + + // execute the spell script + ExecuteSpellScript(sScript, lTargetLocation, OBJECT_INVALID, nSpellID, nMetaMagic, nCasterLevel, nCasterClass, nSaveDC, oItem, oCaster); +} + +void ApplyOnHitCastSpellSubType(int iSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + // get the spellID of the onhitspell + int iSpellID = StringToInt( Get2DACache("iprp_onhitspell", "SpellIndex", iSubType) ); + + // now execute the impact spell script + CastSpellAtObject(iSpellID, oTarget, METAMAGIC_ANY, 0, 0, 0, oItem, oCaster); +} + +void ApplyOnHitDarkfire(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + ApplyOnHitCastSpellSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_DARKFIRE, oTarget, oItem, oCaster); +} + +void ApplyOnHitFlameWeapon(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + ApplyOnHitCastSpellSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_FIREDAMAGE, oTarget, oItem, oCaster); +} + +void ApplyOnHitUniquePower(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + location lInvalid; + // ApplyOnHitCastSpellSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, oTarget, METAMAGIC_ANY, 0, oItem, oCaster); + // unique power hardwired to "prc_onhitcast". If we want to go through the 2das, we should revert to the commented out line of code above + ExecuteSpellScript("prc_onhitcast", lInvalid, oTarget, 0, METAMAGIC_ANY, 0, 0, 0, oItem, oCaster); +} + +void ApplyAllOnHitCastSpellsOnItem(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + int iSubType; + int iNr = 0; + if(!array_exists(oCaster, "ohspl")) + array_create(oCaster, "ohspl"); + + // remember the item that was passed to the function (in case it is invalid we simply pass it through to the function that does the spells) + object oItemPassed = oItem; + + // we need an item in order to cycle over item properties + // if OBJECT_INVALID was given, we must use the "standard" logic in PRCGetSpellCastItem to determine the item + if (oItem == OBJECT_SELF) + oItem = PRCGetSpellCastItem(oCaster); + + itemproperty ip = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ip)) + { +// DoDebug("ApplyAllOnHitCastSpellsExcludingSubType: found " + DebugIProp2Str(ip)); + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_ONHITCASTSPELL) + { + // retrieve the spell ID + iSubType = GetItemPropertySubType(ip); + // we found a new onhit spell, so increment iNr + iNr++; + // store the spell ID in an array and execute the spell later, this is safer than trying to execute the spell script directly + // lets hope that nobody else uses our name "ohspl" for the array + array_set_int(oCaster, "ohspl", iNr, iSubType); + } + ip = GetNextItemProperty(oItem); + } + + // now execute the spell scripts (note that the local array will not be deleted) + while (iNr) + { + iSubType = array_get_int(oCaster, "ohspl", iNr); +//DoDebug("ApplyAllOnHitCastSpellsExcludingSubType: executing onhitcastspell subtype # " + IntToString(iSubType)); + ApplyOnHitCastSpellSubType(iSubType, oTarget, oItemPassed, oCaster); + iNr--; + } + array_delete(oCaster, "ohspl"); +} + +void ApplyAllOnHitCastSpellsOnItemExcludingSubType(int iExcludeSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + int iSubType; + int iNr = 0; + if(!array_exists(oCaster, "ohspl")) + array_create(oCaster, "ohspl"); + + // remember the item that was passed to the function (in case it is invalid we pass it through to the spell cast functions) + object oItemPassed = oItem; + + if (oItem == OBJECT_SELF) + { + oItem = PRCGetSpellCastItem(oCaster); +//DoDebug("ApplyAllOnHitCastSpellsOnItemExcludingSubType: item = OBJECT_SELF, determining item by standard logic, item = " +GetName(oItem)); + } + + itemproperty ip = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ip)) + { +// DoDebug("ApplyAllOnHitCastSpellsExcludingSubType: found " + DebugIProp2Str(ip)); + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_ONHITCASTSPELL) + { + iSubType = GetItemPropertySubType(ip); + if(iSubType == iExcludeSubType) // if(iSubType ==IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER) // == 125 + { + ip = GetNextItemProperty(oItem); + continue; //skip over OnHit:CastSpell:UniquePower otherwise it would TMI. + } + // we found a new onhit spell, so increment iNr + iNr++; + // store the spell ID in an array and execute the spell later, this is safer than trying to execute the spell script directly + // lets hope that nobody else uses our name "ohspl" for the array + array_set_int(oCaster, "ohspl", iNr, iSubType); + } + ip = GetNextItemProperty(oItem); + } + + // now execute the spell scripts (note that the local array will not be deleted) + while (iNr) + { + iSubType = array_get_int(oCaster, "ohspl", iNr); +//DoDebug("ApplyAllOnHitCastSpellsExcludingSubType: executing onhitcastspell subtype # " + IntToString(iSubType)); + ApplyOnHitCastSpellSubType(iSubType, oTarget, oItemPassed, oCaster); + iNr--; + } + array_delete(oCaster, "ohspl"); +} + +// returns true, if oItem has at least one onhitcast spell (of any subtype) +int GetHasOnHitCastSpell(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_ONHITCASTSPELL) + { + return TRUE; + } + ip = GetNextItemProperty(oItem); + } + return FALSE; +} + +// returns true, if oItem has an onhitcast spell with the given subtype +int GetHasOnHitCastSpellSubType(int iSubType, object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ip)) + { + + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_ONHITCASTSPELL + && GetItemPropertySubType(ip) == iSubType) + { + return TRUE; + } + ip = GetNextItemProperty(oItem); + } + return FALSE; +} + +// returns True, if we have the onhit flame weapon spell on the item +int GetHasOnHitFlameWeapon(object oItem) +{ + return GetHasOnHitCastSpellSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_FIREDAMAGE, oItem); +} + +// returns True, if we have the onhit darkfire spell on the item +int GetHasOnHitDarkfire(object oItem) +{ + return GetHasOnHitCastSpellSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_DARKFIRE, oItem); +} + +// returns True, if we have the onhit unique power spell on the item +int GetHasOnHitUniquePower(object oItem) +{ + return GetHasOnHitCastSpellSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, oItem); +} + + +int GetIsPrcOnhitcastRunning(object oSpellOrigin = OBJECT_SELF) +{ + return GetLocalInt(oSpellOrigin, "prc_ohc"); +} + + +// to be used only in on-hit cast spell scripts, in order to route the spell through the unique power script (prc_onhitcast) +int ContinueOnHitCastSpell(object oSpellOrigin = OBJECT_SELF) +{ + // if the local int "frc_ohc" is on, then we never route the spell through prc_onhitcast + // rather we continue the execution of the onhitcast script; so return True in this case + if (GetLocalInt(oSpellOrigin, "frc_ohc")) + { + // signal to caller that it can continue execution (no rerouting to prc_onhitcast) + return TRUE; + } + + // now check whether we were called from prc_onhitcast + int bCalledByPRC = GetIsPrcOnhitcastRunning(oSpellOrigin); + + // if not, call prc_onhitcast + if (!bCalledByPRC) + { + if (DEBUG) DoDebug("onhitcast spell script not called through prc_onhitcast - now routing to prc_onhitcast"); + ExecuteScript("prc_onhitcast", oSpellOrigin); + } + // signal to calling function, whether it should terminate execution, because we rerouted the call (e.g. bCalledByPRC=FALSE) + // or whether it should continue execution, because we were called from prc_onhitcast (e.g. bCalledByPRC=TRUE) + return bCalledByPRC; +} + + +// Checks, whether the spell just running is an onhitcast spell, cast from an item (weapon or armor) during an attack action +// Due to Biowares buggy implementation the check is not 100% reliable +int GetIsOnHitCastSpell(object oSpellTarget = OBJECT_INVALID, object oSpellCastItem = OBJECT_SELF, object oSpellOrigin = OBJECT_SELF) +{ + // determine spell cast item (if not already given) + if (oSpellCastItem == OBJECT_SELF) + oSpellCastItem = PRCGetSpellCastItem(oSpellOrigin); + + // spell cast item must be valid (but it could still be an item from a previous item spell) + if (!GetIsObjectValid(oSpellCastItem)) + { + if (DEBUG) DoDebug("GetIsOnHitCastSpell: no valid spell cast item, returning FALSE"); + return FALSE; + } + + // onhitcasting affects weapons and armor; find out which + int iBaseType = GetBaseItemType(oSpellCastItem); + + object oAttacker; + object oDefender; + int iWeaponType; + + // is the spellcast item an armor? + // Then it could be an onhitcast event from the armor of the defender + // the aurora engine executes the onhitcast spell in the context of the bearer of the armor, e.g. the defender + if (iBaseType == BASE_ITEM_ARMOR) + { + + // find the target, if not already given + if (oSpellTarget == OBJECT_INVALID) + oSpellTarget = PRCGetSpellTargetObject(oSpellOrigin); + + // the spell target of the armor onhitspell is the melee attacker (e.g. oSpellTarget = melee attacker). + // The aurora engine exeutes the onhit spell in the context of the melee defender (e.g. oSpellOrigin = melee defender) + oAttacker = oSpellTarget; + oDefender = oSpellOrigin; + + if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is armor; attacker = "+GetName(oAttacker)+", defender = "+GetName(oDefender)); + } + // is the spell type item a weapon? + else if (iWeaponType == StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType))) + { + // determine the target, if not already given + if (oSpellTarget == OBJECT_INVALID) + oSpellTarget = PRCGetSpellTargetObject(oSpellOrigin); + + oAttacker = oSpellOrigin; + oDefender = oSpellTarget; + + if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is weapon [#"+IntToString(iWeaponType)+"]; attacker = "+GetName(oAttacker)+", defender = "+GetName(oDefender)); + } + else + { + if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is neither weapon nor armor; returning FALSE"); + return FALSE; + } + + + // the spell origin must possess the item that cast the spell (at least for the aurora engine, in prc_inc_combat that may differ) + if (GetItemPossessor(oSpellCastItem) != oSpellOrigin) + { + if (DEBUG) DoDebug("GetIsOnHitCastSpell: the spell cast item is not in the possession of the creature in which context the spell is executed"); + return FALSE; + } + + // the attacker must be physically attacking (only then onhitcasting makes sense) + // however, Bioware seems to set the action to ACTION_INVALID during the spell script + int iAction = GetCurrentAction(oAttacker); + if (DEBUG) DoDebug("GetIsOnHitCastSpell: current action of attacker = "+IntToString(GetCurrentAction(oAttacker))); + + if (iAction != ACTION_INVALID && iAction != ACTION_ATTACKOBJECT) + { + if (DEBUG) DoDebug("GetIsOnHitCastSpell: current action of attacker = "+IntToString(GetCurrentAction(oAttacker))+" is not compatible with onhitcasting, return FALSE"); + return FALSE; + } + + // the attacker should actually be (melee) attacking the defender + if (GetAttackTarget(oAttacker) != oDefender) + { + if (DEBUG) DoDebug("GetIsOnHitCastSpell: melee attacker of onhitcast spell is not attacking the melee defender, return FALSE"); + return FALSE; + } + + // if we made it here, it is an onhitcast spell + return TRUE; +} + +// this will force the execution of any onhitcast spell script, even if it is set up to be +// routed through prc_onhitcast (see the description for the function ContinueOnHitCastSpell) +void ForceExecuteSpellScript(string sScript, location lTargetLocation, object oTarget = OBJECT_INVALID, int nSpellID = 0, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + // set the local int, that tells the spell script that we want to force its execution + SetLocalInt(oCaster, "frc_ohc", TRUE); + + // now call the spell script + ExecuteSpellScript(sScript, lTargetLocation, oTarget, nSpellID, nMetaMagic, nCasterLevel, nCasterClass, nSaveDC, oItem, oCaster); + + // delete the local int for forced execution + DeleteLocalInt(oCaster, "frc_ohc"); +} + +// this will force the instant execution of any onhitcast spell script with nSpellID, as given in spells.2da +// The spell script will be executed, even if it has been set up to be routed through prc_onhitcast +// (for more info, see the description of the function ContinueOnHitCastSpell) +void ForceCastSpellAtObject(int nSpellID, object oTarget = OBJECT_INVALID, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF) +{ + // get the name of the impact spell script (for ExecuteSpellScript) + string sScript = Get2DACache("spells", "ImpactScript", nSpellID); + + if(sScript == "" || sScript == "****") + return; + + // create an invalid location + location lInvalid; + + // now force-execute the spell script + ForceExecuteSpellScript(sScript, lInvalid, oTarget, nSpellID, nMetaMagic, nCasterLevel, nCasterClass, nSaveDC, oItem, oCaster); + +} + +// this will force the instant execution of any AoE onhitcast spell script with nSpellID, as given in spells.2da +// The spell script will be executed, even if it has been set up to be routed through prc_onhitcast +// (for more info, see the description of the function ContinueOnHitCastSpell) +void ForceCastSpellAtLocation(int nSpellID, location lTargetLocation, int nMetaMagic = METAMAGIC_ANY, int nCasterLevel = 0, int nCasterClass = 0, int nSaveDC = 0, object oItem = OBJECT_INVALID, object oCaster = OBJECT_SELF) +{ + // get the name of the impact spell script (for ExecuteSpellScript) + string sScript = Get2DACache("spells", "ImpactScript", nSpellID); + + if(sScript == "" || sScript == "****") + return; + + // now force-execute the spell script + ForceExecuteSpellScript(sScript, lTargetLocation, OBJECT_INVALID, nSpellID, nMetaMagic, nCasterLevel, nCasterClass, nSaveDC, oItem, oCaster); + +} + +// forces the application of the onhitcast spell (situated on oItem) with subtype iSubType to oTarget, in the context of oCaster +void ForceApplyOnHitCastSpellSubType(int iSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + // get the spellID of the onhitspell + int iSpellID = StringToInt( Get2DACache("iprp_onhitspell", "SpellIndex", iSubType) ); + + // now force-execute the impact spell script + ForceCastSpellAtObject(iSpellID, oTarget, METAMAGIC_ANY, 0, 0, 0, oItem, oCaster); +} + +// applies all on hit cast spells on oItem to oTarget in the context of oCaster +// will force apply the spell scripts even if they have internally been set up to be routed through prc_onhitcast +// (for more info, see the description of the function ContinueOnHitCastSpell) +// This is the safe way to do this, without interfering with any other loops over item properties in the spell scripts that are called +void ForceApplyAllOnHitCastSpellsOnItem(object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + // set the local int, that tells the spell scripts that we want to force their execution + SetLocalInt(oCaster, "frc_ohc", TRUE); + + // now apply all onhitcast spells on the item + ApplyAllOnHitCastSpellsOnItem(oTarget, oItem, oCaster); + + // delete the local int for forced execution + DeleteLocalInt(oCaster, "frc_ohc"); +} + +// applies all on hit cast spells on oItem, excluding any spell-subtype given in iExcludeSubType, to oTarget in the context of oCaster +// will force apply the spell scripts even if they have internally been set up to be routed through prc_onhitcast +// (for more info, see the description of the function ContinueOnHitCastSpell) +// This is the safe way to do this, without interfering with any other loops over item properties in the spell scripts that are called +void ForceApplyAllOnHitCastSpellsOnItemExcludingSubType(int iExcludeSubType, object oTarget = OBJECT_INVALID, object oItem = OBJECT_SELF, object oCaster = OBJECT_SELF) +{ + // set the local int, that tells the spell scripts that we want to force their execution + SetLocalInt(oCaster, "frc_ohc", TRUE); + + // now apply all onhitcast spells on the item + ApplyAllOnHitCastSpellsOnItemExcludingSubType(iExcludeSubType, oTarget, oItem, oCaster); + + // delete the local int for forced execution + DeleteLocalInt(oCaster, "frc_ohc"); +} + +const string PRC_CUSTOM_ONHIT_USECOUNT = "prc_custom_onhit_usecount"; +const string PRC_CUSTOM_ONHIT_DONOTREMOVE = "prc_custom_onhit_donotremove"; +const string PRC_CUSTOM_ONHIT_SIGNATURE = "prc_custom_onhit_signature_"; + +int HasOnHitUniquePowerOnHit(object oItem) +{ + int bFound = FALSE; + itemproperty iProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProperty)) + { + if (GetItemPropertyType(iProperty) == ITEM_PROPERTY_ONHITCASTSPELL && + GetItemPropertySubType(iProperty) == IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER && + GetItemPropertyDurationType(iProperty) == DURATION_TYPE_PERMANENT + ) + { + bFound = TRUE; + break; + } + iProperty = GetNextItemProperty(oItem); + } + return FALSE; +} + +void Add_OnHitUniquePower(object oItem, string sCallerSignature) +{ + int nUseCount; + itemproperty iProperty; + + if (!GetLocalInt(oItem, PRC_CUSTOM_ONHIT_SIGNATURE + sCallerSignature)) //Prevent double-add by same caller + { + nUseCount = GetLocalInt(oItem, PRC_CUSTOM_ONHIT_USECOUNT); + SetLocalInt(oItem, PRC_CUSTOM_ONHIT_USECOUNT, nUseCount+1); + if (!nUseCount && HasOnHitUniquePowerOnHit(oItem)) + SetLocalInt(oItem, PRC_CUSTOM_ONHIT_DONOTREMOVE, TRUE); + + //To work properly, all other OnHit properties must be added a after the OnHit: Unique Power property-- + //Make sure this is the case by removing all of them and adding them back properly. + //This is necessary because only the first OnHit property actually fires; + //ours will fire the others, but the others won't allow ours to fire unless ours is first. + + iProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProperty)) + { + if (GetItemPropertyType(iProperty) == ITEM_PROPERTY_ONHITCASTSPELL && + GetItemPropertyDurationType(iProperty) == DURATION_TYPE_PERMANENT + ) + { + RemoveItemProperty(oItem, iProperty); + if (GetItemPropertySubType(iProperty) != IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER) + DelayCommand(0.0, AddItemProperty(GetItemPropertyDurationType(iProperty), iProperty, oItem)); + } + iProperty = GetNextItemProperty(oItem); + } + + //Add our On Hit Cast Spell: Unique Power property (which will fire the others as well) + + iProperty = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1); + IPSafeAddItemProperty(oItem, iProperty); + + SetLocalInt(oItem, PRC_CUSTOM_ONHIT_SIGNATURE + sCallerSignature, TRUE); + } +} + +void Remove_OnHitUniquePower(object oItem, string sCallerSignature) +{ + int nUseCount; + itemproperty iProperty; + + if (GetLocalInt(oItem, PRC_CUSTOM_ONHIT_SIGNATURE + sCallerSignature)) //Prevent remove for caller that didn't add + { + nUseCount = GetLocalInt(oItem, PRC_CUSTOM_ONHIT_USECOUNT); + SetLocalInt(oItem, PRC_CUSTOM_ONHIT_USECOUNT, nUseCount-1); + if (nUseCount == 1 && !GetLocalInt(oItem, PRC_CUSTOM_ONHIT_DONOTREMOVE)) + { + iProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProperty)) + { + if (GetItemPropertyType(iProperty) == ITEM_PROPERTY_ONHITCASTSPELL && + GetItemPropertySubType(iProperty) == IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER && + GetItemPropertyDurationType(iProperty) == DURATION_TYPE_PERMANENT + ) + RemoveItemProperty(oItem, iProperty); + iProperty = GetNextItemProperty(oItem); + } + } + + SetLocalInt(oItem, PRC_CUSTOM_ONHIT_SIGNATURE + sCallerSignature, FALSE); + } +} diff --git a/src/include/prc_inc_racial.nss b/src/include/prc_inc_racial.nss new file mode 100644 index 0000000..101d794 --- /dev/null +++ b/src/include/prc_inc_racial.nss @@ -0,0 +1,103 @@ +// Useful includes for dealing with races. + +//function prototypes +//use this to get class/race adjusted racial type back to one of the bioware bases +//includes shifter changed forms +int MyPRCGetRacialType(object oCreature); + +// Guess what this does +int GetIsWarforged(object oCreature); +// DoRacialSLA() moved to prc_inc_core as it is used by other spell-like scripts, not just race specific + + +#include "prc_class_const" +#include "prc_feat_const" +#include "prc_racial_const" + +int MyPRCGetRacialType(object oCreature) +{ + // Shadow Sun Ninja - Ballance of Light and Dark + if(GetLocalInt(oCreature, "SSN_BALANCE_LD")) + return RACIAL_TYPE_UNDEAD; + if(GetHasFeat(FEAT_UNDEAD_HD, oCreature)) + return RACIAL_TYPE_UNDEAD; + + // Class-based racial type changes + if(GetLevelByClass(CLASS_TYPE_LICH,oCreature) > 3) + return RACIAL_TYPE_UNDEAD; + if(GetLevelByClass(CLASS_TYPE_MONK, oCreature) > 19 + || GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oCreature) > 9 + || GetLevelByClass(CLASS_TYPE_ACOLYTE, oCreature) > 9 + || GetLevelByClass(CLASS_TYPE_INCANDESCENT_CHAMPION, oCreature) > 9) + return RACIAL_TYPE_OUTSIDER; + if(GetLevelByClass(CLASS_TYPE_OOZEMASTER, oCreature) > 9) + return RACIAL_TYPE_OOZE; + if(GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCreature) > 9) + return RACIAL_TYPE_ELEMENTAL; + if(GetLevelByClass(CLASS_TYPE_DRAGONDISCIPLE, oCreature) > 9) + return RACIAL_TYPE_DRAGON; + if (GetLevelByClass(CLASS_TYPE_WEREWOLF, oCreature) > 9) + return RACIAL_TYPE_SHAPECHANGER; + if (GetLevelByClass(CLASS_TYPE_HEARTWARDER, oCreature) > 9) + return RACIAL_TYPE_FEY; + if (GetLevelByClass(CLASS_TYPE_FORESTMASTER, oCreature) > 7) + return RACIAL_TYPE_PLANT; + + // PRC Shifting Polymorph -caused racial type override. Stored with offset +1 to differentiate value 0 from non-existence + int nShiftingOverrideRace = GetLocalInt(oCreature, "PRC_ShiftingOverride_Race"); + if(nShiftingOverrideRace) + return nShiftingOverrideRace - 1; + + // Race pack racial type feats + if(GetHasFeat(FEAT_OUTSIDER, oCreature)) + return RACIAL_TYPE_OUTSIDER; + if(GetHasFeat(FEAT_ELEMENTAL, oCreature)) + return RACIAL_TYPE_ELEMENTAL; + if(GetHasFeat(FEAT_LIVING_CONSTRUCT, oCreature)) + return RACIAL_TYPE_CONSTRUCT; + if(GetHasFeat(FEAT_PLANT, oCreature)) + return RACIAL_TYPE_PLANT; + if(GetHasFeat(FEAT_ABERRATION, oCreature)) + return RACIAL_TYPE_ABERRATION; + if(GetHasFeat(FEAT_DRAGON, oCreature)) + return RACIAL_TYPE_DRAGON; + if(GetHasFeat(FEAT_SHAPECHANGER, oCreature)) + return RACIAL_TYPE_SHAPECHANGER; + if(GetHasFeat(FEAT_GIANT, oCreature)) + return RACIAL_TYPE_GIANT; + if(GetHasFeat(FEAT_FEY, oCreature)) + return RACIAL_TYPE_FEY; + if(GetHasFeat(FEAT_MONSTEROUS, oCreature)) + return RACIAL_TYPE_HUMANOID_MONSTROUS; + if(GetHasFeat(FEAT_BEAST, oCreature)) + return RACIAL_TYPE_BEAST; + if(GetHasFeat(FEAT_DWARVEN, oCreature)) + return RACIAL_TYPE_DWARF; + if(GetHasFeat(FEAT_ELVEN, oCreature)) + return RACIAL_TYPE_ELF; + if(GetHasFeat(FEAT_GNOMISH, oCreature)) + return RACIAL_TYPE_GNOME; + if(GetHasFeat(FEAT_HALFLING, oCreature)) + return RACIAL_TYPE_HALFLING; + if(GetHasFeat(FEAT_ORCISH, oCreature)) + return RACIAL_TYPE_HUMANOID_ORC; + if(GetHasFeat(FEAT_HUMAN, oCreature)) + return RACIAL_TYPE_HUMAN; + if(GetHasFeat(FEAT_GOBLINOID, oCreature)) + return RACIAL_TYPE_HUMANOID_GOBLINOID; + if(GetHasFeat(FEAT_REPTILIAN, oCreature)) + return RACIAL_TYPE_HUMANOID_REPTILIAN; + if(GetHasFeat(FEAT_VERMIN, oCreature)) + return RACIAL_TYPE_VERMIN; + + return GetRacialType(oCreature); +} + +int GetIsWarforged(object oCreature) +{ + int nRace = GetRacialType(oCreature); + + if (nRace == RACIAL_TYPE_WARFORGED || nRace == RACIAL_TYPE_WARFORGED_CHARGER) return TRUE; + + return FALSE; +} \ No newline at end of file diff --git a/src/include/prc_inc_s_det.nss b/src/include/prc_inc_s_det.nss new file mode 100644 index 0000000..907154e --- /dev/null +++ b/src/include/prc_inc_s_det.nss @@ -0,0 +1,496 @@ +//:://///////////////////////////////////////////// +//:: Include file for Detect Alignment spells +//:: prc_inc_s_det +//::////////////////////////////////////////////// +/** @file + +This file handles the Detect Evil/Law/Chaos/Good series of spells +If there are other detect spells, they can probably be put in here too. + + @author Primogenitor +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +#include "prc_inc_spells" +#include "prc_add_spell_dc" +//#include "inc_draw" Provided by inc_utility +//#include "inc_utility" Provided by prc_alterations + +//this is the main detection function +void DetectAlignmentRound(int nRound, location lLoc, int nGoodEvil, int nLawChaos, string sAura, int nBeamVFX); + +// Detect Magic +void DetectMagicAura(int nRound, location lLoc, int nBeamVFX, float fDist); + +const int AURA_STRENGTH_DIM = 1; +const int AURA_STRENGTH_FAINT = 2; +const int AURA_STRENGTH_MODERATE = 3; +const int AURA_STRENGTH_STRONG = 4; +const int AURA_STRENGTH_OVERWHELMING = 5; + +int GetIsUndetectable(object oTarget, object oCaster)//fix this +{ + int nUndetectable = FALSE; + if(GetHasSpellEffect(SPELL_UNDETECTABLE_ALINGMENT, oTarget) || + GetHasSpellEffect(SPELL_NONDETECTION, oTarget) || + GetLevelByClass(CLASS_TYPE_WILD_MAGE, oTarget) >= 6 || + GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oTarget) >= 5 || + GetHasSpellEffect(MELD_ENIGMA_HELM, oTarget) || + GetRacialType(oTarget) == RACIAL_TYPE_SKULK) // MELD_ENIGMA_HELM + { + int nDC = PRCGetSaveDC(OBJECT_SELF, oTarget, SPELL_UNDETECTABLE_ALINGMENT); + //if you dont beat the saving throw of the spell, you cant detect them + if(!PRCMySavingThrow(SAVING_THROW_WILL, oCaster, nDC, SAVING_THROW_TYPE_NONE)) + nUndetectable = TRUE; + } + return nUndetectable; +} + +string AlignmentAuraLocal(int nGoodEvil, int nLawChaos) +{ + if (nGoodEvil == ALIGNMENT_GOOD) return "PRC_AURA_GOOD"; + else if(nGoodEvil == ALIGNMENT_EVIL) return "PRC_AURA_EVIL"; + else if(nLawChaos == ALIGNMENT_LAWFUL) return "PRC_AURA_LAW"; + else if(nLawChaos == ALIGNMENT_CHAOTIC) return "PRC_AURA_CHAOS"; + + return ""; +} + +int GetAuraStrength(object oTarget, int nGoodEvil, int nLawChaos)//add new classes +{ + int nPower = GetLocalInt(oTarget, AlignmentAuraLocal(nGoodEvil, nLawChaos)); + + // Only creatures proceed past this point. + if (GetObjectType(oTarget) != OBJECT_TYPE_CREATURE) + return nPower; + + int nRace = MyPRCGetRacialType(oTarget); + if(nRace == RACIAL_TYPE_OUTSIDER) + nPower = GetHitDice(oTarget); + else if((nRace == RACIAL_TYPE_UNDEAD && nGoodEvil==ALIGNMENT_EVIL)//undead only for evils + || nRace == RACIAL_TYPE_ELEMENTAL) + nPower = GetHitDice(oTarget)/2; + else + nPower = GetHitDice(oTarget)/5; + if(GetPrCAdjustedCasterLevel(CLASS_TYPE_CLERIC, oTarget) > nPower) + nPower = GetPrCAdjustedCasterLevel(CLASS_TYPE_CLERIC, oTarget); + if(GetPrCAdjustedCasterLevel(CLASS_TYPE_ANTI_PALADIN, oTarget) > nPower) + nPower = GetPrCAdjustedCasterLevel(CLASS_TYPE_ANTI_PALADIN, oTarget); + +/*CLASS_TYPE_CLERIC +CLASS_TYPE_ANTI_PALADIN +Dragon shaman +blackguard*/ + return nPower; +} + +string GetNounForStrength(int nStrength) +{ + switch(nStrength) + { + case AURA_STRENGTH_DIM: return GetStringByStrRef(16832039); //"dimly" + case AURA_STRENGTH_FAINT: return GetStringByStrRef(16832040); //"faintly" + case AURA_STRENGTH_MODERATE: return GetStringByStrRef(16832041); //"moderately" + case AURA_STRENGTH_STRONG: return GetStringByStrRef(16832042); //"strongly" + case AURA_STRENGTH_OVERWHELMING: return GetStringByStrRef(16832043); //"overwhelmingly" + } + return ""; +} + +void ApplyEffectDetectAuraOnObject(int nStrength, object oTarget, int nVFX) +{ + location lLoc = GetLocation(oTarget); + //float fRadius = (IntToFloat(GetCreatureSize(oTarget))*0.5)+0.5; //this is graphics related, not rules + float fRadius = StringToFloat(Get2DACache("appearance", "CREPERSPACE", GetAppearanceType(oTarget))); + location lCenter; + vector vCenter = GetPositionFromLocation(lLoc); + switch(nStrength) + { + //yes fallthroughs here + case AURA_STRENGTH_OVERWHELMING: + vCenter.z += (fRadius/2.0); + lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0); + BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f); + case AURA_STRENGTH_STRONG: + vCenter.z += (fRadius/2.0); + lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0); + BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f); + case AURA_STRENGTH_MODERATE: + vCenter.z += (fRadius/2.0); + lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0); + BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f); + case AURA_STRENGTH_FAINT: + vCenter.z += (fRadius/2.0); + lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0); + BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f); + case AURA_STRENGTH_DIM: + vCenter.z += (fRadius/2.0); + lCenter = Location(GetAreaFromLocation(lLoc), vCenter, 0.0); + BeamPentacle(DURATION_TYPE_TEMPORARY, nVFX, lCenter, fRadius, 6.2f, "", 1.0f, 0.0f); + } +} + +void DetectAlignmentRound(int nRound, location lLoc, int nGoodEvil, int nLawChaos, string sAura, int nBeamVFX) +{ + object oCaster = OBJECT_SELF; + int nSpellID = GetSpellId(); + + if(!GetHasSpellEffect(nSpellID, oCaster)) + return; + + //if concentration is broken, abort + if(GetBreakConcentrationCheck(oCaster) && nRound != 0) + { + FloatingTextStringOnCreature(GetStringByStrRef(16832000)/*"Concentration broken."*/, oCaster, FALSE); + PRCRemoveEffectsFromSpell(oCaster, nSpellID); + return; + } + + location lNew = GetLocation(oCaster); + + //if you move/turn, restart the process / abort if moved more than 2 m + if(lLoc != lNew) + { + if(GetDistanceBetweenLocations(lLoc, lNew) > 2.0) + { + PRCRemoveEffectsFromSpell(oCaster, nSpellID); + FloatingTextStringOnCreature("You have moved too far, ending Detection", oCaster, FALSE); + return; + } + else + nRound = 1; + } + if(nRound < 0) + nRound = 1; + //sanity check + if(nRound > 4) + nRound = 4; + + // Generate a target location in front of us. + float fFacing = GetFacing(oCaster); + vector vPos = GetPosition(oCaster); + location lTarget = Location(GetArea(oCaster), + Vector(vPos.x + cos(fFacing), vPos.y + sin(fFacing), vPos.z), + fFacing); + + object oTest = GetFirstObjectInShape(SHAPE_SPELLCONE, 20.0, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_AREA_OF_EFFECT); + int nStrongestAura; + int nAuraCount; + while(GetIsObjectValid(oTest)) + { + if(DEBUG) DoDebug("DetectAlignmentRound() : Round = "+IntToString(nRound)+"from "+GetName(oCaster)+" of "+GetName(oTest)); + + int nUndetectable = GetIsUndetectable(oTest, oCaster); //you cannot be detected if this is true + if((GetAlignmentGoodEvil(oTest) == nGoodEvil || nGoodEvil == -1) + && (GetAlignmentLawChaos(oTest) == nLawChaos || nLawChaos == -1) + && ((sAura == GetStringByStrRef(5018)) ? MyPRCGetRacialType(oTest) == RACIAL_TYPE_UNDEAD : TRUE) + && oTest != oCaster + && !nUndetectable) + { + if(nRound == 1) + { + //presence/absence + //ApplyEffectDetectAuraOnObject(AURA_STRENGTH_MODERATE, oCaster, nBeamVFX); + FloatingTextStringOnCreature(PRCGetRGB(15,5,5) + GetStringByStrRef(16832001)// "You detect the presense of" + + " " + (nLawChaos != -1 ? // "good" and "evil" work as both substantives and adjectives, but not so for "lawful" and "chaotic" + (nLawChaos == ALIGNMENT_LAWFUL ? + GetStringByStrRef(4957) // "law" + : GetStringByStrRef(4958) // "chaos" + ) + : sAura + ) + + ".", + oCaster, FALSE); + break;//end while loop + } + + int nStrength; + int nRawStrength = GetAuraStrength(oTest, nGoodEvil, nLawChaos); + + if(nRawStrength == 0) + nStrength = AURA_STRENGTH_DIM; + else if(nRawStrength == 1) + nStrength = AURA_STRENGTH_FAINT; + else if(nRawStrength >= 2 && nRawStrength <= 4) + nStrength = AURA_STRENGTH_MODERATE; + else if(nRawStrength >= 5 && nRawStrength <= 10) + nStrength = AURA_STRENGTH_STRONG; + else if(nRawStrength >= 11) + nStrength = AURA_STRENGTH_OVERWHELMING; + + if(nRound == 2) + { + //number & strongest + nAuraCount++; + if(nStrength > nStrongestAura) + nStrongestAura = nStrength; + //if overwhelming then can stun + int nOpposingGoodEvil; + if(nGoodEvil == ALIGNMENT_EVIL) + nOpposingGoodEvil = ALIGNMENT_GOOD; + else if(nGoodEvil == ALIGNMENT_GOOD) + nOpposingGoodEvil = ALIGNMENT_EVIL; + else + nOpposingGoodEvil = -1; + int nOpposingLawChaos; + if(nLawChaos == ALIGNMENT_CHAOTIC) + nOpposingLawChaos = ALIGNMENT_LAWFUL; + else if(nLawChaos == ALIGNMENT_LAWFUL) + nOpposingLawChaos = ALIGNMENT_CHAOTIC; + else + nOpposingLawChaos = -1; + + if(nStrength == AURA_STRENGTH_OVERWHELMING + && (GetAlignmentGoodEvil(oCaster)==nOpposingGoodEvil || nOpposingGoodEvil == -1) + && (GetAlignmentLawChaos(oCaster)==nOpposingLawChaos || nOpposingLawChaos == -1) + && nRawStrength > GetHitDice(oCaster)*2) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectStunned(), oCaster, 6.0); + } + } + else if(nRound >= 3) + { + if(nRound == 3) + ActionDoCommand(FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrength*3),16-(nStrength*3)) + GetName(oTest) + " " + GetStringByStrRef(16832044)/*"feels"*/ + " "+GetNounForStrength(nStrength)+" "+sAura+".", oCaster, FALSE)); + //strength & location + ActionDoCommand(ApplyEffectDetectAuraOnObject(nStrength, oTest, nBeamVFX)); + } + } + oTest = GetNextObjectInShape(SHAPE_SPELLCONE, 20.0, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_AREA_OF_EFFECT); + } + if(nRound == 2) + { + //reporting + //ApplyEffectDetectAuraOnObject(nStrongestAura, oCaster, nBeamVFX); + FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrongestAura*3),16-(nStrongestAura*3)) + GetStringByStrRef(16832045)/*"You detected"*/ + " " + IntToString(nAuraCount) + " " + GetNounForStrength(nStrongestAura) + " " + sAura + " " + GetStringByStrRef(16832046)/*"auras"*/ + ".", oCaster, FALSE); + } + + //ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0); + nRound++; + + DelayCommand(6.0f, ActionDoCommand(DetectAlignmentRound(nRound, lNew, nGoodEvil, nLawChaos, sAura, nBeamVFX))); +} + +int GetMagicAuraStrength(object oTarget) +{ + int nPower; + effect eLook = GetFirstEffect(oTarget); + while (GetIsEffectValid(eLook)) + { + int nSpellLvl = PRCGetSpellLevelForClass(GetEffectSpellId(eLook), CLASS_TYPE_INVALID); + if (nSpellLvl > nPower) nPower = nSpellLvl; + + eLook = GetNextEffect(oTarget); + } + + return nPower; +} + +void DetectMagicAura(int nRound, location lLoc, int nBeamVFX, float fDist) +{ + object oCaster = OBJECT_SELF; + int nSpellID = GetSpellId(); + + if(!GetHasSpellEffect(nSpellID, oCaster)) + return; + + //if concentration is broken, abort + if(GetBreakConcentrationCheck(oCaster) && nRound != 0) + { + FloatingTextStringOnCreature(GetStringByStrRef(16832000)/*"Concentration broken."*/, oCaster, FALSE); + PRCRemoveEffectsFromSpell(oCaster, nSpellID); + return; + } + + location lNew = GetLocation(oCaster); + + //if you move/turn, restart the process / abort if moved more than 2 m + if(lLoc != lNew) + { + if(GetDistanceBetweenLocations(lLoc, lNew) > 2.0) + { + PRCRemoveEffectsFromSpell(oCaster, nSpellID); + FloatingTextStringOnCreature("You have moved too far, ending Detection", oCaster, FALSE); + return; + } + else + nRound = 1; + } + if(nRound < 0) + nRound = 1; + //sanity check + if(nRound > 4) + nRound = 4; + + // Generate a target location in front of us. + float fFacing = GetFacing(oCaster); + vector vPos = GetPosition(oCaster); + location lTarget = Location(GetArea(oCaster), + Vector(vPos.x + cos(fFacing), vPos.y + sin(fFacing), vPos.z), + fFacing); + + object oTest = GetFirstObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE); + int nStrongestAura; + int nAuraCount; + while(GetIsObjectValid(oTest)) + { + if(DEBUG) DoDebug("DetectMagicAura() : Round = "+IntToString(nRound)+"from "+GetName(oCaster)+" of "+GetName(oTest)); + + // You cannot be detected if this is true + if(!GetIsUndetectable(oTest, oCaster)) + { + if(nRound == 1) + { + FloatingTextStringOnCreature("You detect the presence of magic.", oCaster, FALSE); + break;//end while loop + } + + int nStrength; + int nRawStrength = GetMagicAuraStrength(oTest); + + if(nRawStrength == 0) + nStrength = AURA_STRENGTH_DIM; + else if(nRawStrength >= 1 && nRawStrength <= 3) + nStrength = AURA_STRENGTH_FAINT; + else if(nRawStrength >= 4 && nRawStrength <= 6) + nStrength = AURA_STRENGTH_MODERATE; + else if(nRawStrength >= 7 && nRawStrength <= 9) + nStrength = AURA_STRENGTH_STRONG; + else if(nRawStrength >= 10) + nStrength = AURA_STRENGTH_OVERWHELMING; + + if(nRound == 2) + { + //number & strongest + nAuraCount++; + if(nStrength > nStrongestAura) + nStrongestAura = nStrength; + } + else if(nRound >= 3) + { + if(nRound == 3) + ActionDoCommand(FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrength*3),16-(nStrength*3)) + GetName(oTest) + " feels affected by "+GetNounForStrength(nStrength)+" magic.", oCaster, FALSE)); + //strength & location + ActionDoCommand(ApplyEffectDetectAuraOnObject(nStrength, oTest, nBeamVFX)); + } + } + oTest = GetNextObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + if(nRound == 2) + { + //reporting + //ApplyEffectDetectAuraOnObject(nStrongestAura, oCaster, nBeamVFX); + FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrongestAura*3),16-(nStrongestAura*3)) + GetStringByStrRef(16832045)/*"You detected"*/ + " " + IntToString(nAuraCount) + " " + GetNounForStrength(nStrongestAura) + " magical " + GetStringByStrRef(16832046)/*"auras"*/ + ".", oCaster, FALSE); + } + + //ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0); + nRound++; + + DelayCommand(6.0f, ActionDoCommand(DetectMagicAura(nRound, lNew, nBeamVFX, fDist))); +} + +void DetectRaceAura(int nRound, int nRace, location lLoc, int nBeamVFX, float fDist) +{ + object oCaster = OBJECT_SELF; + int nSpellID = GetSpellId(); + + if(!GetHasSpellEffect(nSpellID, oCaster)) + return; + + //if concentration is broken, abort + if(GetBreakConcentrationCheck(oCaster) && nRound != 0) + { + FloatingTextStringOnCreature(GetStringByStrRef(16832000)/*"Concentration broken."*/, oCaster, FALSE); + PRCRemoveEffectsFromSpell(oCaster, nSpellID); + return; + } + + location lNew = GetLocation(oCaster); + + //if you move/turn, restart the process / abort if moved more than 2 m + if(lLoc != lNew) + { + if(GetDistanceBetweenLocations(lLoc, lNew) > 2.0) + { + PRCRemoveEffectsFromSpell(oCaster, nSpellID); + FloatingTextStringOnCreature("You have moved too far, ending Detection", oCaster, FALSE); + return; + } + else + nRound = 1; + } + if(nRound < 0) + nRound = 1; + //sanity check + if(nRound > 4) + nRound = 4; + + // Generate a target location in front of us. + float fFacing = GetFacing(oCaster); + vector vPos = GetPosition(oCaster); + location lTarget = Location(GetArea(oCaster), + Vector(vPos.x + cos(fFacing), vPos.y + sin(fFacing), vPos.z), + fFacing); + + object oTest = GetFirstObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE); + int nStrongestAura; + int nAuraCount; + while(GetIsObjectValid(oTest)) + { + if(DEBUG) DoDebug("DetectMagicAura() : Round = "+IntToString(nRound)+"from "+GetName(oCaster)+" of "+GetName(oTest)); + + // You cannot be detected if this is true + if(!GetIsUndetectable(oTest, oCaster) && GetObjectType(oTest) == OBJECT_TYPE_CREATURE) + { + if(nRound == 1) + { + FloatingTextStringOnCreature("You detect the presence of the chosen race.", oCaster, FALSE); + break;//end while loop + } + + int nStrength, nRawStrength; + int nCheck = MyPRCGetRacialType(oTest); + // Detect Living is -1 + if(nRace == nCheck || (nRace == -1 && PRCGetIsAliveCreature(oTest))) + nRawStrength = GetHitDice(oTest); + + if(nRawStrength >= 0 && nRawStrength <= 1) + nStrength = AURA_STRENGTH_FAINT; + else if(nRawStrength >= 2 && nRawStrength <= 4) + nStrength = AURA_STRENGTH_MODERATE; + else if(nRawStrength >= 5 && nRawStrength <= 10) + nStrength = AURA_STRENGTH_STRONG; + else if(nRawStrength >= 11) + nStrength = AURA_STRENGTH_OVERWHELMING; + + if(nRound == 2) + { + //number & strongest + nAuraCount++; + if(nStrength > nStrongestAura) + nStrongestAura = nStrength; + } + else if(nRound >= 3) + { + if(nRound == 3) + ActionDoCommand(FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrength*3),16-(nStrength*3)) + GetName(oTest) + " finds the greatest aura is "+GetNounForStrength(nStrength)+".", oCaster, FALSE)); + //strength & location + ActionDoCommand(ApplyEffectDetectAuraOnObject(nStrength, oTest, nBeamVFX)); + } + } + oTest = GetNextObjectInShape(SHAPE_SPELLCONE, fDist, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + if(nRound == 2) + { + //reporting + //ApplyEffectDetectAuraOnObject(nStrongestAura, oCaster, nBeamVFX); + FloatingTextStringOnCreature(PRCGetRGB(15,16-(nStrongestAura*3),16-(nStrongestAura*3)) + GetStringByStrRef(16832045)/*"You detected"*/ + " " + IntToString(nAuraCount) + " " + GetNounForStrength(nStrongestAura) + " racial " + GetStringByStrRef(16832046)/*"auras"*/ + ".", oCaster, FALSE); + } + + //ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0); + nRound++; + + DelayCommand(6.0f, ActionDoCommand(DetectRaceAura(nRound, nRace, lNew, nBeamVFX, fDist))); +} \ No newline at end of file diff --git a/src/include/prc_inc_sb_const.nss b/src/include/prc_inc_sb_const.nss new file mode 100644 index 0000000..b523806 --- /dev/null +++ b/src/include/prc_inc_sb_const.nss @@ -0,0 +1,20 @@ +/* + Constants file for the Spellbook functions + Linked to via prc_inc_core for wider access +*/ + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int SPELLBOOK_IPRP_FEATS_START = 5000; +const int SPELLBOOK_IPRP_FEATS_END = 11999; + +// range #2 +const int SPELLBOOK_IPRP_FEATS_START2 = 18001; +const int SPELLBOOK_IPRP_FEATS_END2 = 24704; + +const int SPELLBOOK_TYPE_INVALID = 0; +const int SPELLBOOK_TYPE_PREPARED = 1; +const int SPELLBOOK_TYPE_SPONTANEOUS = 2; diff --git a/src/include/prc_inc_sbheir.nss b/src/include/prc_inc_sbheir.nss new file mode 100644 index 0000000..aa29bf8 --- /dev/null +++ b/src/include/prc_inc_sbheir.nss @@ -0,0 +1,169 @@ +//:://///////////////////////////////////////////// +//:: Shining Blade of Heironeous include +//:: prc_inc_sbheir +//:://///////////////////////////////////////////// + +#include "prc_inc_util" +#include "inc_item_props" + +const int SHINING_BLADE_LEVEL_SHOCK = 1; +const int SHINING_BLADE_LEVEL_HOLY = 2; +const int SHINING_BLADE_LEVEL_BRILLIANT = 3; + +const string PRC_ShiningBlade_Generation = "PRC_ShiningBlade_Generation"; +const string PRC_ShiningBlade_Level = "PRC_ShiningBladeLevel"; +const string PRC_ShiningBlade_Duration = "PRC_ShiningBladeDuration"; + +void ShiningBlade_RemoveProperties(object oWeapon, int nLevel) +{ + itemproperty iTest = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(iTest)) + { + int bRemove = FALSE; + if (GetItemPropertyDurationType(iTest) == DURATION_TYPE_TEMPORARY) + { + switch(GetItemPropertyType(iTest)) + { + case ITEM_PROPERTY_DAMAGE_BONUS: + if (nLevel >= SHINING_BLADE_LEVEL_SHOCK) + bRemove = (GetItemPropertySubType(iTest) == IP_CONST_DAMAGETYPE_ELECTRICAL) && (GetItemPropertyCostTableValue(iTest) == DAMAGE_BONUS_1d6); + break; + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + if (nLevel >= SHINING_BLADE_LEVEL_HOLY) + bRemove = (GetItemPropertySubType(iTest) == IP_CONST_ALIGNMENTGROUP_EVIL) && (GetItemPropertyParam1Value(iTest) == IP_CONST_DAMAGETYPE_DIVINE) && (GetItemPropertyCostTableValue(iTest) == DAMAGE_BONUS_2d6); + break; + case ITEM_PROPERTY_LIGHT: + if (nLevel >= SHINING_BLADE_LEVEL_BRILLIANT) + bRemove = TRUE; + break; + } + } + if (bRemove) + RemoveItemProperty(oWeapon, iTest); + iTest = GetNextItemProperty(oWeapon); + } +} + +void ShockBlade_ApplyProperties(object oWeapon, int nDuration) +{ + //Remove any old properties + + ShiningBlade_RemoveProperties(oWeapon, SHINING_BLADE_LEVEL_SHOCK); + + //Add the new properties + + if (nDuration) + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ELECTRICAL, DAMAGE_BONUS_1d6), oWeapon, RoundsToSeconds(nDuration)); +} + +void HolyBlade_ApplyProperties(object oWeapon, int nDuration) +{ + //Remove any old properties + + ShiningBlade_RemoveProperties(oWeapon, SHINING_BLADE_LEVEL_HOLY); + + //Add the new properties + + if (nDuration) + { + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ELECTRICAL, DAMAGE_BONUS_1d6), oWeapon, RoundsToSeconds(nDuration)); + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, IP_CONST_DAMAGETYPE_DIVINE, DAMAGE_BONUS_2d6), oWeapon, RoundsToSeconds(nDuration)); + } +} + +void BrilliantBlade_ApplyProperties(object oWeapon, int nDuration) +{ + //Remove any old properies + + ShiningBlade_RemoveProperties(oWeapon, SHINING_BLADE_LEVEL_BRILLIANT); + + //Add the new properties + + if (nDuration) + { + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ELECTRICAL, DAMAGE_BONUS_1d6), oWeapon, RoundsToSeconds(nDuration)); + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, IP_CONST_DAMAGETYPE_DIVINE, DAMAGE_BONUS_2d6), oWeapon, RoundsToSeconds(nDuration)); + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyLight(IP_CONST_LIGHTBRIGHTNESS_BRIGHT, IP_CONST_LIGHTCOLOR_WHITE), oWeapon, RoundsToSeconds(nDuration)); + } +} + +void BrilliantBlade_ApplyAttackBonuses(object oPC, int bOnHand, int bOffHand) +{ + //ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectAttackIncrease(20, nAttackBonusHand)), oPC, RoundsToSeconds(nDuration)); + //This applies the attack bonus only to hands currently containing longsword, + //but incorrectly the bonus is still active if the longsword is exchanged for another weapon. + + //AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonus(20), oWeapon, RoundsToSeconds(nDuration)); + //This adds the bonus only to the correct wielded weapon(s), but won't increase AB with + //a longsword that one is not wielding at the time the bonus is applied. + //Note: theoretically, we should increase the AB of the weapon by 20, not set it to 20; however, since the AB + //cap is 20, and the AB of a weapon is included in the cap, it makes little practical difference. + //NOTE: this provide +20 DR penetration, so it's a no-go (see http://nwn.wikia.com/wiki/Damage_reduction) + + SetCompositeAttackBonus(oPC, "SB_BrilliantBlade_R", bOnHand?20:0, ATTACK_BONUS_ONHAND); + SetCompositeAttackBonus(oPC, "SB_BrilliantBlade_L", bOffHand?20:0, ATTACK_BONUS_OFFHAND); +} + +void ShiningBlade_ApplyProperties(object oWeapon, int nDuration, int nLevel) +{ + switch(nLevel) + { + case SHINING_BLADE_LEVEL_SHOCK: + ShockBlade_ApplyProperties(oWeapon, nDuration); + break; + + case SHINING_BLADE_LEVEL_HOLY: + HolyBlade_ApplyProperties(oWeapon, nDuration); + break; + + case SHINING_BLADE_LEVEL_BRILLIANT: + BrilliantBlade_ApplyProperties(oWeapon, nDuration); + break; + } +} + +void ShiningBlade_ApplyAttackBonuses(object oPC, int bOnHand, int bOffHand, int nLevel) +{ + if (nLevel >= SHINING_BLADE_LEVEL_BRILLIANT) + BrilliantBlade_ApplyAttackBonuses(oPC, bOnHand, bOffHand); + else + BrilliantBlade_ApplyAttackBonuses(oPC, FALSE, FALSE); +} + +void ShiningBlade_ExpireBonuses(object oPC, int nGeneration, int nLevel) +{ + int nShiningBladeGeneration = GetLocalInt(oPC, PRC_ShiningBlade_Generation); + if (nGeneration == nShiningBladeGeneration) + { + if (DEBUG) DoDebug("Expiring Shining Blade bonuses"); + + //Zero out variables so that equipping a weapon doesn't reapply the expired bonuses + SetLocalInt(oPC, PRC_ShiningBlade_Duration, 0); + SetLocalInt(oPC, PRC_ShiningBlade_Level, 0); + + //Expire bonsues on wielded weapons + object oOnHandWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oOffHandWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + if (GetBaseItemType(oOnHandWeapon) == BASE_ITEM_LONGSWORD) + ShiningBlade_RemoveProperties(oOnHandWeapon, nLevel); + if (GetBaseItemType(oOffHandWeapon) == BASE_ITEM_LONGSWORD) + ShiningBlade_RemoveProperties(oOffHandWeapon, nLevel); + + //Expire attack bonuses + ShiningBlade_ApplyAttackBonuses(oPC, FALSE, FALSE, nLevel); + } + else + { + //The Brilliant/Holy/Shock Blade feat has been used again since this removal was scheduled, + //so don't expire--the next application of the feat will expire them + //when *it's* time runs out. + if (DEBUG) DoDebug("Not expiring Shining Blade bonuses: " + IntToString(nGeneration) + ", " + IntToString(nShiningBladeGeneration)); + } +} + +void ShiningBlade_ScheduleBonusExpiration(object oPC, int nDuration, int nLevel) +{ + int nGeneration = PRC_NextGeneration(GetLocalInt(oPC, PRC_ShiningBlade_Generation)); + SetLocalInt(oPC, PRC_ShiningBlade_Generation, nGeneration); + DelayCommand(RoundsToSeconds(nDuration), ShiningBlade_ExpireBonuses(oPC, nGeneration, nLevel)); +} diff --git a/src/include/prc_inc_scry.nss b/src/include/prc_inc_scry.nss new file mode 100644 index 0000000..0178f0a --- /dev/null +++ b/src/include/prc_inc_scry.nss @@ -0,0 +1,596 @@ +//:://///////////////////////////////////////////// +//:: Scrying include +//:: prc_inc_scry +//:://///////////////////////////////////////////// +/** @file + This include contains all of the code that is used + to scry. At the moment, this is the basic scrying + and will be expanded with the additional material + as coding goes along. This is based on the code + of the Baelnorn Project written by Ornedan. + + All the operations work only on PCs, as there is no + AI that could have NPCs take any advantage of the + system. +*/ +//::////////////////////////////////////////////// +//:: Created By: Stratovarius +//:: Created On: 30.04.2007 +//::////////////////////////////////////////////// + +#include "psi_inc_psifunc" +#include "inc_utility" +#include "true_utter_const" +#include "shd_myst_const" + +/////////////////////// +/* Public Constants */ +/////////////////////// + +const string COPY_LOCAL_NAME = "Scry_Copy"; +const string ALREADY_IMMORTAL_LOCAL_NAME = "Scry_ImmortalAlready"; +const float SCRY_HB_DELAY = 1.0f; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * ONLY CALL THIS FUNCTION + * Does all of the necessary set up for scrying + * Also checks for all of the anti-scrying features + * if oPC == oTarget, will use PRCGetSpellLocation + * as the location for the effects to begin + * + * @param oPC The PC on whom to operate. + * @param oTarget The object to target + */ +void ScryMain(object oPC, object oTarget); + +/** + * Moves all of the PC's items to the copy creature + * Switches locations of the two creatures + * + * @param oPC The PC on whom to operate. + * @param oCopy The copy of the PC + */ +void DoScryBegin(object oPC, object oCopy); + +/** + * Undoes all the effects of DoScryBegin + * + * @param oPC The PC on whom to operate. + * @param oCopy The copy of the PC + */ +void DoScryEnd(object oPC, object oCopy); + +/** + * Runs tests to see if the Scry effect can still continue. + * If the copy is dead, end Scry and kill the PC. + * + * @param oPC The PC on whom to operate. + * @param oCopy The copy of the PC + */ +void ScryMonitor(object oPC, object oCopy); + +/** + * Reverses ApplyScryEffects + * + * @param oPC The PC on whom to operate. + */ +void RemoveScryEffects(object oPC); + +/** + * Checks whether the PC is scrying or not + * + * @param oPC The PC on whom to operate. + */ +int GetIsScrying(object oPC); + +/** + * Does the Discern Location content + * + * @param oPC The PC on whom to operate. + * @param oTarget The spell Target + */ +void DiscernLocation(object oPC, object oTarget); + +/** + * Does the Locate Creature and Object content + * + * @param oPC The PC on whom to operate. + * @param oTarget The spell Target + */ +void LocateCreatureOrObject(object oPC, object oTarget); + +/** + * Prevents the copy from dropping goods when dead. + * + * @param oImage The PC's copy. + */ +void CleanCopy(object oImage); + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void ScryMain(object oPC, object oTarget) +{ + // Get the main variables used. + object oCopy = GetLocalObject(oPC, COPY_LOCAL_NAME); + effect eLight = EffectVisualEffect(VFX_IMP_HEALING_X , FALSE); + effect eGlow = EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE, FALSE); + // Use prestored variables because of time delay + int nCasterLevel = GetLocalInt(oPC, "ScryCasterLevel"); + int nSpell = PRCGetSpellId(); //GetLocalInt(oPC, "ScrySpellId"); + int nDC = GetLocalInt(oPC, "ScrySpellDC"); + float fDur = GetLocalFloat(oPC, "ScryDuration"); + if(DEBUG) DoDebug("prc_inc_scry: Beginning ScryMain. nSpell: " + IntToString(nSpell)); + + if (oPC != oTarget) // No hitting yourself with defenses + { + if(GetHasSpellEffect(POWER_REMOTE_VIEW_TRAP, oTarget)) + { + effect eVis = EffectVisualEffect(VFX_IMP_LIGHTNING_S); + // Failed Save + if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_NONE)) + { + FloatingTextStringOnCreature("You have been caught in a Remote View Trap", oPC, FALSE); + eVis = EffectLinkEffects(eVis, EffectDamage(d6(8), DAMAGE_TYPE_ELECTRICAL)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + return; + } + else + { + FloatingTextStringOnCreature("You have saved against a Remote View Trap", oPC, FALSE); + eVis = EffectLinkEffects(eVis, EffectDamage(d6(4), DAMAGE_TYPE_ELECTRICAL)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + } + } + if(GetHasSpellEffect(SPELL_SEQUESTER, oTarget) || GetHasSpellEffect(POWER_SEQUESTER, oTarget)) + { + // Spell auto-fails if this is the case + FloatingTextStringOnCreature(GetName(oTarget) + " has been Sequestered.", oPC, FALSE); + return; + } + if(GetHasSpellEffect(SPELL_OBSCURE_OBJECT, oTarget)) + { + // Spell auto-fails if this is the case + FloatingTextStringOnCreature(GetName(oTarget) + " has been Obscured.", oPC, FALSE); + return; + } + if(GetHasSpellEffect(SPELL_NONDETECTION, oTarget) || GetLevelByClass(CLASS_TYPE_WILD_MAGE, oTarget) >= 6 || GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oTarget) >= 5 || GetHasSpellEffect(MELD_ENIGMA_HELM, oTarget) || GetRacialType(oTarget) == RACIAL_TYPE_SKULK) + { + // Caster level check or the Divination fails. + int nTarget = PRCGetCasterLevel(oTarget) + 11; + if (GetRacialType(oTarget) == RACIAL_TYPE_SKULK && 20 > nTarget) nTarget = 20; + if(nTarget > nCasterLevel + d20()) + { + FloatingTextStringOnCreature(GetName(oTarget) + " has Nondetection active.", oPC, FALSE); + return; + } + } + if(GetHasSpellEffect(POWER_ESCAPE_DETECTION, oTarget)) + { + // Caster level check or the Divination fails. + // Max of 10 + if(PRCMax(10, GetManifesterLevel(oTarget)) + 13 > nCasterLevel + d20()) + { + FloatingTextStringOnCreature(GetName(oTarget) + " has Escape Detection active.", oPC, FALSE); + return; + } + } + if(GetHasSpellEffect(POWER_DETECT_REMOTE_VIEWING, oTarget)) + { + // Caster level check for the target to learn where the caster is + if(GetManifesterLevel(oTarget) + d20() >= nCasterLevel + d20()) + { + FloatingTextStringOnCreature(GetName(oPC) + " is viewing you from " + GetName(GetArea(oPC)), oTarget, FALSE); + } + } + } + // Discern Location skips all of this + if(nSpell == SPELL_DISCERN_LOCATION) + { + DiscernLocation(oPC, oTarget); + return; + } + // So do the Locate Twins + if(nSpell == SPELL_LOCATE_CREATURE || nSpell == SPELL_LOCATE_OBJECT || nSpell == MYST_TRAIL_OF_HAZE) + { + LocateCreatureOrObject(oPC, oTarget); + return; + } + + if(nSpell != SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE && nSpell != POWER_CLAIRTANGENT_HAND && nSpell != SPELL_PNP_SCRY_FAMILIAR + && nSpell != POWER_CLAIRVOYANT_SENSE && nSpell != TRUE_SEE_THE_NAMED && oPC != oTarget) // No save if you target yourself. + { + //Make SR Check + if(PRCDoResistSpell(oPC, oTarget, nCasterLevel)) + { + FloatingTextStringOnCreature(GetName(oTarget) + " made Spell Resistance check vs Scrying", oPC, FALSE); + DoScryEnd(oPC, oCopy); + return; + } + //Save + if(PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_MIND_SPELLS)) + { + FloatingTextStringOnCreature(GetName(oTarget) + " saved vs Scrying", oPC, FALSE); + DoScryEnd(oPC, oCopy); + return; + } + } + + if(DEBUG) DoDebug("prc_inc_scry running.\n" + + "oPC = '" + GetName(oPC) + "' - '" + GetTag(oPC) + "' - " + ObjectToString(oPC) + + "Copy exists: " + DebugBool2String(GetIsObjectValid(oCopy)) + ); + + location lTarget = GetLocation(oTarget); + if (oTarget == oPC || !GetIsObjectValid(oTarget)) // Some spells just target ground for this + lTarget = PRCGetSpellTargetLocation(); + // Create the copy + oCopy = CopyObject(oPC, lTarget, OBJECT_INVALID, GetName(oPC) + "_" + COPY_LOCAL_NAME); + CleanCopy(oCopy); + // Attempted Fix to the Copy get's murdered problem. + int nMaxHenchmen = GetMaxHenchmen(); + SetMaxHenchmen(99); + AddHenchman(oPC, oCopy); + SetMaxHenchmen(nMaxHenchmen); + SetIsTemporaryFriend(oPC, oCopy, FALSE); + // Set the copy to be undestroyable, so that it won't vanish to the ether + // along with the PC's items. + AssignCommand(oCopy, SetIsDestroyable(FALSE, FALSE, FALSE)); + // Make the copy immobile and minimize the AI on it + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ExtraordinaryEffect(EffectCutsceneImmobilize()), oCopy); + SetAILevel(oCopy, AI_LEVEL_VERY_LOW); + + // Store a referece to the copy on the PC + SetLocalObject(oPC, COPY_LOCAL_NAME, oCopy); + + //Set Immortal flag on the PC or if they were already immortal, + //leave a note about it on them. + if(GetImmortal(oPC)) + { + if(DEBUG) DoDebug("prc_inc_scry: The PC was already immortal"); + SetLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME, TRUE); + } + else + { + if(DEBUG) DoDebug("prc_inc_scry: Setting PC immortal"); + SetImmortal(oPC, TRUE); + DeleteLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME); // Paranoia + } + + // Do VFX on PC and copy + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eLight, oCopy); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eLight, oPC); + + // Do the switching around + DoScryBegin(oPC, oCopy); + + //Set up duration marker for ending effect + DelayCommand(fDur, SetLocalInt(oPC, "SCRY_EXPIRED", 1)); +} + +// Moves the PC's items to the copy and switches their locations around +void DoScryBegin(object oPC, object oCopy) +{ + if(DEBUG) DoDebug("prc_inc_scry: DoScryBegin():\n" + + "oPC = '" + GetName(oPC) + "'\n" + + "oCopy = '" + GetName(oCopy) + "'" + ); + // Make sure both objects are valid + if(!GetIsObjectValid(oCopy) || !GetIsObjectValid(oPC)){ + if(DEBUG) DoDebug("DoScryBegin called, but one of the parameters wasn't a valid object. Object status:" + + "\nPC - " + (GetIsObjectValid(oPC) ? "valid":"invalid") + + "\nCopy - " + (GetIsObjectValid(oCopy) ? "valid":"invalid") + ); + + // Some cleanup before aborting + if(!GetLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME)) + { + SetImmortal(oPC, FALSE); + DeleteLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME); + } + MyDestroyObject(oCopy); + DeleteLocalInt(oPC, "Scry_Active"); + RemoveScryEffects(oPC); + + return; + } + + // Set a local on the PC telling that it's a scry. This is used + // to keep the PC from picking up or losing objects. + // Also stops it from casting spells + SetLocalInt(oPC, "Scry_Active", TRUE); + + // Start a pseudo-hb to monitor the status of both PC and copy + DelayCommand(SCRY_HB_DELAY, ScryMonitor(oPC, oCopy)); + + // Add eventhooks + + if(DEBUG) DoDebug("AddEventScripts"); + + AddEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "prc_scry_event", TRUE, FALSE); // OnEquip + AddEventScript(oPC, EVENT_ONACQUIREITEM, "prc_scry_event", TRUE, FALSE); // OnAcquire + AddEventScript(oPC, EVENT_ONPLAYERREST_STARTED, "prc_scry_event", FALSE, FALSE); // OnRest + AddEventScript(oPC, EVENT_ONCLIENTENTER, "prc_scry_event", TRUE, FALSE); //OnClientEnter + + // Adjust reputation so the target monster doesn't attack you + //ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectCharmed()), oTarget, GetLocalFloat(oPC, "ScryDuration")); + // Swap the copy and PC + location lPC = GetLocation(oPC); + location lCopy = GetLocation(oCopy); + DelayCommand(1.5f,AssignCommand(oPC, JumpToLocation(lCopy))); + DelayCommand(1.5f,AssignCommand(oCopy, JumpToLocation(lPC))); +} + +// Switches the PC's inventory back from the copy and returns the PC to the copy's location. +void DoScryEnd(object oPC, object oCopy) +{ + if(DEBUG) DoDebug("prc_inc_scry: DoScryEnd():\n" + + "oPC = '" + GetName(oPC) + "'\n" + + "oCopy = '" + GetName(oCopy) + "'" + ); + + //effect eGlow = EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE, FALSE); + effect eLight = EffectVisualEffect(VFX_IMP_HEALING_X , FALSE); + + // Remove Immortality from the PC if necessary + if(!GetLocalInt(oPC, ALREADY_IMMORTAL_LOCAL_NAME)) + SetImmortal(oPC, FALSE); + + // Remove the VFX and the attack penalty from all spells. + PRCRemoveSpellEffects(SPELL_SCRY , oPC, oPC); + PRCRemoveSpellEffects(SPELL_GREATER_SCRYING , oPC, oPC); + PRCRemoveSpellEffects(SPELL_DISCERN_LOCATION , oPC, oPC); + PRCRemoveSpellEffects(SPELL_LOCATE_CREATURE , oPC, oPC); + PRCRemoveSpellEffects(SPELL_LOCATE_OBJECT , oPC, oPC); + PRCRemoveSpellEffects(SPELL_ARCANE_EYE , oPC, oPC); + PRCRemoveSpellEffects(MYST_BEND_PERSPECTIVE , oPC, oPC); + PRCRemoveSpellEffects(MYST_FAR_SIGHT , oPC, oPC); + PRCRemoveSpellEffects(MYST_EPHEMERAL_IMAGE , oPC, oPC); + PRCRemoveSpellEffects(SPELL_SHADOWDOUBLE , oPC, oPC); + PRCRemoveSpellEffects(SPELL_OBSCURE_OBJECT , oPC, oPC); + PRCRemoveSpellEffects(SPELL_SEQUESTER , oPC, oPC); + PRCRemoveSpellEffects(SPELL_PNP_SCRY_FAMILIAR, oPC, oPC); + PRCRemoveSpellEffects(SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE, oPC, oPC); + + // Remove the local signifying that the PC is a projection + DeleteLocalInt(oPC, "Scry_Active"); + + // Remove the local signifying projection being terminated by an external cause + DeleteLocalInt(oPC, "Scry_Abort"); + + // Remove the heartbeat HP tracking local + DeleteLocalInt(oPC, "Scry_HB_HP"); + // Delete all of the marker ints for the spell + DeleteLocalInt(oPC, "ScryCasterLevel"); + DeleteLocalInt(oPC, "ScrySpellId"); + DeleteLocalInt(oPC, "ScrySpellDC"); + DeleteLocalFloat(oPC, "ScryDuration"); + + // Remove weapons nerfing + RemoveScryEffects(oPC); + + // Remove eventhooks + RemoveEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "prc_scry_event", TRUE, FALSE); // OnEquip + RemoveEventScript(oPC, EVENT_ONACQUIREITEM, "prc_scry_event", TRUE, FALSE); // OnAcquire + RemoveEventScript(oPC, EVENT_ONPLAYERREST_STARTED, "prc_scry_event", FALSE, FALSE); // OnRest + RemoveEventScript(oPC, EVENT_ONCLIENTENTER, "prc_scry_event", TRUE, FALSE); //OnClientEnter + + // Move PC and inventory + location lCopy = GetLocation(oCopy); + DelayCommand(1.5f, AssignCommand(oPC, JumpToLocation(lCopy))); + + // Set the PC's hitpoints to be whatever the copy has + int nOffset = GetCurrentHitPoints(oCopy) - GetCurrentHitPoints(oPC); + if(nOffset > 0) + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nOffset), oPC); + else if (nOffset < 0) + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(-nOffset, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY), oPC); + + // Schedule deletion of the copy + DelayCommand(0.3f, MyDestroyObject(oCopy)); + + //Delete the object reference + DeleteLocalObject(oPC, COPY_LOCAL_NAME); + + // VFX + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eLight, lCopy, 3.0); + DestroyObject(oCopy); + + //Remove duration marker + DeleteLocalInt(oPC, "SCRY_EXPIRED"); +} + +//Runs tests to see if the projection effect can still continue. +//If the PC has reached 1 HP, end projection normally. +//If the copy is dead, end projection and kill the PC. +void ScryMonitor(object oPC, object oCopy) +{ + if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor():\n" + + "oPC = '" + GetName(oPC) + "'\n" + + "oCopy = '" + GetName(oCopy) + "'" + ); + // Abort if the projection is no longer marked as being active + if(!GetLocalInt(oPC, "Scry_Active")) + return; + + // Some paranoia in case something interfered and either PC or copy has been destroyed + if(!(GetIsObjectValid(oPC) && GetIsObjectValid(oCopy))){ + WriteTimestampedLogEntry("Baelnorn Projection hearbeat aborting due to an invalid object. Object status:" + + "\nPC - " + (GetIsObjectValid(oPC) ? "valid":"invalid") + + "\nCopy - " + (GetIsObjectValid(oCopy) ? "valid":"invalid")); + return; + } + + // Start the actual work by checking the copy's status. The death thing should take priority + if(GetIsDead(oCopy)) + { + if (DEBUG) DoDebug("prc_inc_scry: Copy is dead, killing PC"); + DoScryEnd(oPC, oCopy); + effect eKill = EffectDamage(9999, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + DelayCommand(3.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eKill, oPC)); + } + else + { + // Check if the "projection" has been destroyed or if some other event has caused the projection to end + if(GetLocalInt(oPC, "Scry_Abort")) + { + if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor(): The Projection has been terminated, ending projection"); + DoScryEnd(oPC, oCopy); + } + else + { + // This makes sure you are invisible. + //AssignCommand(oPC, ClearAllActions(TRUE)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectEthereal()), oPC, SCRY_HB_DELAY + 0.3); + DelayCommand(SCRY_HB_DELAY, ScryMonitor(oPC, oCopy)); + } + + //If duration expired, end effect + if(GetLocalInt(oPC, "SCRY_EXPIRED")) + { + DoScryEnd(oPC, oCopy); + } + // End due to being in combat, except when ephemeral image + if(GetIsInCombat(oPC) && GetLocalInt(oPC, "ScrySpellId") != MYST_EPHEMERAL_IMAGE) + { + DoScryEnd(oPC, oCopy); + if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor(): Scryer in combat, ending projection"); + AssignCommand(oPC, ClearAllActions(TRUE)); + } + // In PnP, once you lose line of effect, the spell ends + float fDistance = FeetToMeters(100.0 + GetLocalInt(oPC, "ScryCasterLevel") * 10.0); + if (GetLocalInt(oPC, "ScrySpellId") == MYST_EPHEMERAL_IMAGE && GetDistanceBetween(oPC, oCopy) > fDistance) + { + DoScryEnd(oPC, oCopy); + if(DEBUG) DoDebug("prc_inc_scry: ScryMonitor(): Ephemeral Image distance exceeded, ending projection"); + AssignCommand(oPC, ClearAllActions(TRUE)); + } + } +} + +//Undoes changes made in ApplyScryEffects(). +void RemoveScryEffects(object oPC) +{ + if(DEBUG) DoDebug("prc_inc_scry: RemoveScryEffects():\n" + + "oPC = '" + GetName(oPC) + "'" + ); + effect eCheck = GetFirstEffect(oPC); + while(GetIsEffectValid(eCheck)){ + if(GetEffectSpellId(eCheck) == GetLocalInt(oPC, "ScrySpellId")) + { + RemoveEffect(oPC, eCheck); + } + eCheck = GetNextEffect(oPC); + } + + // Remove the no-damage property from all weapons it was added to + int i; + object oWeapon; + for(i = 0; i < array_get_size(oPC, "Scry_Nerfed"); i++) + { + oWeapon = array_get_object(oPC, "Scry_Nerfed", i); + IPRemoveMatchingItemProperties(oWeapon, ITEM_PROPERTY_NO_DAMAGE, DURATION_TYPE_PERMANENT); + } + + array_delete(oPC, "Scry_Nerfed"); +} + +int GetIsScrying(object oPC) +{ + return GetLocalInt(oPC, "Scry_Active"); +} + +void DiscernLocation(object oPC, object oTarget) +{ + // Tells the name of the area the target is in + // Yes, this is a little crappy for an 8th level spell. + FloatingTextStringOnCreature(GetName(oTarget) + " is in " + GetName(GetArea(oTarget)), oPC, FALSE); + + // Delete all of the marker ints for the spell + DeleteLocalInt(oPC, "ScryCasterLevel"); + DeleteLocalInt(oPC, "ScrySpellId"); + DeleteLocalInt(oPC, "ScrySpellDC"); + DeleteLocalFloat(oPC, "ScryDuration"); +} + +void LocateCreatureOrObject(object oPC, object oTarget) +{ + location lPC = GetLocation(oPC); + location lTarget = GetLocation(oTarget); + // Cause the caller to face the target + SetFacingPoint(GetPosition(oTarget)); + // Now spit out the distance + float fDist = GetDistanceBetweenLocations(lPC, lTarget); + FloatingTextStringOnCreature(GetName(oTarget) + " is " + FloatToString(MetersToFeet(fDist)) + " feet away from you.", oPC, FALSE); + + // Delete all of the marker ints for the spell + DeleteLocalInt(oPC, "ScryCasterLevel"); + DeleteLocalInt(oPC, "ScrySpellId"); + DeleteLocalInt(oPC, "ScrySpellDC"); + DeleteLocalFloat(oPC, "ScryDuration"); +} + +void CleanCopy(object oImage) +{ + SetLootable(oImage, FALSE); + // remove inventory contents + object oItem = GetFirstItemInInventory(oImage); + while(GetIsObjectValid(oItem)) + { + SetPlotFlag(oItem,FALSE); + if(GetHasInventory(oItem)) + { + object oItem2 = GetFirstItemInInventory(oItem); + while(GetIsObjectValid(oItem2)) + { + object oItem3 = GetFirstItemInInventory(oItem2); + while(GetIsObjectValid(oItem3)) + { + SetPlotFlag(oItem3,FALSE); + DestroyObject(oItem3); + oItem3 = GetNextItemInInventory(oItem2); + } + SetPlotFlag(oItem2,FALSE); + DestroyObject(oItem2); + oItem2 = GetNextItemInInventory(oItem); + } + } + DestroyObject(oItem); + oItem = GetNextItemInInventory(oImage); + } + // remove non-visible equipped items + int i; + for(i=0;i 0 ) + { + // Get the item in this slot. + oItem = GetItemInSlot(nSlot, oCreature); + if ( oItem != OBJECT_INVALID ) + { + // Loop through oItem's item properties. + ipBypass = GetFirstItemProperty(oItem); + while ( GetIsItemPropertyValid(ipBypass) ) + { + // See if ipBypass is what we want to bypass. + if ( GetItemPropertyType(ipBypass) == nType && + GetItemPropertySubType(ipBypass) == nSubType && + GetItemPropertyDurationType(ipBypass) == DURATION_TYPE_PERMANENT ) + { + // Remove ipBypass for a split second. + RemoveItemProperty(oItem, ipBypass); + DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT, ipBypass, oItem)); + } + // Next item property. + ipBypass = GetNextItemProperty(oItem); + }//while (ipBypass) + }//if (oItem) + }//while (nSlot) +} + +void _prc_inc_shifting_ApplyStatPenalties(object oCreature, object oPropertyHolder, int nDeltaSTR, int nDeltaDEX, int nDeltaCON) +{ + //The immunity properties removed by _prc_inc_shifting_ApplyStatPenalties aren't actually removed until this script + //finishes running, so we delay adding the penalties until after that happens. Re-adding the removed immunity properties + //is delayed even longer (schedulded by _prc_inc_shifting_BypassItemProperties) so that it happens after the penalties have been applied. + _prc_inc_shifting_BypassItemProperties(oCreature, IP_CONST_IMMUNITYMISC_LEVEL_ABIL_DRAIN); + if(nDeltaSTR < 0) + DelayCommand(0.0, SetCompositeBonus(oPropertyHolder, "Shifting_AbilityAdjustmentSTRPenalty", -nDeltaSTR, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_STR)); + if(nDeltaDEX < 0) + DelayCommand(0.0, SetCompositeBonus(oPropertyHolder, "Shifting_AbilityAdjustmentDEXPenalty", -nDeltaDEX, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_DEX)); + if(nDeltaCON < 0) + DelayCommand(0.0, SetCompositeBonus(oPropertyHolder, "Shifting_AbilityAdjustmentCONPenalty", -nDeltaCON, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_CON)); +} + +//TODO: use ForceEquip() in inc_utility instead? +void SafeEquipItem(object oShifter, object oItem, int nSlot, float fDelay = 1.0f) +{ + if (GetIsObjectValid(oItem) && GetItemInSlot(nSlot, oShifter) != oItem) + { + AssignCommand(oShifter, ActionEquipItem(oItem, nSlot)); + if (fDelay < 9.0f) //Don't repeat forever + DelayCommand(fDelay, SafeEquipItem(oShifter, oItem, nSlot, fDelay * 2)); //Try increasing delays until it works + } +} + +void _prc_inc_shifting_ApplyEffects(object oShifter, int bShifting) +{ + if (GetLocalInt(oShifter, "PRC_Shifter_AffectsToApply")) + { + if(bShifting) + { + SetLocalInt(oShifter, "PRC_SHIFTER_APPLY_ALL_SPELL_EFFECTS", TRUE); + //The only place this is ever removed is by the spell script after it is used. + //This prevents race conditions where this function is called again + //and changes the value to FALSE before the spell script had a chance to use it. + } + + if (GetLocalInt(oShifter, "PRC_Shifter_Use_RodOfWonder")) + { + if (DEBUG_EFFECTS || DEBUG) + DoDebug("ADDING EFFECTS: ROD OF WONDER"); + object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oShifter)); + AssignCommand(oCastingObject, ActionCastSpellAtObject(SPELL_SHIFTING_EFFECTS, oShifter, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + //Handled by spell code: DestroyObject(oCastingObject, 6.0); + } + else + { + if (DEBUG_EFFECTS || DEBUG) + DoDebug("ADDING EFFECTS: ALTERNATE"); + CastSpellAtObject(SPELL_SHIFTING_EFFECTS, oShifter, METAMAGIC_NONE, 0, 0, 0, OBJECT_INVALID, oShifter); + } + } +} + +void _prc_inc_shifting_RemoveSpellEffects_RodOfWonder(object oShifter, int bApplyAll) +{ + effect eTest = GetFirstEffect(oShifter); + while(GetIsEffectValid(eTest)) + { + if(GetEffectSpellId(eTest) == SPELL_SHIFTING_EFFECTS) + { + if (GetEffectType(eTest) == EFFECT_TYPE_TEMPORARY_HITPOINTS) + { + int nHPBonus = GetLocalInt(oShifter, "PRC_Shifter_ExtraCON_HPBonus"); + if (bApplyAll || !nHPBonus) + { + if(DEBUG_EFFECTS || DEBUG) + DoDebug("Removing temp HP: " + IntToString(bApplyAll) + ", " + IntToString(nHPBonus)); + RemoveEffect(oShifter, eTest); + } + else if(DEBUG_EFFECTS || DEBUG) + DoDebug("Skipped removing temp HP" + IntToString(bApplyAll) + ", " + IntToString(nHPBonus)); + } + else if (GetEffectType(eTest) == EFFECT_TYPE_INVISIBILITY) + { + int bHarmlessInvisible = GetLocalInt(oShifter, "PRC_Shifter_HarmlessInvisible"); + if (bApplyAll || !bHarmlessInvisible) + { + if(DEBUG_EFFECTS || DEBUG) + DoDebug("Removing invisibility" + IntToString(bApplyAll) + ", " + IntToString(bHarmlessInvisible)); + RemoveEffect(oShifter, eTest); + } + else if(DEBUG_EFFECTS || DEBUG) + DoDebug("Skipped removing invisibility" + IntToString(bApplyAll) + ", " + IntToString(bHarmlessInvisible)); + } + else + RemoveEffect(oShifter, eTest); + } + eTest = GetNextEffect(oShifter); + } +} + +void _prc_inc_shifting_RemoveSpellEffects_CastSpellAtObject(object oShifter, int bApplyAll) +{ + effect eTest = GetFirstEffect(oShifter); + while(GetIsEffectValid(eTest)) + { + int nSpellId = GetEffectSpellId(eTest); + //TODO: Is THERE ANY WAY TO SET THE SPELL ID CORRECTLY INSTEAD OF IT BEING -1? + //EvalPRCFeats seems to call scripts using the ExecuteScript function and the spell id is set correctly. How does this happen? + if(nSpellId == SPELL_SHIFTING_EFFECTS || nSpellId == -1) + { + switch(GetEffectType(eTest)) + { + case EFFECT_TYPE_ATTACK_INCREASE: + case EFFECT_TYPE_ATTACK_DECREASE: + case EFFECT_TYPE_DAMAGE_INCREASE: + case EFFECT_TYPE_DAMAGE_DECREASE: + case EFFECT_TYPE_SAVING_THROW_INCREASE: + case EFFECT_TYPE_SAVING_THROW_DECREASE: + case EFFECT_TYPE_SKILL_INCREASE: + case EFFECT_TYPE_SKILL_DECREASE: + case EFFECT_TYPE_AC_INCREASE: + case EFFECT_TYPE_AC_DECREASE: + RemoveEffect(oShifter, eTest); + break; + + case EFFECT_TYPE_TEMPORARY_HITPOINTS: + { + int nHPBonus = GetLocalInt(oShifter, "PRC_Shifter_ExtraCON_HPBonus"); + if (bApplyAll || !nHPBonus) + RemoveEffect(oShifter, eTest); + else if(DEBUG_EFFECTS || DEBUG) + DoDebug("Skipped removing temp HP" + IntToString(bApplyAll) + ", " + IntToString(nHPBonus)); + break; + } + + case EFFECT_TYPE_INVISIBILITY: + { + int bHarmlessInvisible = GetLocalInt(oShifter, "PRC_Shifter_HarmlessInvisible"); + if (bApplyAll || !bHarmlessInvisible) + RemoveEffect(oShifter, eTest); + else if(DEBUG_EFFECTS || DEBUG) + DoDebug("Skipped removing invisibility" + IntToString(bApplyAll) + ", " + IntToString(bHarmlessInvisible)); + break; + } + } + } + eTest = GetNextEffect(oShifter); + } + if(GetLocalInt(oShifter, "ReserveFeatsRunning")) + DelayCommand(0.1f, ExecuteScript("prc_reservefeat", oShifter)); +} + +void _prc_inc_shifting_RemoveSpellEffects(object oShifter, int bApplyAll) +{ + if (GetLocalInt(oShifter, "PRC_Shifter_Use_RodOfWonder")) + { + if(DEBUG_EFFECTS || DEBUG) + DoDebug("REMOVING EFFECTS: ROD OF WONDER"); + if(!GetLocalInt(oShifter, "PRC_Shifter_Using_RodOfWonder")) + { + //Remove effects of other approach if just switching to this one. + //This may remove too many effects, but shifting should force the effects + //that should not have been removed to be re-added, and they won't be + //removed again. + SetLocalInt(oShifter, "PRC_Shifter_Using_RodOfWonder", TRUE); + _prc_inc_shifting_RemoveSpellEffects_CastSpellAtObject(oShifter, bApplyAll); + } + _prc_inc_shifting_RemoveSpellEffects_RodOfWonder(oShifter, bApplyAll); + } + else + { + /* + This method has advantages and disadvantages compared to the Rod of Wonder method. + Advantages: + * It's instantaneous; if the system is busy (e.g., in a really difficult fight) + the Rod of Wonder sometimes takes 10-15 seconds, which can literally kill you + Disadvantages: + * It can't tell which effects it added, so it sometimes has to remove too many + (e.g., it might perhaps remove race-related AC bonus, etc.) + //TODO: if I can come up with a better way to mark or detect which effects were added here, this can be fixed + //Check GetEffectCreator? Tie the effect to a unique object so we can identify it? + + Even with the disadvantages, for the builds I've used I find that the advantages make this method preferable. + However, for some builds or environments the Rod of Wonder might still be preferable, so provide the user with an option + by means of the PRC_Shifter_Use_RodOfWonder variable. + */ + if(DEBUG_EFFECTS || DEBUG) + DoDebug("REMOVING EFFECTS: ALTERNATE"); + _prc_inc_shifting_RemoveSpellEffects_CastSpellAtObject(oShifter, bApplyAll); + } +} + +void _prc_inc_shifting_DeleteEffectInts(object oShifter) +{ + DeleteLocalInt(oShifter, "PRC_SHIFTER_EXTRA_STR_WEAPON_MAINHAND"); + DeleteLocalInt(oShifter, "PRC_SHIFTER_EXTRA_STR_WEAPON_OFFHAND"); + DeleteLocalInt(oShifter, "PRC_SHIFTER_EXTRA_DEX_ARMOR"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraSTR"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraSTR_Feats"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraSTR_AttackBonus"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraSTR_DamageBonus"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraSTR_DamageType"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraSTR_SaveAndSkillBonus"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraDEX"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraDEX_Feats"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraDEX_ACBonus"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraDEX_SaveAndSkillBonus"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraCON"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraCON_Feats"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraCON_HPBonus"); + DeleteLocalInt(oShifter, "PRC_Shifter_ExtraCON_SaveAndSkillBonus"); + + DeleteLocalInt(oShifter, "PRC_Shifter_NaturalAC"); + DeleteLocalInt(oShifter, SHIFTER_RESTRICT_SPELLS); + DeleteLocalInt(oShifter, "PRC_Shifter_HarmlessInvisible"); + + DeleteLocalInt(oShifter, SHIFTER_OVERRIDE_RACE); + int nShiftingTrueRace = GetPersistantLocalInt(oShifter, SHIFTER_TRUE_RACE); + if(nShiftingTrueRace) + PRC_Funcs_SetRace(oShifter, nShiftingTrueRace - 1); //Offset by 1 to differentiate value 0 from non-existence + + DeleteLocalInt(oShifter, "PRC_Shifter_AffectsToApply"); +} + +int _prc_inc_shifting_HasExtraFeat(object oPC, string sRemovedFeatList, object oTemplate, int nFeat, int nIPFeat) +{ + if (nFeat == -1 || nIPFeat == -1) + return FALSE; + int bPCHasFeat = GetHasFeat(nFeat, oPC); + string sIPFeat = "("+IntToString(nIPFeat)+")"; + int bDeleted = (FindSubString(sRemovedFeatList, sIPFeat) != -1); + int bTemplateHasFeat = GetHasFeat(nFeat, oTemplate); + return (bPCHasFeat && !bDeleted) || bTemplateHasFeat; +} + +void _prc_inc_shifting_AddExtraFeat(object oPC, string sRemovedFeatList, object oPropertyHolder, int nIPFeat, string sFeatTrackingVariable) +{ + string sIPFeat = "("+IntToString(nIPFeat)+")"; + itemproperty iProp = ItemPropertyBonusFeat(nIPFeat); + if (FindSubString(sRemovedFeatList, sIPFeat) != -1) //TODO: skip this check and always use the delay? + { + //An item property for the same feat was present before and was removed; + //but removed item properties aren't actually removed until the script + //ends. However, this means that the new one we add here is removed. + //So, delay adding it until after the removal takes place. + DelayCommand(0.0f, IPSafeAddItemProperty(oPropertyHolder, iProp, 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING)); + } + else + IPSafeAddItemProperty(oPropertyHolder, iProp, 0.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING); + SetLocalString(oPC, sFeatTrackingVariable, GetLocalString(oPC, sFeatTrackingVariable) + sIPFeat); +} + +int _prc_inc_shifting_TryAddExtraFeat(object oPC, string sRemovedFeatList, object oTemplate, object oPropertyHolder, int nFeat, int nIPFeat, string sFeatTrackingVariable) +{ + int nAddedCount = 0; + if (!_prc_inc_shifting_HasExtraFeat(oPC, sRemovedFeatList, oTemplate, nFeat, nIPFeat)) + { + _prc_inc_shifting_AddExtraFeat(oPC, sRemovedFeatList, oPropertyHolder, nIPFeat, sFeatTrackingVariable); + nAddedCount = 1; + } + return nAddedCount; +} + +int _prc_inc_shifting_TryAddExtraFeats(object oPC, string sRemovedFeatList, object oTemplate, object oPropertyHolder, int nFeatStart, int nFeatEnd, int nIPFeatStart, int nCount, string sFeatTrackingVariable) +{ + int nAddedCount = 0; + int nIPFeat = -1; + int nFeat; + //Find which feats should be added + for (nFeat = nFeatStart; nAddedCount < nCount && nFeat <= nFeatEnd; nFeat++) + { + nIPFeat = nIPFeatStart + (nFeat - nFeatStart); + if (!_prc_inc_shifting_HasExtraFeat(oPC, sRemovedFeatList, oTemplate, nFeat, nIPFeat)) + nAddedCount += 1; + } + //Only need to add the last one, since they're cumulative + if (nAddedCount) + _prc_inc_shifting_AddExtraFeat(oPC, sRemovedFeatList, oPropertyHolder, nIPFeat, sFeatTrackingVariable); + return nAddedCount; +} +/* +void _prc_inc_shifting_SetSTR(object oShifter, int nSTR) +{ + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_STRENGTH, nSTR); + //NWNX does not take into account any racial ability bonus/penalty. For instance, if the race has a + //+2 STR bonus, and we try to set STR to 10, it will instead set to 12. Handle that here. + int nErrorSTR = nSTR - GetAbilityScore(oShifter, ABILITY_STRENGTH, TRUE); + if (nErrorSTR) + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_STRENGTH, nSTR + nErrorSTR); +} + +void _prc_inc_shifting_SetDEX(object oShifter, int nDEX) +{ + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_DEXTERITY, nDEX); + //NWNX does not take into account any racial ability bonus/penalty. For instance, if the race has a + //+2 DEX bonus, and we try to set DEX to 10, it will instead set to 12. Handle that here. + int nErrorDEX = nDEX - GetAbilityScore(oShifter, ABILITY_DEXTERITY, TRUE); + if (nErrorDEX) + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_DEXTERITY, nDEX + nErrorDEX); +} + +void _prc_inc_shifting_SetCON(object oShifter, int nCON) +{ + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_CONSTITUTION, nCON); + //NWNX does not take into account any racial ability bonus/penalty. For instance, if the race has a + //+2 CON bonus, and we try to set CON to 10, it will instead set to 12. Handle that here. + int nErrorCON = nCON - GetAbilityScore(oShifter, ABILITY_CONSTITUTION, TRUE); + if (nErrorCON) + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_CONSTITUTION, nCON + nErrorCON); +} + +void _prc_inc_shifting_SetINT(object oShifter, int nINT) +{ + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_INTELLIGENCE, nINT); + //NWNX does not take into account any racial ability bonus/penalty. For instance, if the race has a + //+2 INT bonus, and we try to set INT to 10, it will instead set to 12. Handle that here. + int nErrorINT = nINT - GetAbilityScore(oShifter, ABILITY_INTELLIGENCE, TRUE); + if (nErrorINT) + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_INTELLIGENCE, nINT + nErrorINT); +} + +void _prc_inc_shifting_SetWIS(object oShifter, int nWIS) +{ + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_WISDOM, nWIS); + //NWNX does not take into account any racial ability bonus/penalty. For instance, if the race has a + //+2 WIS bonus, and we try to set WIS to 10, it will instead set to 12. Handle that here. + int nErrorWIS = nWIS - GetAbilityScore(oShifter, ABILITY_WISDOM, TRUE); + if (nErrorWIS) + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_WISDOM, nWIS + nErrorWIS); +} + +void _prc_inc_shifting_SetCHA(object oShifter, int nCHA) +{ + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_CHARISMA, nCHA); + //NWNX does not take into account any racial ability bonus/penalty. For instance, if the race has a + //+2 CHA bonus, and we try to set CHA to 10, it will instead set to 12. Handle that here. + int nErrorCHA = nCHA - GetAbilityScore(oShifter, ABILITY_CHARISMA, TRUE); + if (nErrorCHA) + PRC_Funcs_SetAbilityScore(oShifter, ABILITY_CHARISMA, nCHA + nErrorCHA); +} +*/ +void _prc_inc_shifting_ApplyTemplate(object oShifter, int nIndex, int nShifterType, object oTemplate, int bShifting, object oShifterPropertyHolder1=OBJECT_INVALID, object oShifterPropertyHolder2=OBJECT_INVALID) +{ + int nShapeGeneration = GetLocalInt(oShifter, PRC_Shifter_ShapeGeneration); + if (nShapeGeneration != nIndex) + { + //Don't apply properties that were scheduled when we were in another shape + if (DEBUG_APPLY_PROPERTIES) + DoDebug("_prc_inc_shifting_ApplyTemplate, exiting--old shape, Shifter Index: " + IntToString(nShapeGeneration)); + return; + } + + if(!GetIsObjectValid(oShifterPropertyHolder1)) + { + //Put some properties on skin because they won't work otherwise-- + //e.g., OnHit properties that should fire when the Shifter is hit by + //an enemy don't fire if they're not on the hide. + //Also, for some reason stat penalty item properties simply won't + //apply on creature weapons but work correctly on the skin. + //All properties applied to the skin need to be reapplied + //whenever the skin is scrubbed. + oShifterPropertyHolder1 = GetPCSkin(oShifter); + } + int bSkinScrubbed = !GetLocalInt(oShifterPropertyHolder1, "PRC_SHIFTER_TEMPLATE_APPLIED"); + //TODO: Use PRC_ScrubPCSkin_Generation instead? + SetLocalInt(oShifterPropertyHolder1, "PRC_SHIFTER_TEMPLATE_APPLIED", TRUE); //This gets cleared by DeletePRCLocalInts (after sleeping, etc.). + + int bApplyProperties1 = bSkinScrubbed; + + if(!GetIsObjectValid(oShifterPropertyHolder2)) + { + object oShifterCWpR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oShifter); + object oShifterCWpL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oShifter); + object oShifterCWpB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oShifter); + //Put some properties on a creature weapon, because having them removed and re-added causes issues + //(e.g. having CON increase removed when your HP is low enough can kill you). + //These properties never need to be reapplied because the creature weapons are never scrubbed. + oShifterPropertyHolder2 = OBJECT_INVALID; + if(GetIsObjectValid(oShifterCWpR)) + oShifterPropertyHolder2 = oShifterCWpR; + if(GetIsObjectValid(oShifterCWpL)) + oShifterPropertyHolder2 = oShifterCWpL; + if(GetIsObjectValid(oShifterCWpB)) + oShifterPropertyHolder2 = oShifterCWpB; + } + int bApplyProperties2 = bShifting && GetIsObjectValid(oShifterPropertyHolder2); + + object oTemplateHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate); + + int bNeedSpellCast = FALSE; //Indicates whether there are any effects that require the spell to apply them + + if(nShifterType != SHIFTER_TYPE_CHANGESHAPE && + nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE) + { + // Copy all itemproperties from the source's hide. No need to check for validity of oTemplateHide - it not + // existing works the same as it existing, but having no iprops. + if(bApplyProperties1) + _prc_inc_shifting_CopyAllItemProperties(oTemplateHide, oShifterPropertyHolder1); + } + + _prc_inc_shifting_DeleteEffectInts(oShifter); + + + + // Ability score adjustments - doesn't apply to Change Shape + if(nShifterType != SHIFTER_TYPE_CHANGESHAPE && + nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE && + nShifterType != SHIFTER_TYPE_ALTER_SELF && + nShifterType != SHIFTER_TYPE_DISGUISE_SELF && + nShifterType != SHIFTER_TYPE_ARANEA) + { + struct _prc_inc_ability_info_struct rInfoStruct = _prc_inc_shifter_GetAbilityInfo(oTemplate, oShifter); + + if (DEBUG_ABILITY_BOOST_CALCULATIONS || DEBUG) + { + DoDebug("Template Creature STR/DEX/CON: " + IntToString(rInfoStruct.nTemplateSTR) + "/" + IntToString(rInfoStruct.nTemplateDEX) + "/" + IntToString(rInfoStruct.nTemplateCON)); + DoDebug("Shifter STR/DEX/CON: " + IntToString(rInfoStruct.nShifterSTR) + "/" + IntToString(rInfoStruct.nShifterDEX) + "/" + IntToString(rInfoStruct.nShifterCON)); + DoDebug("Delta STR/DEX/CON: " + IntToString(rInfoStruct.nDeltaSTR) + "/" + IntToString(rInfoStruct.nDeltaDEX) + "/" + IntToString(rInfoStruct.nDeltaCON)); + DoDebug("Item STR/DEX/CON: " + IntToString(rInfoStruct.nItemSTR) + "/" + IntToString(rInfoStruct.nItemDEX) + "/" + IntToString(rInfoStruct.nItemCON)); + DoDebug("Item Delta STR/DEX/CON: " + IntToString(rInfoStruct.nItemDeltaSTR) + "/" + IntToString(rInfoStruct.nItemDeltaDEX) + "/" + IntToString(rInfoStruct.nItemDeltaCON)); + DoDebug("Extra STR/DEX/CON: " + IntToString(rInfoStruct.nExtraSTR) + "/" + IntToString(rInfoStruct.nExtraDEX) + "/" + IntToString(rInfoStruct.nExtraCON)); + } + + // Set the ability score adjustments as composite bonuses + + { + if(bApplyProperties2 || bApplyProperties1) + { + + //set ability bonus in engine to allow for bonuses greater than 12 (default engine limit) + //SetAbilityBonusLimit(127); + SetAbilityBonusLimit (GetPRCSwitch(PRC_PNP_SHIFTER_BONUS)); + + //clear any existing ability boosts + effect eEffect = GetFirstEffect(oShifter); + while(GetIsEffectValid(eEffect)) + + { + if(GetEffectTag(eEffect) == "ShifterAbilities") + RemoveEffect(oShifter,eEffect); + eEffect = GetNextEffect(oShifter); + } + + //New upper limit checks based on the max bonus of +127 to ability scores, also checking against PRC switch -Barmlot + if (rInfoStruct.nDeltaSTR > 127) + rInfoStruct.nDeltaSTR = 127; + if (rInfoStruct.nDeltaSTR > Max_Bonus) + rInfoStruct.nDeltaSTR = Max_Bonus; + if (rInfoStruct.nDeltaDEX > 127) + rInfoStruct.nDeltaDEX = 127; + if (rInfoStruct.nDeltaDEX > Max_Bonus) + rInfoStruct.nDeltaDEX = Max_Bonus; + if (rInfoStruct.nDeltaCON > 127) + rInfoStruct.nDeltaCON = 127; + if (rInfoStruct.nDeltaCON > Max_Bonus) + rInfoStruct.nDeltaCON = Max_Bonus; + + effect eNewStr; + effect eNewDex; + effect eNewCon; + + //Not sure this positive/negative check is necessary as EffectAbilityIncrease seems to take a signed integer, leaving as is -Barmlot + if (rInfoStruct.nDeltaSTR > 0) + eNewStr = EffectAbilityIncrease(IP_CONST_ABILITY_STR, rInfoStruct.nDeltaSTR); + else + eNewStr = EffectAbilityDecrease(IP_CONST_ABILITY_STR, rInfoStruct.nDeltaSTR); + if (rInfoStruct.nDeltaDEX > 0) + eNewDex = EffectAbilityIncrease(IP_CONST_ABILITY_DEX, rInfoStruct.nDeltaDEX); + else + eNewDex = EffectAbilityDecrease(IP_CONST_ABILITY_DEX, rInfoStruct.nDeltaDEX); + if (rInfoStruct.nDeltaCON > 0) + eNewCon = EffectAbilityIncrease(IP_CONST_ABILITY_CON, rInfoStruct.nDeltaCON); + else + eNewCon = EffectAbilityDecrease(IP_CONST_ABILITY_CON, rInfoStruct.nDeltaCON); + + + + ePhysicalStats = EffectLinkEffects(eNewStr, eNewDex); + ePhysicalStats = EffectLinkEffects(ePhysicalStats, eNewCon); + ePhysicalStats = TagEffect(ePhysicalStats, "ShifterAbilities"); + + if (GetLevelByClass(CLASS_TYPE_PNP_SHIFTER, oShifter) > 5) + ePhysicalStats = SupernaturalEffect(ePhysicalStats); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,ePhysicalStats,oShifter); + } + } + } + // Approximately figure out the template's natural AC bonus + if (nShifterType != SHIFTER_TYPE_CHANGESHAPE && + nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE && + nShifterType != SHIFTER_TYPE_DISGUISE_SELF && + nShifterType != SHIFTER_TYPE_ARANEA) + { + int nNaturalAC = _prc_inc_CreatureNaturalAC(oTemplate); + if(nNaturalAC > 0) + { + bNeedSpellCast = TRUE; + SetLocalInt(oShifter, "PRC_Shifter_NaturalAC", nNaturalAC); + } + } + + // Feats - read from shifter_feats.2da, check if template has it and copy over if it does + // Delayed, since this takes way too long + if(bApplyProperties1) + { + if(nShifterType != SHIFTER_TYPE_CHANGESHAPE + && nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE + && nShifterType != SHIFTER_TYPE_ALTER_SELF + && nShifterType != SHIFTER_TYPE_DISGUISE_SELF + && nShifterType != SHIFTER_TYPE_ARANEA) + { + //Copy feats in chunks because shapes with *many* feats were giving TMI errors + string sFeat; + int CHUNK_SIZE = 25; //50 was too big, so use 25 + int i = 0; + while((sFeat = Get2DACache("shifter_feats", "Feat", i)) != "") + { + DelayCommand(0.0f, _prc_inc_shifting_CopyFeats(oShifter, oTemplate, oShifterPropertyHolder1, i, i+CHUNK_SIZE)); + i += CHUNK_SIZE; + } + } + + DelayCommand(0.1f, _prc_inc_shifting_AddCreatureWeaponFeats(oShifter, oShifterPropertyHolder1)); + + DelayCommand(1.0f, DoWeaponsEquip(oShifter)); //Since our weapon proficiency feats may have changed, reapply weapon feat simulations + //TODO: Handle armor also? + //TODO: Should actually unequip weapon if incorrect size, armor if not proficient (but this might be a pain). + } + + // Casting restrictions if our - inaccurate - check indicates the template can't cast spells + if(!_prc_inc_shifting_GetCanFormCast(oTemplate) && !GetHasFeat(FEAT_PRESTIGE_SHIFTER_NATURALSPELL, oShifter)) + SetLocalInt(oShifter, SHIFTER_RESTRICT_SPELLS, TRUE); + + // Harmless stuff gets invisibility + if(_prc_inc_shifting_GetIsCreatureHarmless(oTemplate)) + { + bNeedSpellCast = TRUE; + SetLocalInt(oShifter, "PRC_Shifter_HarmlessInvisible", TRUE); + } + + //Override racial type. + //Change shape doesn't include this, but there is a feat that gives it to Changelings + if((nShifterType != SHIFTER_TYPE_CHANGESHAPE + && nShifterType != SHIFTER_TYPE_SHIFTER + && nShifterType != SHIFTER_TYPE_POLYMORPH + && nShifterType != SHIFTER_TYPE_DRUID + && nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE + && nShifterType != SHIFTER_TYPE_ALTER_SELF + && nShifterType != SHIFTER_TYPE_DISGUISE_SELF + && nShifterType != SHIFTER_TYPE_ARANEA) + || GetHasFeat(FEAT_RACIAL_EMULATION)) + { + int nRace = MyPRCGetRacialType(oTemplate); + SetLocalInt(oShifter, SHIFTER_OVERRIDE_RACE, nRace + 1); //Offset by 1 to differentiate value 0 from non-existence + + if(GetPersistantLocalInt(oShifter, SHIFTER_TRUE_RACE)) //Only change race if we can get back again when we unshift! + PRC_Funcs_SetRace(oShifter, nRace); + } + + // If something needs permanent effects applied, create a placeable to do the casting in order to bind the effects to a spellID + if(bNeedSpellCast) + { + SetLocalInt(oShifter, PRC_Shifter_ApplyEffects_EvalPRC_Generation, GetLocalInt(oShifter, PRC_EvalPRCFeats_Generation)); + SetLocalInt(oShifter, "PRC_Shifter_AffectsToApply", TRUE); + _prc_inc_shifting_ApplyEffects(oShifter, bShifting); + } + else + { + DeleteLocalInt(oShifter, "PRC_SHIFTER_APPLY_ALL_SPELL_EFFECTS"); + _prc_inc_shifting_RemoveSpellEffects(oShifter, TRUE); + } +} + +/** Internal function. + * Implements the actual shifting bit. Copies creature items, changes appearance, etc + * + * @param oShifter The creature shifting + * @param nShifterType SHIFTER_TYPE_* + * @param oTemplate The template creature + * @param bGainSpellLikeAbilities Whether to create the SLA item + */ +void _prc_inc_shifting_ShiftIntoTemplateAux(object oShifter, int nShifterType, object oTemplate, int bGainSpellLikeAbilities) +{ + if(DEBUG) DoDebug("prc_inc_shifting: _ShiftIntoResRefAux():\n" + + "oShifter = " + DebugObject2Str(oShifter) + "\n" + + "nShifterType = " + IntToString(nShifterType) + "\n" + + "oTemplate = " + DebugObject2Str(oTemplate) + "\n" + + "bGainSpellLikeAbilities = " + DebugBool2String(bGainSpellLikeAbilities) + "\n" + ); + + string sFormName = GetName(oTemplate); + int nRequiredShifterLevel = _prc_inc_shifting_ShifterLevelRequirement(oTemplate); + int nRequiredCharacterLevel = _prc_inc_shifting_CharacterLevelRequirement(oTemplate); + float fChallengeRating = GetChallengeRating(oTemplate); + + // Make sure the template creature is still valid + if(!GetIsObjectValid(oTemplate) || GetObjectType(oTemplate) != OBJECT_TYPE_CREATURE) + { + if(DEBUG) DoDebug("prc_inc_shifting: _ShiftIntoTemplateAux(): ERROR: oTemplate is not a valid object or not a creature: " + DebugObject2Str(oTemplate)); + /// @todo Write a better error message + SendMessageToPCByStrRef(oShifter, STRREF_TEMPLATE_FAILURE); // "Polymorph failed: Failed to create a template of the creature to polymorph into." + + // On failure, unset the mutex right away + SetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX, FALSE); + } + else + { + // Queue unsetting the mutex. Done here so that even if something breaks along the way, this has a good chance of getting executed + DelayCommand(SHIFTER_MUTEX_UNSET_DELAY, SetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX, FALSE)); + + /* Start the actual shifting */ + + // First, clear the shifter's action queue. We'll be assigning a bunch of commands that should get executed ASAP + AssignCommand(oShifter, ClearAllActions(TRUE)); + + // Get the shifter's creature items + object oShifterHide = GetPCSkin(oShifter); // Use the PRC wrapper for this to make sure we get the right object + object oShifterCWpR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oShifter); + object oShifterCWpL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oShifter); + object oShifterCWpB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oShifter); + + // Get the template's creature items + object oTemplateHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate); + object oTemplateCWpR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTemplate); + object oTemplateCWpL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTemplate); + object oTemplateCWpB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTemplate); + + //Changelings don't get the natural attacks + object oCreatureWeapon = OBJECT_INVALID; + if(nShifterType != SHIFTER_TYPE_DISGUISE_SELF) + { + // Handle creature weapons - replace any old weapons with new + // Delete old natural weapons + if(GetIsObjectValid(oShifterCWpR)) MyDestroyObject(oShifterCWpR); + if(GetIsObjectValid(oShifterCWpL)) MyDestroyObject(oShifterCWpL); + if(GetIsObjectValid(oShifterCWpB)) MyDestroyObject(oShifterCWpB); + oShifterCWpR = oShifterCWpL = oShifterCWpR = OBJECT_INVALID; + + // Copy the template's weapons and assign equipping + + if(GetIsObjectValid(oTemplateCWpR)) + { + oShifterCWpR = CopyItem(oTemplateCWpR, oShifter, TRUE); + oCreatureWeapon = oShifterCWpR; + SetIdentified(oShifterCWpR, TRUE); + SafeEquipItem(oShifter, oShifterCWpR, INVENTORY_SLOT_CWEAPON_R); + } + if(GetIsObjectValid(oTemplateCWpL)) + { + oShifterCWpL = CopyItem(oTemplateCWpL, oShifter, TRUE); + oCreatureWeapon = oShifterCWpL; + SetIdentified(oShifterCWpL, TRUE); + SafeEquipItem(oShifter, oShifterCWpL, INVENTORY_SLOT_CWEAPON_L); + } + if(GetIsObjectValid(oTemplateCWpB)) + { + oShifterCWpB = CopyItem(oTemplateCWpB, oShifter, TRUE); + oCreatureWeapon = oShifterCWpB; + SetIdentified(oShifterCWpB, TRUE); + SafeEquipItem(oShifter, oShifterCWpB, INVENTORY_SLOT_CWEAPON_B); + } + if (!GetIsObjectValid(oCreatureWeapon)) + { + //Make a dummy creature weapon that doesn't do anything except hold properties--it is never used as a weapon + //Don't do this if using NWNX funcs: the properties that would normally be applied to the creature + //weapon aren't needed, since NWNX funcs makes the changes directly to the creature instead. + oShifterCWpB = CreateItemOnObject("pnp_shft_cweap", oShifter); //create a shifter blank creature weapon + SetIdentified(oShifterCWpB, TRUE); + oCreatureWeapon = oShifterCWpB; + SafeEquipItem(oShifter, oShifterCWpB, INVENTORY_SLOT_CWEAPON_B); + } + } + //Hide isn't modified for Change Shape - Special Qualities don't transfer + if(nShifterType != SHIFTER_TYPE_CHANGESHAPE && + nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE && + nShifterType != SHIFTER_TYPE_ARANEA) + { + // Handle hide + // Nuke old props and composite bonus tracking - they will be re-evaluated later + ScrubPCSkin(oShifter, oShifterHide); + DeletePRCLocalInts(oShifterHide); + } + + //Do this here instead of letting EvalPRCFeats do it below so that it happens sooner + _prc_inc_shifting_ApplyTemplate(oShifter, GetLocalInt(oShifter, PRC_Shifter_ShapeGeneration), nShifterType, oTemplate, TRUE, oShifterHide, oCreatureWeapon); + + // Ability score adjustments - doesn't apply to Change Shape + if(nShifterType != SHIFTER_TYPE_CHANGESHAPE && + nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE && + nShifterType != SHIFTER_TYPE_ALTER_SELF && + nShifterType != SHIFTER_TYPE_DISGUISE_SELF && + nShifterType != SHIFTER_TYPE_ARANEA) + { + // Get the base delta + int nCreatureCON = GetAbilityScore(oTemplate, ABILITY_CONSTITUTION, TRUE); + int nHealHP = GetHitDice(oShifter) + nCreatureCON; //TODO: Need to take into account Great Constitution and Epic Toughness? + if(!GetPRCSwitch(PRC_PNP_REST_HEALING)) + { + //Wildshape is supposed to heal the same amount as a night's sleep, which by default NWN rules + //is full healing. That would be overpowered, so we'll use only double the PnP healing amount here. + nHealHP *= 2; + } + + { + //Preserve HP loss we had before shifting, but first heal by the amount that wildshaping is supposed to + //(one hitpoint per hit die plus one hitpoint per point of CON) + //Since we temporarily fully healed before shifting, we can assume we have full HP before damaging or healing. + int nOriginalMaxHP = GetLocalInt(oShifter, SHIFTER_ORIGINALMAXHP); + if(nOriginalMaxHP) + { + int nOriginalHP = GetLocalInt(oShifter, SHIFTER_ORIGINALHP); + int nDamageAmount = nOriginalMaxHP - nOriginalHP - nHealHP; + if(nDamageAmount > 0) + { + //TODO: make sure the full damage amount is applied + //(e.g., try a different damage type if immune to this one, + //or damage multiple times if full damage is prevented due + //to partial immunity, etc.). + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamageAmount), oShifter); + } + else + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(-nDamageAmount), oShifter); + SetLocalInt(oShifter, SHIFTER_ORIGINALMAXHP, 0); + } + else + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHealHP), oShifter); + } + } + + // If requested, generate an item for using SLAs + if(bGainSpellLikeAbilities) + { + object oSLAItem = CreateItemOnObject(SHIFTING_SLAITEM_RESREF, oShifter); + // Delayed to prevent potential TMI + DelayCommand(0.0f, _prc_inc_shifting_CreateShifterActiveAbilitiesItem(oShifter, oTemplate, oSLAItem)); + } + + // Change the appearance to that of the template + if(GetAppearanceType(oTemplate) > 5) + SetAppearanceData(oShifter, GetAppearanceData(oTemplate)); + else + { + SetAppearanceData(oShifter, GetAppearanceData(oTemplate)); + SetLocalInt(oShifter, "DynamicAppearance", TRUE); + SetCreatureAppearanceType(oShifter, GetAppearanceType(oTemplate)); + } + + // Some VFX + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_POLYMORPH), oShifter); + + // Set the shiftedness marker + SetPersistantLocalInt(oShifter, SHIFTER_ISSHIFTED_MARKER, TRUE); + + if(nShifterType == SHIFTER_TYPE_ALTER_SELF || + (nShifterType == SHIFTER_TYPE_DISGUISE_SELF + && GetRacialType(oShifter) != RACIAL_TYPE_CHANGELING + && !GetLocalInt(oShifter, "MaskOfFleshInvocation") + && !GetLocalInt(oShifter, "HasFaceChanger"))) + { + int nShiftedNumber = GetPersistantLocalInt(oShifter, "nTimesShifted"); + if(nShiftedNumber > 9) nShiftedNumber = 0; + nShiftedNumber++; + SetPersistantLocalInt(oShifter, "nTimesShifted", nShiftedNumber); + int nMetaMagic = PRCGetMetaMagicFeat(); + int nDuration = PRCGetCasterLevel(oShifter) * 10; + if ((nMetaMagic & METAMAGIC_EXTEND)) + { + nDuration *= 2; + } + DelayCommand(TurnsToSeconds(nDuration), ForceUnshift(oShifter, nShiftedNumber)); + } + + else if(GetLocalInt(oShifter, "HumanoidShapeInvocation")) + { + int nShiftedNumber = GetPersistantLocalInt(oShifter, "nTimesShifted"); + if(nShiftedNumber > 9) nShiftedNumber = 0; + nShiftedNumber++; + SetPersistantLocalInt(oShifter, "nTimesShifted", nShiftedNumber); + DelayCommand(HoursToSeconds(24), ForceUnshift(oShifter, nShiftedNumber)); + } + + else if(GetLocalInt(oShifter, "MaskOfFleshInvocation")) + { + int nShiftedNumber = GetPersistantLocalInt(oShifter, "nTimesShifted"); + if(nShiftedNumber > 9) nShiftedNumber = 0; + nShiftedNumber++; + SetPersistantLocalInt(oShifter, "nTimesShifted", nShiftedNumber); + int nDuration = GetLocalInt(oShifter, "MaskOfFleshInvocation"); + DelayCommand(HoursToSeconds(nDuration), ForceUnshift(oShifter, nShiftedNumber)); + } + + else if(GetLocalInt(oShifter, "HasFaceChanger")) + { + int nShiftedNumber = GetPersistantLocalInt(oShifter, "nTimesShifted"); + if(nShiftedNumber > 9) nShiftedNumber = 0; + nShiftedNumber++; + SetPersistantLocalInt(oShifter, "nTimesShifted", nShiftedNumber); + int nDuration = GetLocalInt(oShifter, "FaceChangerBonus"); + DelayCommand(RoundsToSeconds(nDuration), ForceUnshift(oShifter, nShiftedNumber)); + } + + // Run the class & feat evaluation code + SetPersistantLocalString(oShifter, "PRC_SHIFTING_TEMPLATE_RESREF", GetResRef(oTemplate)); + SetPersistantLocalInt(oShifter, "PRC_SHIFTING_SHIFTER_TYPE", nShifterType); + + _prc_inc_shifting_EvalPRCFeats(oShifter, oShifterHide); + } + + // Print shift information--this is slow, so wait until shifting is done + int bDebug = GetLocalInt(oShifter, "prc_shift_debug"); + DelayCommand(SHIFTER_SHAPE_PRINT_DELAY, _prc_inc_PrintShape(oShifter, oTemplate, bDebug)); + + // Destroy the template creature--we need the template to print, so wait until that's done + DelayCommand(SHIFTER_TEMPLATE_DESTROY_DELAY, MyDestroyObject(oTemplate)); +} + +/** Internal function. + * Does the actual work in unshifting. Restores creature items and + * appearance. If oTemplate is valid, _prc_inc_shifting_ShiftIntoTemplateAux() + * will be called once unshifting is finished. + * + * NOTE: This assumes that all polymorph effects have already been removed. + * + * @param oShifter Creature to unshift + * + * Reshift parameters: + * @param nShifterType Passed to _prc_inc_shifting_ShiftIntoTemplateAux() when reshifting. + * @param oTemplate Passed to _prc_inc_shifting_ShiftIntoTemplateAux() when reshifting. + * @param bGainSpellLikeAbilities Passed to _prc_inc_shifting_ShiftIntoTemplateAux() when reshifting. + */ +void _prc_inc_shifting_UnShiftAux(object oShifter, int nShifterType, object oTemplate, int bGainSpellLikeAbilities) +{ + int bReshift = GetIsObjectValid(oTemplate); + + effect eEffect = GetFirstEffect(oShifter); + while(GetIsEffectValid(eEffect)) + { + if(GetEffectTag(eEffect) == "ShifterAbilities") + RemoveEffect(oShifter,eEffect); + eEffect = GetNextEffect(oShifter); + } + + // Get the shifter's creature items + object oShifterHide = GetPCSkin(oShifter); // Use the PRC wrapper for this to make sure we get the right object + object oShifterCWpR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oShifter); + object oShifterCWpL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oShifter); + object oShifterCWpB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oShifter); + + int nAdjustHP = bReshift && ( + nShifterType != SHIFTER_TYPE_CHANGESHAPE + && nShifterType != SHIFTER_TYPE_HUMANOIDSHAPE + && nShifterType != SHIFTER_TYPE_ALTER_SELF + && nShifterType != SHIFTER_TYPE_DISGUISE_SELF + && nShifterType != SHIFTER_TYPE_ARANEA + //TODO?: && nShifterType != SHIFTER_TYPE_NONE + ); + if(nAdjustHP) + { + int nOriginalHP = GetCurrentHitPoints(oShifter); + int nOriginalMaxHP = GetMaxHitPoints(oShifter); + SetLocalInt(oShifter, SHIFTER_ORIGINALHP, nOriginalHP); + SetLocalInt(oShifter, SHIFTER_ORIGINALMAXHP, nOriginalMaxHP); + + //Before unshifting, fully heal the shifter. After shifting, some of the added hitpoints will be taken away again. + //This is to prevent the Shifter from dying when shifting from one high CON form to another high CON form + //due to the temporary loss of CON caused by shifting into the low-CON true form in between. + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nOriginalMaxHP), oShifter); + + } + + // Clear the hide. We'll have to run EvalPRCFeats() later on + ScrubPCSkin(oShifter, oShifterHide); + DeletePRCLocalInts(oShifterHide); + GetPersistantLocalInt(oShifter, SHIFTER_TRUEAPPEARANCE); + //HACK: For some reason, the next call to this function after the line above it returns FALSE even if it should return TRUE, + //but the next call to it after that returns the correct result. So, call it here: this makes it return the correct + //result for the RestoreTrueAppearance call below. + //TODO: REMOVE when workaround no longer needed + //TODO: is this because of the delays in DeletePRCLocalInts? + + // Nuke the creature weapons. If the normal form is supposed to have natural weapons, they'll get re-constructed + if(GetIsObjectValid(oShifterCWpR)) MyDestroyObject(oShifterCWpR); + if(GetIsObjectValid(oShifterCWpL)) MyDestroyObject(oShifterCWpL); + if(GetIsObjectValid(oShifterCWpB)) MyDestroyObject(oShifterCWpB); + + object oSLAItem = GetItemPossessedBy(oShifter, SHIFTING_SLAITEM_TAG); + int bCheckForAuraEffects = FALSE; + if(GetIsObjectValid(oSLAItem)) + { + MyDestroyObject(oSLAItem); + bCheckForAuraEffects = TRUE; + } + + // Remove effects + _prc_inc_shifting_DeleteEffectInts(oShifter); + + if (!bReshift) + _prc_inc_shifting_RemoveSpellEffects(oShifter, TRUE); + + effect eTest = GetFirstEffect(oShifter); + if (bCheckForAuraEffects) + { + while(GetIsEffectValid(eTest)) + { + int nEffectSpellID = GetEffectSpellId(eTest); + + if(nEffectSpellID == SPELLABILITY_AURA_BLINDING || + nEffectSpellID == SPELLABILITY_AURA_COLD || + nEffectSpellID == SPELLABILITY_AURA_ELECTRICITY || + nEffectSpellID == SPELLABILITY_AURA_FEAR || + nEffectSpellID == SPELLABILITY_AURA_FIRE || + nEffectSpellID == SPELLABILITY_AURA_MENACE || + nEffectSpellID == SPELLABILITY_AURA_PROTECTION || + nEffectSpellID == SPELLABILITY_AURA_STUN || + nEffectSpellID == SPELLABILITY_AURA_UNEARTHLY_VISAGE || + nEffectSpellID == SPELLABILITY_AURA_UNNATURAL || + nEffectSpellID == SPELLABILITY_DRAGON_FEAR + ) + { + RemoveEffect(oShifter, eTest); + } + + eTest = GetNextEffect(oShifter); + } + } + + // Restore appearance + if(!RestoreTrueAppearance(oShifter)) + { + string sError = "prc_inc_shifting: _UnShiftAux(): ERROR: Unable to restore true form for " + DebugObject2Str(oShifter); + if(DEBUG) DoDebug(sError); + else WriteTimestampedLogEntry(sError); + } + + // Unset the shiftedness marker + SetPersistantLocalInt(oShifter, SHIFTER_ISSHIFTED_MARKER, FALSE); + + _prc_inc_shifting_EvalPRCFeats(oShifter, oShifterHide); + + // Queue reshifting to happen if needed. Let a short while pass so any fallout from the unshift gets handled + if(bReshift) + DelayCommand(1.0f, _prc_inc_shifting_ShiftIntoTemplateAux(oShifter, nShifterType, oTemplate, bGainSpellLikeAbilities)); + else + SetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX, FALSE); +} + +/** Internal function. + * A polymorph effect was encountered during unshifting and removed. We need to + * wait until it's actually removed (instead of merely gone from the active effects + * list on oShifter) before calling _prc_inc_shifting_UnShiftAux(). + * This is done by tracking the contents of the creature armour slot. The object in + * it will change when the polymorph is really removed. + * + * @param oShifter The creature whose creature armour slot to monitor. + * @param oSkin The skin object that was in the slot when the UnShift() call that triggered + * this was run. + * @param nRepeats Number of times this function has repeated the delay. Used to track timeout + */ +void _prc_inc_shifting_UnShiftAux_SeekPolyEnd(object oShifter, object oSkin, int nRepeats = 0) +{ + // Over 15 seconds passed, something is wrong + if(nRepeats++ > 100) + { + if(DEBUG) DoDebug("prc_inc_shifting: _UnShiftAux_SeekPolyEnd(): ERROR: Repeated over 100 times, skin object remains the same."); + return; + } + + // See if the skin object has changed. When it does, the polymorph is genuinely gone instead of just being removed from the effects list + if(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oShifter) == oSkin) + DelayCommand(0.15f, _prc_inc_shifting_UnShiftAux_SeekPolyEnd(oShifter, oSkin, nRepeats)); + // It's gone, finish unshifting + else + _prc_inc_shifting_UnShiftAux(oShifter, SHIFTER_TYPE_NONE, OBJECT_INVALID, FALSE); +} + +object _prc_inc_load_template_from_resref(string sResRef, int nHD) +{ + /* Create the template to shift into */ + // Get the waypoint in Limbo where shifting template creatures are spawned + object oSpawnWP = GetWaypointByTag(SHIFTING_TEMPLATE_WP_TAG); + // Paranoia check - the WP should be built into the area data of Limbo + if(!GetIsObjectValid(oSpawnWP)) + { + if(DEBUG) DoDebug("prc_inc_shifting: ShiftIntoResRef(): ERROR: Template spawn waypoint does not exist."); + // Create the WP + oSpawnWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(GetObjectByTag("HEARTOFCHAOS")), FALSE, SHIFTING_TEMPLATE_WP_TAG); + } + + // Get the WP's location + location lSpawn = GetLocation(oSpawnWP); + + // And spawn an instance of the given template there + object oTemplate = CreateObject(OBJECT_TYPE_CREATURE, sResRef, lSpawn); + + /* + Some modules create low-level templates and level them up. + Until now, when we tried learning the higher-level version, we ended up with the + lower-level template instead. This level-up code takes care of that. + + Properly, we should remember what level we actually learned it at and only level that + far, instead of leveling up to our current level. I've chosen to do it this way + for the following reasons: + > Doing it properly is more intrusive and more error-prone coding. For now, at least, + I don't think it makes sense to do that. + > Doing it properly way would litter our known shapes list with the same creature at different + levels, when all we really want is the highest level one we can use. + > From a role-playing point of view, shapes implemented that way by a module are presumably + common in the module, so a shifter would be pretty familiar with them and so would generally + have learned the highest level he can shift into. + */ + int bTemplateCanLevel = FALSE; + if (LevelUpHenchman(oTemplate)) + { + bTemplateCanLevel = TRUE; + MyDestroyObject(oTemplate); + oTemplate = CreateObject(OBJECT_TYPE_CREATURE, sResRef, lSpawn); + } + if(bTemplateCanLevel) + { + int nNeedLevels = nHD - GetHitDice(oTemplate); + if (GetPRCSwitch(PNP_SHFT_USECR)) + { + int i; + for (i=0; i<40; i+=1) + { + if (GetChallengeRating(oTemplate) > IntToFloat(nHD)) + break; + if (!LevelUpHenchman(oTemplate)) + break; + } + nNeedLevels = GetHitDice(oTemplate); + if (GetChallengeRating(oTemplate) > IntToFloat(nHD)) + nNeedLevels -= 1; + MyDestroyObject(oTemplate); + oTemplate = CreateObject(OBJECT_TYPE_CREATURE, sResRef, lSpawn); + nNeedLevels -= GetHitDice(oTemplate); + } + while (nNeedLevels > 0) + { + if (!LevelUpHenchman(oTemplate)) + break; + nNeedLevels -= 1; + } + SetLocalInt(oTemplate, "prc_template_can_level", 1); + } + + return oTemplate; +} + +string _prc_inc_get_stored_name_from_template(object oTemplate) +{ + string sSuffix; + if(GetLocalInt(oTemplate, "prc_template_can_level")) + sSuffix = "+"; + string sResult = GetStringByStrRef(57438+0x01000000); + sResult = ReplaceString(sResult, "%(NAME)", GetName(oTemplate)); + sResult = ReplaceString(sResult, "%(SL)", IntToString(_prc_inc_shifting_ShifterLevelRequirement(oTemplate))); + sResult = ReplaceString(sResult, "%(CL)", IntToString(GetHitDice(oTemplate)) + sSuffix); + sResult = ReplaceString(sResult, "%(CR)", FloatToString(GetChallengeRating(oTemplate), 4, 1) + sSuffix); + return sResult; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int StoreCurrentRaceAsTrueRace(object oShifter) +{ + if (GetPersistantLocalInt(oShifter, SHIFTER_ISSHIFTED_MARKER)) + return FALSE; + + if (IsPolymorphed(oShifter)) + return FALSE; + + SetPersistantLocalInt(oShifter, SHIFTER_TRUE_RACE, MyPRCGetRacialType(oShifter) + 1); + + return TRUE; +} + +int StoreCurrentAppearanceAsTrueAppearance(object oShifter, int bCarefull = TRUE) +{ + // If requested, check that the creature isn't shifted or polymorphed + if(bCarefull) + { + if (GetPersistantLocalInt(oShifter, SHIFTER_ISSHIFTED_MARKER)) + return FALSE; + + if (IsPolymorphed(oShifter)) + return FALSE; + } + + // Get the appearance data + struct appearancevalues appval = GetAppearanceData(oShifter); + + // Store it + SetPersistantLocalAppearancevalues(oShifter, SHIFTER_TRUEAPPEARANCE, appval); + SetPersistantLocalInt(oShifter, "TrueFormAppearanceType", GetAppearanceType(oShifter)); + + // Set a marker that tells that the true appearance is stored + SetPersistantLocalInt(oShifter, SHIFTER_TRUEAPPEARANCE, TRUE); + + return TRUE; +} + +int RestoreTrueAppearance(object oShifter) +{ + // Check for the "true appearance stored" marker. Abort if it's not present + if(!GetPersistantLocalInt(oShifter, SHIFTER_TRUEAPPEARANCE)) + return FALSE; + + // See if the character is polymorphed. Won't restore the appearance if it is + if (IsPolymorphed(oShifter)) + return FALSE; + + // We got this far, everything should be OK + // Retrieve the appearance data + struct appearancevalues appval = GetPersistantLocalAppearancevalues(oShifter, SHIFTER_TRUEAPPEARANCE); + + // Apply it to the creature + SetAppearanceData(oShifter, appval); + + if(GetLocalInt(oShifter, "DynamicAppearance")) + { + SetCreatureAppearanceType(oShifter, GetPersistantLocalInt(oShifter, "TrueFormAppearanceType")); + DeleteLocalInt(oShifter, "DynamicAppearance"); + } + + // Inform caller of success + return TRUE; +} + + +// Storage functions // + +int StoreShiftingTemplate(object oShifter, int nShifterType, object oTarget) +{ + // Some paranoia - both the target and the object to store on must be valid. And PCs are never legal for storage - PC resref should be always empty + if(!(GetIsObjectValid(oShifter) && GetIsObjectValid(oTarget) && GetResRef(oTarget) != "")) + return FALSE; + + string sResRefsArray = SHIFTER_RESREFS_ARRAY + IntToString(nShifterType); + string sNamesArray = SHIFTER_NAMES_ARRAY + IntToString(nShifterType); + string sRacialTypeArray = SHIFTER_RACIALTYPE_ARRAY + IntToString(nShifterType); + + // Determine array existence + if(!persistant_array_exists(oShifter, sResRefsArray)) + persistant_array_create(oShifter, sResRefsArray); + if(!persistant_array_exists(oShifter, sNamesArray)) + persistant_array_create(oShifter, sNamesArray); + if(!persistant_array_exists(oShifter, sRacialTypeArray)) + persistant_array_create(oShifter, sRacialTypeArray); + + // Get the storeable data + string sResRef = GetResRef(oTarget); + string sName = _prc_inc_get_stored_name_from_template(oTarget); + string sRacialType = IntToString(MyPRCGetRacialType(oTarget)); + int nArraySize = persistant_array_get_size(oShifter, sResRefsArray); + + // Check for the template already being present + if(_prc_inc_shifting_GetIsTemplateStored(oShifter, nShifterType, sResRef)) + return FALSE; + + persistant_array_set_string(oShifter, sResRefsArray, nArraySize, sResRef); + persistant_array_set_string(oShifter, sNamesArray, nArraySize, sName); + persistant_array_set_string(oShifter, sRacialTypeArray, nArraySize, sRacialType); + + return TRUE; +} + +int GetNumberOfStoredTemplates(object oShifter, int nShifterType) +{ + if(!persistant_array_exists(oShifter, SHIFTER_RESREFS_ARRAY + IntToString(nShifterType))) + return 0; + + return persistant_array_get_size(oShifter, SHIFTER_RESREFS_ARRAY + IntToString(nShifterType)); +} + +string GetStoredTemplate(object oShifter, int nShifterType, int nIndex) +{ + return persistant_array_get_string(oShifter, SHIFTER_RESREFS_ARRAY + IntToString(nShifterType), nIndex); +} + +string GetStoredTemplateFilter(object oShifter, int nShifterType) +{ + string sResult = "|"; + int nCount = GetNumberOfStoredTemplates(oShifter, nShifterType); + int i; + for(i=0; i nArraySize) + return FALSE; + + string sName = persistant_array_get_string(oShifter, sNamesArray, nIndex); + return GetStringLeft(sName, nDeletedPrefixLen) == SHIFTER_DELETED_SHAPE_PREFIX; +} + +void SetStoredTemplateDeleteMark(object oShifter, int nShifterType, int nIndex, int bMark) +{ + int nDeletedPrefixLen = GetStringLength(SHIFTER_DELETED_SHAPE_PREFIX); + string sNamesArray = SHIFTER_NAMES_ARRAY + IntToString(nShifterType); + + if(!persistant_array_exists(oShifter, sNamesArray)) + return; + + int nArraySize = persistant_array_get_size(oShifter, sNamesArray); + if (nIndex > nArraySize) + return; + + string sName = persistant_array_get_string(oShifter, sNamesArray, nIndex); + if (GetStringLeft(sName, nDeletedPrefixLen) == SHIFTER_DELETED_SHAPE_PREFIX) + { + if (!bMark) + sName = GetStringRight(sName, GetStringLength(sName)-nDeletedPrefixLen); + } + else + { + if (bMark) + sName = SHIFTER_DELETED_SHAPE_PREFIX + sName; + } + persistant_array_set_string(oShifter, sNamesArray, nIndex, sName); +} + +void DeleteMarkedStoredTemplates(object oShifter, int nShifterType, int nSrc=0, int nDst=0) +{ + int nDeletedPrefixLen = GetStringLength(SHIFTER_DELETED_SHAPE_PREFIX); + string sResRefsArray = SHIFTER_RESREFS_ARRAY + IntToString(nShifterType); + string sNamesArray = SHIFTER_NAMES_ARRAY + IntToString(nShifterType); + string sRacialTypeArray = SHIFTER_RACIALTYPE_ARRAY + IntToString(nShifterType); + + // Determine array existence + if(!persistant_array_exists(oShifter, sResRefsArray)) + return; + if(!persistant_array_exists(oShifter, sNamesArray)) + return; + int nRacialTypeArrayExists = persistant_array_exists(oShifter, sRacialTypeArray); + + // Move array entries, skipping the marked ones + int nCount = 0, nArraySize = persistant_array_get_size(oShifter, sResRefsArray); + string sName, sResRef, sRace; + while(nSrc < nArraySize && nCount++ < CHUNK_SIZE) + { + sName = persistant_array_get_string(oShifter, sNamesArray, nSrc); + if (GetStringLeft(sName, nDeletedPrefixLen) != SHIFTER_DELETED_SHAPE_PREFIX) + { + if (nSrc != nDst) + { + persistant_array_set_string(oShifter, sNamesArray, nDst, sName); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nSrc); + persistant_array_set_string(oShifter, sResRefsArray, nDst, sResRef); + if (nRacialTypeArrayExists) + { + sRace = persistant_array_get_string(oShifter, sRacialTypeArray, nSrc); + persistant_array_set_string(oShifter, sRacialTypeArray, nDst, sRace); + } + } + nDst++; + } + else + { + DoDebug("Deleting shape: " + sName); + } + nSrc++; + } + + if (nSrc < nArraySize) + DelayCommand(0.0f, DeleteMarkedStoredTemplates(oShifter, nShifterType, nSrc, nDst)); + else + { + // Shrink the arrays + persistant_array_shrink(oShifter, sResRefsArray, nDst); + persistant_array_shrink(oShifter, sNamesArray, nDst); + if(nRacialTypeArrayExists) + persistant_array_shrink(oShifter, sRacialTypeArray, nDst); + } +} + + +// Shifting-related functions + +int GetCreatureIsKnown(object oShifter, int nShifterType, object oTemplate) +{ + int nReturn = 0; + + if(GetIsObjectValid(oShifter) && GetIsObjectValid(oTemplate)) + nReturn = _prc_inc_shifting_GetIsTemplateStored(oShifter, nShifterType, GetResRef(oTemplate)); + + return nReturn; +} + +string FindResRefFromString(object oShifter, int nShifterType, string sFindString, int bList) +{ + string sResRef, sResRefsArray = SHIFTER_RESREFS_ARRAY + IntToString(nShifterType); + string sName, sNamesArray = SHIFTER_NAMES_ARRAY + IntToString(nShifterType); + int nResRef, nArraySize = persistant_array_get_size(oShifter, sResRefsArray); + + //Check for current shape + + if(sFindString == ".") + { + sResRef = GetPersistantLocalString(oShifter, "PRC_SHIFTING_TEMPLATE_RESREF"); + if(sResRef == "") + DoDebug("Current shape match: NOT SHIFTED"); + else + { + nResRef = _prc_inc_shifting_GetIsTemplateStored(oShifter, nShifterType, sResRef) - 1; + if(nResRef >= 0) + sName = persistant_array_get_string(oShifter, sNamesArray, nResRef); + else + sName = "???"; + DoDebug("Current shape match: " + IntToString(nResRef+1) + " = " + sName + " [" + sResRef + "]"); + } + return sResRef; + } + + //Check for shape numbers + + int nShapeNumberMatch = StringToInt(sFindString); + if(nShapeNumberMatch >= 1 && nShapeNumberMatch <= nArraySize) + { + nShapeNumberMatch -= 1; + sName = persistant_array_get_string(oShifter, sNamesArray, nShapeNumberMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nShapeNumberMatch); + DoDebug("Shape number match: #" + IntToString(nShapeNumberMatch+1) + " = " + sName + " [" + sResRef + "]"); + return sResRef; + } + + //Check for quick shift slots numbers + + string sFirstLetter = GetStringLeft(sFindString, 1); + if(sFirstLetter == "q" || sFirstLetter == "Q") + { + int nQuickSlotMatch = StringToInt(GetStringRight(sFindString, GetStringLength(sFindString)-1)); + if(nQuickSlotMatch >= 1 && nQuickSlotMatch <= 10) + { + sResRef = GetPersistantLocalString(oShifter, "PRC_Shifter_Quick_" + IntToString(nQuickSlotMatch) + "_ResRef"); + nResRef = _prc_inc_shifting_GetIsTemplateStored(oShifter, nShifterType, sResRef) - 1; + if(nResRef >= 0) + sName = persistant_array_get_string(oShifter, sNamesArray, nResRef); + else + sName = "???"; + if(sResRef == "") + DoDebug("Quickslot match: Q" + IntToString(nQuickSlotMatch) + " = EMPTY QUICKSLOT"); + else + DoDebug("Quickslot match: Q" + IntToString(nQuickSlotMatch) + " = " + sName + " [" + sResRef + "]"); + return sResRef; + } + } + + //Check for matching resref first (exact case) + + int nResRefMatch = _prc_inc_shifting_GetIsTemplateStored(oShifter, nShifterType, sFindString) - 1; + if(nResRefMatch >= 0) + { + sName = persistant_array_get_string(oShifter, sNamesArray, nResRefMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nResRefMatch); + DoDebug("ResRef match: "+ IntToString(nResRefMatch+1) + " = " + sName + " [" + sResRef + "]"); + return sResRef; + } + + //Check for matching name next (ignoring case) + + sFindString = GetStringLowerCase(sFindString); + + int nExactMatch = -1, nPrefixMatch = -1, nPartialMatch = -1; + int nExactMatchCount = 0, nPrefixMatchCount = 0, nPartialMatchCount = 0; + int i, nFind; + string sLowerName; + for(i = 0; i < nArraySize; i++) + { + sName = persistant_array_get_string(oShifter, sNamesArray, i); + nFind = FindSubString(sName, " ("); + if(nFind != -1) + sName = GetStringLeft(sName, nFind); //Remove the part in parens that tells shape HD, CR, etc. + sLowerName = GetStringLowerCase(sName); + + if (bList && sFindString == "") + { + sName = persistant_array_get_string(oShifter, sNamesArray, i); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, i); + DoDebug("#" + IntToString(i+1) + " = " + sName + " [" + sResRef + "]"); + } + else if(sFindString == sLowerName) + { + nExactMatch = i; + nExactMatchCount += 1; + sName = persistant_array_get_string(oShifter, sNamesArray, nExactMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nExactMatch); + DoDebug("Exact match: #" + IntToString(i+1) + " = " + sName + " [" + sResRef + "]"); + } + else + { + //TODO: multi-word prefix matches + nFind = FindSubString(sLowerName, sFindString); + if(nFind == 0) + { + nPrefixMatch = i; + nPrefixMatchCount += 1; + sName = persistant_array_get_string(oShifter, sNamesArray, nPrefixMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nPrefixMatch); + DoDebug("Beginning match: #" + IntToString(i+1) + " = " + sName + " [" + sResRef + "]"); + } + else if(nFind > 0) + { + nPartialMatch = i; + nPartialMatchCount += 1; + sName = persistant_array_get_string(oShifter, sNamesArray, nPartialMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nPartialMatch); + DoDebug("Middle match: #" + IntToString(i+1) + " = " + sName + " [" + sResRef + "]"); + } + } + } + + if(nExactMatchCount) + { + if(nExactMatchCount > 1) + { + DoDebug("TOO MANY EXACT MATCHES (" +IntToString(nExactMatchCount) + " found)"); + return ""; + } + sName = persistant_array_get_string(oShifter, sNamesArray, nExactMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nExactMatch); + DoDebug("FINAL MATCH: #" + IntToString(nExactMatch+1) + " = " + sName + " [" + sResRef + "]"); + return sResRef; + } + if(nPrefixMatchCount) + { + if(nPrefixMatchCount > 1) + { + DoDebug("TOO MANY BEGINNING MATCHES (" +IntToString(nPrefixMatchCount) + " found)"); + return ""; + } + sName = persistant_array_get_string(oShifter, sNamesArray, nPrefixMatch); + sResRef = persistant_array_get_string(oShifter, sResRefsArray, nPrefixMatch); + DoDebug("FINAL MATCH: #" + IntToString(nPrefixMatch+1) + " = " + sName + " [" + sResRef + "]"); + return sResRef; + } + if(nPartialMatchCount) + { + if(nPartialMatchCount > 1) + { + DoDebug("TOO MANY MIDDLE MATCHES (" +IntToString(nPartialMatchCount) + " found)"); + return ""; + } + sName = persistant_array_get_string(oShifter, sNamesArray, nPartialMatch); + sResRef= persistant_array_get_string(oShifter, sResRefsArray, nPartialMatch); + DoDebug("FINAL MATCH: #" + IntToString(nPartialMatch+1) + " = " + sName + " [" + sResRef + "]"); + return sResRef; + } + + DoDebug("NO MATCH"); + + return ""; +} + +int GetCanShiftIntoCreature(object oShifter, int nShifterType, object oTemplate) +{ + if (!GetPersistantLocalInt(oShifter, "PRC_UNI_SHIFT_SCRIPT")) + { + SetPersistantLocalInt(oShifter, "PRC_UNI_SHIFT_SCRIPT", 1); + ExecuteScript("prc_uni_shift", oShifter); + } + + // Base assumption: Can shift into the target + int bReturn = TRUE; + + // Some basic checks + if(GetIsObjectValid(oShifter) && GetIsObjectValid(oTemplate)) + { + // PC check + if(GetIsPC(oTemplate)) + { + bReturn = FALSE; + SendMessageToPCByStrRef(oShifter, STRREF_NOPOLYTOPC); // "You cannot polymorph into a PC." + } + // Shifting prevention feat + else if(GetHasFeat(SHIFTER_BLACK_LIST, oTemplate)) + { + bReturn = FALSE; + SendMessageToPCByStrRef(oShifter, STRREF_FORBIDPOLY); // "Target cannot be polymorphed into." + } + + // Test switch-based limitations + if(bReturn) + { + int nSize = PRCGetCreatureSize(oTemplate); + int nRacialType = MyPRCGetRacialType(oTemplate); + + // Size switches + if(nSize >= CREATURE_SIZE_HUGE && GetPRCSwitch(PNP_SHFT_S_HUGE)) + bReturn = FALSE; + if(nSize == CREATURE_SIZE_LARGE && GetPRCSwitch(PNP_SHFT_S_LARGE)) + bReturn = FALSE; + if(nSize == CREATURE_SIZE_MEDIUM && GetPRCSwitch(PNP_SHFT_S_MEDIUM)) + bReturn = FALSE; + if(nSize == CREATURE_SIZE_SMALL && GetPRCSwitch(PNP_SHFT_S_SMALL)) + bReturn = FALSE; + if(nSize <= CREATURE_SIZE_TINY && GetPRCSwitch(PNP_SHFT_S_TINY)) + bReturn = FALSE; + + // Type switches + if(nRacialType == RACIAL_TYPE_OUTSIDER && GetPRCSwitch(PNP_SHFT_F_OUTSIDER)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_ELEMENTAL && GetPRCSwitch(PNP_SHFT_F_ELEMENTAL)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_CONSTRUCT && GetPRCSwitch(PNP_SHFT_F_CONSTRUCT)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_UNDEAD && GetPRCSwitch(PNP_SHFT_F_UNDEAD)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_DRAGON && GetPRCSwitch(PNP_SHFT_F_DRAGON)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_ABERRATION && GetPRCSwitch(PNP_SHFT_F_ABERRATION)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_OOZE && GetPRCSwitch(PNP_SHFT_F_OOZE)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_MAGICAL_BEAST && GetPRCSwitch(PNP_SHFT_F_MAGICALBEAST)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_GIANT && GetPRCSwitch(PNP_SHFT_F_GIANT)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_VERMIN && GetPRCSwitch(PNP_SHFT_F_VERMIN)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_BEAST && GetPRCSwitch(PNP_SHFT_F_BEAST)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_ANIMAL && GetPRCSwitch(PNP_SHFT_F_ANIMAL)) + bReturn = FALSE; + if(nRacialType == RACIAL_TYPE_HUMANOID_MONSTROUS && GetPRCSwitch(PNP_SHFT_F_MONSTROUSHUMANOID)) + bReturn = FALSE; + if(GetPRCSwitch(PNP_SHFT_F_HUMANOID) && + (nRacialType == RACIAL_TYPE_DWARF || + nRacialType == RACIAL_TYPE_ELF || + nRacialType == RACIAL_TYPE_GNOME || + nRacialType == RACIAL_TYPE_HUMAN || + nRacialType == RACIAL_TYPE_HALFORC || + nRacialType == RACIAL_TYPE_HALFELF || + nRacialType == RACIAL_TYPE_HALFLING || + nRacialType == RACIAL_TYPE_HUMANOID_ORC || + nRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + )) + bReturn = FALSE; + + if(!bReturn) + SendMessageToPCByStrRef(oShifter, STRREF_SETTINGFORBID); // "The module settings prevent this creature from being polymorphed into." + } + + // Still OK, test HD or CR + if(bReturn) + { + // Check target's HD or CR + int nShifterHD = GetHitDice(oShifter); + int nTemplateHD = _prc_inc_shifting_CharacterLevelRequirement(oTemplate); + if(nTemplateHD > nShifterHD) + { + bReturn = FALSE; + // "You need X more character levels before you can take on that form." + SendMessageToPC(oShifter, GetStringByStrRef(STRREF_YOUNEED) + " " + IntToString(nTemplateHD - nShifterHD) + " " + GetStringByStrRef(STRREF_MORECHARLVL)); + } + + if(nShifterType == SHIFTER_TYPE_ALTER_SELF && nTemplateHD > 5) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is too high a level to copy with this spell."); + } + }// end if - Checking HD or CR + + // Move onto shifting type-specific checks if there haven't been any problems yet + if(bReturn) + { + if(nShifterType == SHIFTER_TYPE_SHIFTER) + { + int nShifterLevel = GetLevelByClass(CLASS_TYPE_PNP_SHIFTER, oShifter); + + int nRacialType = MyPRCGetRacialType(oTemplate); + + // Fey and shapechangers are forbidden targets for PnP Shifter + if(nRacialType == RACIAL_TYPE_FEY || nRacialType == RACIAL_TYPE_SHAPECHANGER) + { + bReturn = FALSE; + SendMessageToPCByStrRef(oShifter, STRREF_PNPSFHT_FEYORSSHIFT); // "You cannot use PnP Shifter abilities to polymorph into this creature." + } + else + { + // Test level required + int nLevelRequired = _prc_inc_shifting_ShifterLevelRequirement(oTemplate); + if(nLevelRequired > nShifterLevel) + { + bReturn = FALSE; + // "You need X more PnP Shifter levels before you can take on that form." + SendMessageToPC(oShifter, GetStringByStrRef(STRREF_YOUNEED) + " " + IntToString(nLevelRequired - nShifterLevel) + " " + GetStringByStrRef(STRREF_PNPSHFT_MORELEVEL)); + } + }// end else - Not outright forbidden due to target being Fey or Shapeshifter + }// end if - PnP Shifter checks + + //Change Shape checks + else if(nShifterType == SHIFTER_TYPE_HUMANOIDSHAPE) + { + int nTargetSize = PRCGetCreatureSize(oTemplate); + int nRacialType = MyPRCGetRacialType(oTemplate); + int nShifterSize = PRCGetCreatureSize(oShifter); + + int nSizeDiff = nTargetSize - nShifterSize; + + if(nSizeDiff > 1 || nSizeDiff < -1) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is too large or too small."); + } + + if(!(nRacialType == RACIAL_TYPE_DWARF || + nRacialType == RACIAL_TYPE_ELF || + nRacialType == RACIAL_TYPE_GNOME || + nRacialType == RACIAL_TYPE_HUMAN || + nRacialType == RACIAL_TYPE_HALFORC || + nRacialType == RACIAL_TYPE_HALFELF || + nRacialType == RACIAL_TYPE_HALFLING || + nRacialType == RACIAL_TYPE_HUMANOID_ORC || + nRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + )) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is not a humanoid racial type."); + } + } + + //Changeling check + else if(nShifterType == SHIFTER_TYPE_DISGUISE_SELF && GetRacialType(oShifter) == RACIAL_TYPE_CHANGELING) + { + int nSize = PRCGetCreatureSize(oTemplate); + int nRacialType = MyPRCGetRacialType(oTemplate); + int nShifterSize = PRCGetCreatureSize(oShifter); + + if(nSize != nShifterSize) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is too large or too small."); + } + + if(!(nRacialType == RACIAL_TYPE_DWARF || + nRacialType == RACIAL_TYPE_ELF || + nRacialType == RACIAL_TYPE_GNOME || + nRacialType == RACIAL_TYPE_HUMAN || + nRacialType == RACIAL_TYPE_HALFORC || + nRacialType == RACIAL_TYPE_HALFELF || + nRacialType == RACIAL_TYPE_HALFLING || + nRacialType == RACIAL_TYPE_HUMANOID_ORC || + nRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + )) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is not a humanoid racial type."); + } + } + + //Generic check + else if(nShifterType == SHIFTER_TYPE_CHANGESHAPE + || nShifterType == SHIFTER_TYPE_ALTER_SELF + || (nShifterType == SHIFTER_TYPE_DISGUISE_SELF && GetRacialType(oShifter) != RACIAL_TYPE_CHANGELING)) + { + int nTargetSize = PRCGetCreatureSize(oTemplate); + int nTargetRacialType = MyPRCGetRacialType(oTemplate); + int nShifterSize = PRCGetCreatureSize(oShifter); + int nShifterRacialType = MyPRCGetRacialType(oShifter); + + int nSizeDiff = nTargetSize - nShifterSize; + + if(nSizeDiff > 1 || nSizeDiff < -1) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is too large or too small."); + } + + if(!(nTargetRacialType == nShifterRacialType || + //check for humanoid type + ((nTargetRacialType == RACIAL_TYPE_DWARF || + nTargetRacialType == RACIAL_TYPE_ELF || + nTargetRacialType == RACIAL_TYPE_GNOME || + nTargetRacialType == RACIAL_TYPE_HUMAN || + nTargetRacialType == RACIAL_TYPE_HALFORC || + nTargetRacialType == RACIAL_TYPE_HALFELF || + nTargetRacialType == RACIAL_TYPE_HALFLING || + nTargetRacialType == RACIAL_TYPE_HUMANOID_ORC || + nTargetRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + ) && + (nShifterRacialType == RACIAL_TYPE_DWARF || + nShifterRacialType == RACIAL_TYPE_ELF || + nShifterRacialType == RACIAL_TYPE_GNOME || + nShifterRacialType == RACIAL_TYPE_HUMAN || + nShifterRacialType == RACIAL_TYPE_HALFORC || + nShifterRacialType == RACIAL_TYPE_HALFELF || + nShifterRacialType == RACIAL_TYPE_HALFLING || + nShifterRacialType == RACIAL_TYPE_HUMANOID_ORC || + nShifterRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + )) + )) + { + bReturn = FALSE; + SendMessageToPC(oShifter, "This creature is a different racial type."); + } + } + }// end if - Check shifting list specific stuff + } + // Failed one of the basic checks + else + bReturn = FALSE; + + return bReturn; +} + +int ShiftIntoCreature(object oShifter, int nShifterType, object oTemplate, int bGainSpellLikeAbilities = FALSE) +{ + // Just grab the resref and move on + return ShiftIntoResRef(oShifter, nShifterType, GetResRef(oTemplate), bGainSpellLikeAbilities); +} + +int ShiftIntoResRef(object oShifter, int nShifterType, string sResRef, int bGainSpellLikeAbilities = FALSE) +{ + if (!GetPersistantLocalInt(oShifter, "PRC_UNI_SHIFT_SCRIPT")) + { + SetPersistantLocalInt(oShifter, "PRC_UNI_SHIFT_SCRIPT", 1); + ExecuteScript("prc_uni_shift", oShifter); + } + + // Make sure there is nothing that would prevent the successful execution of the shift from happening + if(!_prc_inc_shifting_GetCanShift(oShifter)) + return FALSE; + + /* Create the template to shift into */ + object oTemplate = _prc_inc_load_template_from_resref(sResRef, GetHitDice(oShifter)); + + // Make sure the template creature was successfully created. We have nothing to do if it wasn't + if(!GetIsObjectValid(oTemplate)) + { + if(DEBUG) DoDebug("prc_inc_shifting: ShiftIntoResRef(): ERROR: Failed to create creature from template resref: " + sResRef); + SendMessageToPCByStrRef(oShifter, STRREF_TEMPLATE_FAILURE); // "Polymorph failed: Failed to create a template of the creature to polymorph into." + } + else + { + // See if the shifter can in fact shift into the given template + if(GetCanShiftIntoCreature(oShifter, nShifterType, oTemplate)) + { + // It can - activate mutex + SetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX, TRUE); + SetLocalInt(oShifter, SHIFTER_ORIGINALMAXHP, 0); + + int nShapeGeneration = GetLocalInt(oShifter, PRC_Shifter_ShapeGeneration); + SetLocalInt(oShifter, PRC_Shifter_ShapeGeneration, nShapeGeneration+1); + if (DEBUG_APPLY_PROPERTIES) + DoDebug("ShiftIntoResRef, Shifter Index: " + IntToString(nShapeGeneration+1)); + + // Unshift if already shifted and then proceed with shifting into the template + // Also, give other stuff 100ms to execute in between + if(GetPersistantLocalInt(oShifter, SHIFTER_ISSHIFTED_MARKER)) + DelayCommand(0.1f, _prc_inc_shifting_UnShiftAux(oShifter, nShifterType, oTemplate, bGainSpellLikeAbilities)); + else + DelayCommand(0.1f, _prc_inc_shifting_ShiftIntoTemplateAux(oShifter, nShifterType, oTemplate, bGainSpellLikeAbilities)); + + // Return that we were able to successfully start shifting + return TRUE; + } + } + + // We didn't reach the success branch for some reason + return FALSE; +} + +int GWSPay(object oShifter, int bEpic) +{ + int nFeat = 0; + + if(!bEpic) + { + // First try paying using Greater Wildshape uses + if(GetHasFeat(FEAT_PRESTIGE_SHIFTER_GWSHAPE_1, oShifter)) + { + DecrementRemainingFeatUses(oShifter, FEAT_PRESTIGE_SHIFTER_GWSHAPE_1); + nFeat = FEAT_PRESTIGE_SHIFTER_GWSHAPE_1; + + // If we would reach 0 uses this way, see if we could pay with Druid Wildshape uses instead + if(!GetHasFeat(FEAT_PRESTIGE_SHIFTER_GWSHAPE_1, oShifter) && + GetPersistantLocalInt(oShifter, "PRC_Shifter_UseDruidWS") && + GetHasFeat(FEAT_WILD_SHAPE, oShifter) + ) + { + IncrementRemainingFeatUses(oShifter, FEAT_PRESTIGE_SHIFTER_GWSHAPE_1); + DecrementRemainingFeatUses(oShifter, FEAT_WILD_SHAPE); + nFeat = FEAT_WILD_SHAPE; + } + } + // Otherwise try paying with Druid Wildshape uses + else if(GetPersistantLocalInt(oShifter, "PRC_Shifter_UseDruidWS") && + GetHasFeat(FEAT_WILD_SHAPE, oShifter) + ) + { + DecrementRemainingFeatUses(oShifter, FEAT_WILD_SHAPE); + nFeat = FEAT_WILD_SHAPE; + } + } + // Epic shift, uses Epic Greater Wildshape + else if(GetHasFeat(FEAT_PRESTIGE_SHIFTER_EGWSHAPE_1, oShifter)) + { + DecrementRemainingFeatUses(oShifter, FEAT_PRESTIGE_SHIFTER_EGWSHAPE_1); + nFeat = FEAT_PRESTIGE_SHIFTER_EGWSHAPE_1; + } + + return nFeat; +} + +void GWSRefund(object oShifter, int nRefundFeat) +{ + IncrementRemainingFeatUses(oShifter, nRefundFeat); +} + +int UnShift(object oShifter, int bRemovePoly = TRUE, int bIgnoreShiftingMutex = FALSE) +{ + // Shifting mutex is set and we are not told to ignore it, so abort right away + if(GetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX) && !bIgnoreShiftingMutex) + { + DelayCommand(SHIFTER_MUTEX_UNSET_DELAY, SetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX, FALSE)); //In case the mutex got stuck, unstick it + return UNSHIFT_FAIL; + } + + // Check for polymorph effects + int bHadPoly = FALSE; + effect eTest = GetFirstEffect(oShifter); + while(GetIsEffectValid(eTest)) + { + if(GetEffectType(eTest) == EFFECT_TYPE_POLYMORPH) + // Depending on whether we are supposed to remove them or not either remove the effect or abort + if(bRemovePoly) + { + bHadPoly = UNSHIFT_FAIL; + RemoveEffect(oShifter, eTest); + } + else + return FALSE; + + eTest = GetNextEffect(oShifter); + } + + // Check for true form being stored + if(!GetPersistantLocalInt(oShifter, SHIFTER_TRUEAPPEARANCE)) + return UNSHIFT_FAIL; + + // The unshifting should always proceed succesfully from this point on, so set the mutex + SetLocalInt(oShifter, SHIFTER_SHIFT_MUTEX, TRUE); + SetLocalInt(oShifter, SHIFTER_ORIGINALMAXHP, 0); + DeletePersistantLocalString(oShifter, "PRC_SHIFTING_TEMPLATE_RESREF"); + DeletePersistantLocalInt(oShifter, "PRC_SHIFTING_SHIFTER_TYPE"); + + int nShapeGeneration = GetLocalInt(oShifter, PRC_Shifter_ShapeGeneration); + SetLocalInt(oShifter, PRC_Shifter_ShapeGeneration, nShapeGeneration+1); + if (DEBUG_APPLY_PROPERTIES) + DoDebug("UnShift, Shifter Index: " + IntToString(nShapeGeneration+1)); + + // If we had a polymorph effect present, start the removal monitor + if(bHadPoly) + { + DelayCommand(0.1f, _prc_inc_shifting_UnShiftAux_SeekPolyEnd(oShifter, GetItemInSlot(INVENTORY_SLOT_CARMOUR, oShifter))); + return UNSHIFT_SUCCESS_DELAYED; + } + else + { + _prc_inc_shifting_UnShiftAux(oShifter, SHIFTER_TYPE_NONE, OBJECT_INVALID, FALSE); + DeletePersistantLocalInt(oShifter, "TempShifted"); + return UNSHIFT_SUCCESS; + } +} + + +// Appearance data functions + +struct appearancevalues GetAppearanceData(object oTemplate) +{ + struct appearancevalues appval; + // The appearance type + appval.nAppearanceType = GetAppearanceType(oTemplate); + // Body parts + appval.nBodyPart_RightFoot = GetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT, oTemplate); + appval.nBodyPart_LeftFoot = GetCreatureBodyPart(CREATURE_PART_LEFT_FOOT, oTemplate); + appval.nBodyPart_RightShin = GetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN, oTemplate); + appval.nBodyPart_LeftShin = GetCreatureBodyPart(CREATURE_PART_LEFT_SHIN, oTemplate); + appval.nBodyPart_RightThigh = GetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH, oTemplate); + appval.nBodyPart_LeftThight = GetCreatureBodyPart(CREATURE_PART_LEFT_THIGH, oTemplate); + appval.nBodyPart_Pelvis = GetCreatureBodyPart(CREATURE_PART_PELVIS, oTemplate); + appval.nBodyPart_Torso = GetCreatureBodyPart(CREATURE_PART_TORSO, oTemplate); + appval.nBodyPart_Belt = GetCreatureBodyPart(CREATURE_PART_BELT, oTemplate); + appval.nBodyPart_Neck = GetCreatureBodyPart(CREATURE_PART_NECK, oTemplate); + appval.nBodyPart_RightForearm = GetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM, oTemplate); + appval.nBodyPart_LeftForearm = GetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM, oTemplate); + appval.nBodyPart_RightBicep = GetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP, oTemplate); + appval.nBodyPart_LeftBicep = GetCreatureBodyPart(CREATURE_PART_LEFT_BICEP, oTemplate); + appval.nBodyPart_RightShoulder = GetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER, oTemplate); + appval.nBodyPart_LeftShoulder = GetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER, oTemplate); + appval.nBodyPart_RightHand = GetCreatureBodyPart(CREATURE_PART_RIGHT_HAND, oTemplate); + appval.nBodyPart_LeftHand = GetCreatureBodyPart(CREATURE_PART_LEFT_HAND, oTemplate); + appval.nBodyPart_Head = GetCreatureBodyPart(CREATURE_PART_HEAD, oTemplate); + // Wings + appval.nWingType = GetCreatureWingType(oTemplate); + // Tail + appval.nTailType = GetCreatureTailType(oTemplate); + // Portrait ID + appval.nPortraitID = GetPortraitId(oTemplate); + // Portrait resref + appval.sPortraitResRef = GetPortraitResRef(oTemplate); + // Footstep type + appval.nFootStepType = GetFootstepType(oTemplate); + // Gender + appval.nGender = GetGender(oTemplate); + /* Commented out until 1.69 + // Skin color + appval.nSkinColor = GetColor(oTemplate, COLOR_CHANNEL_SKIN); + // Hair color + appval.nHairColor = GetColor(oTemplate, COLOR_CHANNEL_HAIR); + // Tattoo 1 color + appval.nTat1Color = GetColor(oTemplate, COLOR_CHANNEL_TATTOO_1); + // Tattoo 2 color + appval.nTat2Color = GetColor(oTemplate, COLOR_CHANNEL_TATTOO_2); + */ + + + return appval; +} + +void SetAppearanceData(object oTarget, struct appearancevalues appval) +{ +//DoDebug("Setting the appearance of " + DebugObject2Str(oTarget) + "to:\n" + DebugAppearancevalues2Str(appval)); + // The appearance type + SetCreatureAppearanceType(oTarget, appval.nAppearanceType); + // Body parts - Delayed, since it seems not delaying this makes the body part setting fail, instead resulting in no visible + // parts. Some interaction with SetCreatureAppearance(), maybe? + // Applies to NWN1 1.68. Kudos to Primogenitor for originally figuring this out - Ornedan + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_FOOT , appval.nBodyPart_RightFoot , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_FOOT , appval.nBodyPart_LeftFoot , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_SHIN , appval.nBodyPart_RightShin , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_SHIN , appval.nBodyPart_LeftShin , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_THIGH , appval.nBodyPart_RightThigh , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_THIGH , appval.nBodyPart_LeftThight , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_PELVIS , appval.nBodyPart_Pelvis , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_TORSO , appval.nBodyPart_Torso , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_BELT , appval.nBodyPart_Belt , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_NECK , appval.nBodyPart_Neck , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_FOREARM , appval.nBodyPart_RightForearm , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_FOREARM , appval.nBodyPart_LeftForearm , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_BICEP , appval.nBodyPart_RightBicep , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_BICEP , appval.nBodyPart_LeftBicep , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_SHOULDER , appval.nBodyPart_RightShoulder , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_SHOULDER , appval.nBodyPart_LeftShoulder , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_RIGHT_HAND , appval.nBodyPart_RightHand , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_LEFT_HAND , appval.nBodyPart_LeftHand , oTarget)); + DelayCommand(1.0f, SetCreatureBodyPart(CREATURE_PART_HEAD , appval.nBodyPart_Head , oTarget)); + // Wings + SetCreatureWingType(appval.nWingType, oTarget); + // Tail + SetCreatureTailType(appval.nTailType, oTarget); + // Footstep type + SetFootstepType(appval.nFootStepType, oTarget); + + /* Portrait stuff */ + // If the portrait ID is not PORTRAIT_INVALID, use it. This will also set the resref + if(appval.nPortraitID != PORTRAIT_INVALID) + SetPortraitId(oTarget, appval.nPortraitID); + // Otherwise, use the portrait resref. This will set portrait ID to PORTRAIT_INVALID + else + SetPortraitResRef(oTarget, appval.sPortraitResRef); + + //replace with SetGender if 1.69 adds it + if(GetGender(oTarget) != appval.nGender && appval.nAppearanceType < 7) + { + if(GetPrimaryArcaneClass(oTarget) != CLASS_TYPE_INVALID) + SetPortraitId(oTarget, 1061); //generic wizard port + else if(GetPrimaryDivineClass(oTarget) != CLASS_TYPE_INVALID) + SetPortraitId(oTarget, 1033); //generic cleric port + else + SetPortraitId(oTarget, 1043); //generic fighter port + } + + /* Commented out until 1.69 + // Skin color + SetColor(oTarget, COLOR_CHANNEL_SKIN, appval.nSkinColor); + // Hair color + SetColor(oTemplate, COLOR_CHANNEL_HAIR, appval.nHairColor); + // Tattoo 2 color + SetColor(oTemplate, COLOR_CHANNEL_TATTOO_1, appval.nTat1Color); + // Tattoo 1 color + SetColor(oTemplate, COLOR_CHANNEL_TATTOO_2, appval.nTat2Color); + */ +} + +struct appearancevalues GetLocalAppearancevalues(object oStore, string sName) +{ + struct appearancevalues appval; + // The appearance type + appval.nAppearanceType = GetPersistantLocalInt(oStore, sName + "nAppearanceType"); + // Body parts + appval.nBodyPart_RightFoot = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightFoot"); + appval.nBodyPart_LeftFoot = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftFoot"); + appval.nBodyPart_RightShin = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightShin"); + appval.nBodyPart_LeftShin = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftShin"); + appval.nBodyPart_RightThigh = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightThigh"); + appval.nBodyPart_LeftThight = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftThight"); + appval.nBodyPart_Pelvis = GetPersistantLocalInt(oStore, sName + "nBodyPart_Pelvis"); + appval.nBodyPart_Torso = GetPersistantLocalInt(oStore, sName + "nBodyPart_Torso"); + appval.nBodyPart_Belt = GetPersistantLocalInt(oStore, sName + "nBodyPart_Belt"); + appval.nBodyPart_Neck = GetPersistantLocalInt(oStore, sName + "nBodyPart_Neck"); + appval.nBodyPart_RightForearm = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightForearm"); + appval.nBodyPart_LeftForearm = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftForearm"); + appval.nBodyPart_RightBicep = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightBicep"); + appval.nBodyPart_LeftBicep = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftBicep"); + appval.nBodyPart_RightShoulder = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightShoulder"); + appval.nBodyPart_LeftShoulder = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftShoulder"); + appval.nBodyPart_RightHand = GetPersistantLocalInt(oStore, sName + "nBodyPart_RightHand"); + appval.nBodyPart_LeftHand = GetPersistantLocalInt(oStore, sName + "nBodyPart_LeftHand"); + appval.nBodyPart_Head = GetPersistantLocalInt(oStore, sName + "nBodyPart_Head"); + // Wings + appval.nWingType = GetPersistantLocalInt(oStore, sName + "nWingType"); + // Tail + appval.nTailType = GetPersistantLocalInt(oStore, sName + "nTailType"); + // Portrait ID + appval.nPortraitID = GetPersistantLocalInt(oStore, sName + "nPortraitID"); + // Portrait resref + appval.sPortraitResRef = GetPersistantLocalString(oStore, sName + "sPortraitResRef"); + // Footstep type + appval.nFootStepType = GetPersistantLocalInt(oStore, sName + "nFootStepType"); + // Gender + appval.nGender = GetPersistantLocalInt(oStore, sName + "nGender"); + + // Skin color + appval.nSkinColor = GetPersistantLocalInt(oStore, sName + "nSkinColor"); + // Hair color + appval.nHairColor = GetPersistantLocalInt(oStore, sName + "nHairColor"); + // Tattoo 1 color + appval.nTat1Color = GetPersistantLocalInt(oStore, sName + "nTat1Color"); + // Tattoo 2 color + appval.nTat2Color = GetPersistantLocalInt(oStore, sName + "nTat2Color"); + + + return appval; +} + +void SetLocalAppearancevalues(object oStore, string sName, struct appearancevalues appval) +{ + // The appearance type + SetPersistantLocalInt(oStore, sName + "nAppearanceType" , appval.nAppearanceType ); + // Body parts + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightFoot" , appval.nBodyPart_RightFoot ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftFoot" , appval.nBodyPart_LeftFoot ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightShin" , appval.nBodyPart_RightShin ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftShin" , appval.nBodyPart_LeftShin ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightThigh" , appval.nBodyPart_RightThigh ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftThight" , appval.nBodyPart_LeftThight ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_Pelvis" , appval.nBodyPart_Pelvis ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_Torso" , appval.nBodyPart_Torso ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_Belt" , appval.nBodyPart_Belt ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_Neck" , appval.nBodyPart_Neck ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightForearm" , appval.nBodyPart_RightForearm ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftForearm" , appval.nBodyPart_LeftForearm ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightBicep" , appval.nBodyPart_RightBicep ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftBicep" , appval.nBodyPart_LeftBicep ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightShoulder", appval.nBodyPart_RightShoulder ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftShoulder" , appval.nBodyPart_LeftShoulder ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_RightHand" , appval.nBodyPart_RightHand ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_LeftHand" , appval.nBodyPart_LeftHand ); + SetPersistantLocalInt(oStore, sName + "nBodyPart_Head" , appval.nBodyPart_Head ); + // Wings + SetPersistantLocalInt(oStore, sName + "nWingType" , appval.nWingType ); + // Tail + SetPersistantLocalInt(oStore, sName + "nTailType" , appval.nTailType ); + // Portrait ID + SetPersistantLocalInt(oStore, sName + "nPortraitID" , appval.nPortraitID ); + // Portrait resref + SetPersistantLocalString(oStore, sName + "sPortraitResRef" , appval.sPortraitResRef ); + // Footstep type + SetPersistantLocalInt(oStore, sName + "nFootStepType" , appval.nFootStepType ); + //Gender + SetPersistantLocalInt(oStore, sName + "nGender" , appval.nGender ); + + // Skin color + SetPersistantLocalInt(oStore, sName + "nSkinColor" , appval.nSkinColor ); + // Hair color + SetPersistantLocalInt(oStore, sName + "nHairColor" , appval.nHairColor ); + // Tattoo 1 color + SetPersistantLocalInt(oStore, sName + "nTat1Color" , appval.nTat1Color ); + // Tattoo 2 color + SetPersistantLocalInt(oStore, sName + "nTat2Color" , appval.nTat2Color ); + +} + +void DeleteLocalAppearancevalues(object oStore, string sName) +{ + // The appearance type + DeletePersistantLocalInt(oStore, sName + "nAppearanceType"); + // Body parts + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightFoot"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftFoot"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightShin"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftShin"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightThigh"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftThight"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_Pelvis"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_Torso"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_Belt"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_Neck"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightForearm"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftForearm"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightBicep"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftBicep"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightShoulder"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftShoulder"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_RightHand"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_LeftHand"); + DeletePersistantLocalInt(oStore, sName + "nBodyPart_Head"); + // Wings + DeletePersistantLocalInt(oStore, sName + "nWingType"); + // Tail + DeletePersistantLocalInt(oStore, sName + "nTailType"); + // Portrait ID + DeletePersistantLocalInt(oStore, sName + "nPortraitID"); + // Portrait resref + DeletePersistantLocalString(oStore, sName + "sPortraitResRef"); + // Footstep type + DeletePersistantLocalInt(oStore, sName + "nFootStepType"); + // Gender + DeletePersistantLocalInt(oStore, sName + "nGender"); + // Skin color + DeletePersistantLocalInt(oStore, sName + "nSkinColor"); + // Hair color + DeletePersistantLocalInt(oStore, sName + "nHairColor"); + // Tattoo 1 color + DeletePersistantLocalInt(oStore, sName + "nTat1Color"); + // Tattoo 2 color + DeletePersistantLocalInt(oStore, sName + "nTat2Color"); +} + +struct appearancevalues GetPersistantLocalAppearancevalues(object oStore, string sName) +{ + return GetLocalAppearancevalues(oStore, sName); +} + +void SetPersistantLocalAppearancevalues(object oStore, string sName, struct appearancevalues appval) +{ + SetLocalAppearancevalues(oStore, sName, appval); +} + +void DeletePersistantLocalAppearancevalues(object oStore, string sName) +{ + DeleteLocalAppearancevalues(oStore, sName); +} + +void ForceUnshift(object oShifter, int nShiftedNumber) +{ + if(GetPersistantLocalInt(oShifter, "nTimesShifted") == nShiftedNumber) + UnShift(oShifter); +} + +string DebugAppearancevalues2Str(struct appearancevalues appval) +{ + return "Appearance type = " + IntToString(appval.nAppearanceType) + "\n" + + "Body part - Right Foot = " + IntToString(appval.nBodyPart_RightFoot ) + "\n" + + "Body part - Left Foot = " + IntToString(appval.nBodyPart_LeftFoot ) + "\n" + + "Body part - Right Shin = " + IntToString(appval.nBodyPart_RightShin ) + "\n" + + "Body part - Left Shin = " + IntToString(appval.nBodyPart_LeftShin ) + "\n" + + "Body part - Right Thigh = " + IntToString(appval.nBodyPart_RightThigh ) + "\n" + + "Body part - Left Thigh = " + IntToString(appval.nBodyPart_LeftThight ) + "\n" + + "Body part - Pelvis = " + IntToString(appval.nBodyPart_Pelvis ) + "\n" + + "Body part - Torso = " + IntToString(appval.nBodyPart_Torso ) + "\n" + + "Body part - Belt = " + IntToString(appval.nBodyPart_Belt ) + "\n" + + "Body part - Neck = " + IntToString(appval.nBodyPart_Neck ) + "\n" + + "Body part - Right Forearm = " + IntToString(appval.nBodyPart_RightForearm ) + "\n" + + "Body part - Left Forearm = " + IntToString(appval.nBodyPart_LeftForearm ) + "\n" + + "Body part - Right Bicep = " + IntToString(appval.nBodyPart_RightBicep ) + "\n" + + "Body part - Left Bicep = " + IntToString(appval.nBodyPart_LeftBicep ) + "\n" + + "Body part - Right Shoulder = " + IntToString(appval.nBodyPart_RightShoulder) + "\n" + + "Body part - Left Shoulder = " + IntToString(appval.nBodyPart_LeftShoulder ) + "\n" + + "Body part - Right Hand = " + IntToString(appval.nBodyPart_RightHand ) + "\n" + + "Body part - Left Hand = " + IntToString(appval.nBodyPart_LeftHand ) + "\n" + + "Body part - Head = " + IntToString(appval.nBodyPart_Head ) + "\n" + + "Wings = " + IntToString(appval.nWingType) + "\n" + + "Tail = " + IntToString(appval.nTailType) + "\n" + + "Portrait ID = " + (appval.nPortraitID == PORTRAIT_INVALID ? "PORTRAIT_INVALID" : IntToString(appval.nPortraitID)) + "\n" + + "Portrait ResRef = " + appval.sPortraitResRef + "\n" + + "Footstep type = " + IntToString(appval.nFootStepType) + "\n" + + "Gender = " + IntToString(appval.nGender) + "\n" + + "Skin color = " + IntToString(appval.nSkinColor) + "\n" + + "Hair color = " + IntToString(appval.nHairColor) + "\n" + + "Tattoo 1 color = " + IntToString(appval.nTat1Color) + "\n" + + "Tattoo 2 color = " + IntToString(appval.nTat2Color) + "\n" + ; +} + +int IsPolymorphed(object oPC) +{ + effect eTest = GetFirstEffect(oPC); + while(GetIsEffectValid(eTest)) + { + if(GetEffectType(eTest) == EFFECT_TYPE_POLYMORPH) + return TRUE; + eTest = GetNextEffect(oPC); + } + return FALSE; +} + +void HandleTrueShape(object oPC) +{ + if(!GetPersistantLocalInt(oPC, SHIFTER_TRUEAPPEARANCE)) + StoreCurrentAppearanceAsTrueAppearance(oPC, TRUE); + + if(!GetPersistantLocalInt(oPC, SHIFTER_TRUE_RACE)) + StoreCurrentRaceAsTrueRace(oPC); +} + +void HandleApplyShiftEffects(object oPC) +{ + if (IsPolymorphed(oPC)) + return; + DelayCommand(0.0f, _prc_inc_shifting_ApplyEffects(oPC, TRUE)); +} + +void HandleApplyShiftTemplate(object oPC) +{ + if (IsPolymorphed(oPC)) + return; + + string sResRef; + int nShifterType; + int nShapeGeneration; + object oTemplate; + + if(GetLocalInt(oPC, SHIFTER_SHIFT_MUTEX)) + { + //If shifting, the following is already being handled + //by the shifting code so don't do it again here. + return; + } + + sResRef = GetPersistantLocalString(oPC, "PRC_SHIFTING_TEMPLATE_RESREF"); + if(sResRef != "") + { + oTemplate = _prc_inc_load_template_from_resref(sResRef, GetHitDice(oPC)); + if(GetIsObjectValid(oTemplate)) + { + nShifterType = GetPersistantLocalInt(oPC, "PRC_SHIFTING_SHIFTER_TYPE"); + nShapeGeneration = GetLocalInt(oPC, PRC_Shifter_ShapeGeneration); + if (DEBUG_APPLY_PROPERTIES) + DoDebug("HandleApplyShiftTemplate, Shifter Index: " + IntToString(nShapeGeneration)); + DelayCommand(0.0f, _prc_inc_shifting_ApplyTemplate(oPC, nShapeGeneration, nShifterType, oTemplate, FALSE, GetPCSkin(oPC))); + } + } +} + +int PnPShifterFeats() +{ + if(GetPRCSwitch(PRC_NWNX_FUNCS)) + { + //If any stats have been changed by NWNX, this could qualify the PC for feats they should + //not actually qualify for, so force unshifting before levelling up. + if(GetPersistantLocalInt(OBJECT_SELF, "Shifting_NWNXSTRAdjust") + || GetPersistantLocalInt(OBJECT_SELF, "Shifting_NWNXDEXAdjust") + || GetPersistantLocalInt(OBJECT_SELF, "Shifting_NWNXCONAdjust")) + { + FloatingTextStringOnCreature("You must unshift before levelling up.", OBJECT_SELF, FALSE); //TODO: TLK entry + return TRUE; + } + } + return FALSE; +} + +// Test main +// void main(){} diff --git a/src/include/prc_inc_skills.nss b/src/include/prc_inc_skills.nss new file mode 100644 index 0000000..bbca299 --- /dev/null +++ b/src/include/prc_inc_skills.nss @@ -0,0 +1,390 @@ +//::////////////////////////////////////////////// +/* + This file is for the code/const for any custom skills. +*/ +//::////////////////////////////////////////////// + +/** + * Returns the height differential between the two locations. + * Return value is in feet. + */ +float GetHeight(location lPC, location lTarget); + +// * Returns true or false depending on whether the creature is flying +// * or not +int PRCIsFlying(object oCreature); + +// * Returns true or false depending on whether the creature has flying +// * mount or not +int PRCHasFlyingMount(object oCreature); + +#include "prc_inc_clsfunc" +#include "moi_inc_moifunc" +#include "x3_inc_horse" + +//::////////////////////////////////////////////// +//:: Skill Functions +//::////////////////////////////////////////////// + +// returns true if they pass the jump check +// also handles animations and knockdown +int PerformJump(object oPC, location lLoc, int bDoKnockDown = TRUE) +{ + object oArea = GetArea(oPC); + int bCanFly = FALSE; + // if jumping is disabled in this place. + if( GetLocalInt(oArea, "AreaJumpOff") == TRUE ) + { + SendMessageToPC(oPC, "Jumping is not allowed in this area."); + return FALSE; + } + + // Simulate "leap of the clouds", or RDD's having wings can "fly" + if((GetLevelByClass(CLASS_TYPE_MONK, oPC) >= 7 + || GetLevelByClass(CLASS_TYPE_NINJA_SPY, oPC) >= 3 + || PRCIsFlying(oPC)) + && GetLocalInt(oArea, "AreaFlyOff") == FALSE) + bCanFly = TRUE; + + // This meld allows someone to fly when bound to a chakra + if (GetIsMeldBound(oPC, MELD_ACROBAT_BOOTS)) + bCanFly = TRUE; + + // This meld allows someone to fly when bound to a chakra + if (GetIsMeldBound(oPC, MELD_MANTICORE_BELT) == CHAKRA_WAIST) + bCanFly = TRUE; + + //ebonfowl: Can fly temporarily when using Borne Aloft + if (GetLocalInt(oPC, "BorneAloft")) + bCanFly = TRUE; + + // can't fly while mounted + if (HorseGetIsMounted(oPC) && !PRCHasFlyingMount(oPC)) + bCanFly = FALSE; + + // Height restriction on jumping + if (GetHeight(GetLocation(oPC), lLoc) > 8.0 && !bCanFly) + { + SendMessageToPC(oPC, "The target location is too high to jump to. Please pick a lower target."); + return FALSE; + } + + // Immobilized creatures can't jump + effect eCheck = GetFirstEffect(oPC); + int nCheck; + while(GetIsEffectValid(eCheck)){ + nCheck = GetEffectType(eCheck); + if(nCheck == EFFECT_TYPE_CUTSCENEIMMOBILIZE || + nCheck == EFFECT_TYPE_ENTANGLE) + { + SendMessageToPC(oPC, "You cannot move."); + return FALSE; + } + + eCheck = GetNextEffect(oPC); + } + + int bIsRunningJump = FALSE; + int bPassedJumpCheck = FALSE; + int iMinJumpDistance = 0; + int iMaxJumpDistance = 6; + int iDivisor = 1; + int iJumpDistance = 0; + int bIsInHeavyArmor = FALSE; + + // Goliaths and Garguns treat all jumps as running + if(GetRacialType(oPC) == RACIAL_TYPE_GOLIATH || GetRacialType(oPC) == RACIAL_TYPE_FERAL_GARGUN) + bIsRunningJump = TRUE; + + int iDistance = FloatToInt(MetersToFeet(GetDistanceBetweenLocations(GetLocation(oPC), lLoc ) ) ); + + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC); + int AC = GetBaseAC(oArmor); + if(AC >= 6) bIsInHeavyArmor = TRUE; + + // Jump check rules depend on a running jump or standing jump + // running jumps require at least 20 feet run length + if (iDistance >= 18 && !bIsInHeavyArmor) bIsRunningJump = TRUE; + + int iBonus = 0; + if (GetHasFeat(FEAT_GREAT_LEAP, oPC)) + { + if (Ninja_AbilitiesEnabled(oPC)) + { + bIsRunningJump = TRUE; + iBonus = 4; + } + } + /*if (GetHasSpellEffect(MOVE_TC_LEAPING_DRAGON, oPC)) + { + bIsRunningJump = TRUE; + iBonus = 10; + } */ + // PnP rules are height * 6 for run and height * 2 for jump. + // I can't get height so that is assumed to be 6. + // Changed maxed jump distance because the NwN distance is rather short + // at least compared to height it is. + if(bIsRunningJump) + { + iMinJumpDistance = 5; + iDivisor = 1; + iMaxJumpDistance *= 6; + } + else + { + iMinJumpDistance = 3; + iDivisor = 2; + iMaxJumpDistance *= 3; + } + + // skill 28 = jump + int iJumpRoll = d20() + GetSkillRank(SKILL_JUMP, oPC) + iBonus + GetAbilityModifier(ABILITY_STRENGTH, oPC); + + if(bCanFly) + { + iMaxJumpDistance *= 100; + iJumpRoll *= 100; + } + + // Use Dex instead of Strength + if (GetHasFeat(FEAT_AGILE_ATHLETE, oPC)) + { + iJumpRoll -= GetAbilityModifier(ABILITY_STRENGTH, oPC); + iJumpRoll += GetAbilityModifier(ABILITY_DEXTERITY, oPC); + } + + if(GetSkillRank(SKILL_TUMBLE, oPC, TRUE) >= 5) iJumpRoll += 2; + + // Jump distance is determined by the number exceeding 10 + // divided based on running or standing jump. + iJumpRoll -= 10; + if(iJumpRoll < 1) iJumpRoll = 1; + iJumpRoll /= iDivisor; + iJumpDistance = iMinJumpDistance + iJumpRoll; + + // Hadozee gliding doubles jump distance + if(GetRacialType(oPC) == RACIAL_TYPE_HADOZEE) + { + iMaxJumpDistance *= 2; + iJumpDistance *= 2; + } + + if(iJumpDistance >= iDistance && iDistance <= iMaxJumpDistance) + { + // they passed jump check + bPassedJumpCheck = TRUE; + + effect eJump = EffectDisappearAppear(lLoc); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eJump, oPC, 3.1); + } + else + { + // they failed jump check + FloatingTextStringOnCreature("Jump check failed.", oPC); + bPassedJumpCheck = FALSE; + + if(bDoKnockDown) + { + effect eKnockDown = EffectKnockdown(); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eKnockDown, oPC); + } + } + + return bPassedJumpCheck; +} + +float GetHeight(location lPC, location lTarget) +{ + vector vPC = GetPositionFromLocation(lPC); + vector vTarget = GetPositionFromLocation(lTarget); + + return MetersToFeet(vTarget.z - vPC.z); +} + +float GetDistanceForClimbing(object oPC, location lLoc) +{ + object oArea = GetAreaFromLocation(lLoc); + vector vTest1 = GetPositionFromLocation(GetLocation(oPC)); + vector vTest2 = GetPositionFromLocation(lLoc); + float fDistance = GetDistanceBetweenLocations(Location(oArea, Vector(vTest1.x, vTest1.y, 0.0), 0.0), + Location(oArea, Vector(vTest2.x, vTest2.y, 0.0), 0.0)); + + return MetersToFeet(fDistance); +} + +int DoClimb(object oPC, location lLoc, int bDoKnockDown = TRUE) +{ + // check to see if abort due to being mounted + if(HorseGetIsMounted(oPC)) + return FALSE; // abort + + // if Climbing is disabled in this place. + if(GetLocalInt(GetArea(oPC), "AreaClimbOff") == TRUE) + { + SendMessageToPC(oPC, "Climbing is not allowed in this area."); + return FALSE; + } + + // Immobilized creatures can't Climb + effect eCheck = GetFirstEffect(oPC); + int nCheck; + while(GetIsEffectValid(eCheck)) + { + nCheck = GetEffectType(eCheck); + if(nCheck == EFFECT_TYPE_CUTSCENEIMMOBILIZE || + nCheck == EFFECT_TYPE_ENTANGLE) + { + SendMessageToPC(oPC, "You cannot move."); + return FALSE; + } + + eCheck = GetNextEffect(oPC); + } + + if(GetDistanceForClimbing(oPC, lLoc) > 10.0) + { + SendMessageToPC(oPC, "The target location is too far away to climb to. Please pick a closer target."); + return FALSE; + } + // Height restriction on climbing. Has to be at least 5 up or 5 down. + if(5.0 > GetHeight(GetLocation(oPC), lLoc) && GetHeight(GetLocation(oPC), lLoc) > -5.0) + { + SendMessageToPC(oPC, "The target location is too low to climb to. Please pick a higher target."); + return FALSE; + } + int bPassedClimbCheck = FALSE; + + int iClimbRoll = d20() + GetSkillRank(SKILL_CLIMB, oPC) + GetAbilityModifier(ABILITY_STRENGTH, oPC); + + // Flat DC, one of the hardest + if(iClimbRoll >= 25) + { + // they passed Climb check + bPassedClimbCheck = TRUE; + + effect eClimb = EffectDisappearAppear(lLoc); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eClimb, oPC, 3.1); + } + else + { + // they failed Climb check + FloatingTextStringOnCreature("Climb check failed.", oPC); + + if(bDoKnockDown) + { + effect eKnockDown = EffectKnockdown(); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnockDown, oPC, 5.0); + } + } + + return bPassedClimbCheck; +} + +// * Returns true or false depending on whether the creature is flying +// * or not +int PRCIsFlying(object oCreature) +{ + int nAppearance = GetAppearanceType(oCreature); + int nWings = GetCreatureWingType(oCreature); + int bFlying = FALSE; + switch(nAppearance) + { + case APPEARANCE_TYPE_PARROT: + case APPEARANCE_TYPE_BAT: + case APPEARANCE_TYPE_BAT_HORROR: + case APPEARANCE_TYPE_DRAGON_BLACK: + case APPEARANCE_TYPE_DRAGON_BLUE: + case APPEARANCE_TYPE_DRAGON_BRASS: + case APPEARANCE_TYPE_DRAGON_BRONZE: + case APPEARANCE_TYPE_DRAGON_COPPER: + case APPEARANCE_TYPE_DRAGON_GOLD: + case APPEARANCE_TYPE_DRAGON_GREEN: + case APPEARANCE_TYPE_DRAGON_PRIS: + case APPEARANCE_TYPE_DRAGON_RED: + case APPEARANCE_TYPE_DRAGON_SHADOW: + case APPEARANCE_TYPE_DRAGON_SILVER: + case APPEARANCE_TYPE_DRAGON_WHITE: + case APPEARANCE_TYPE_ELEMENTAL_AIR: + case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER: + case APPEARANCE_TYPE_FAIRY: + case APPEARANCE_TYPE_GARGOYLE: + case APPEARANCE_TYPE_HELMED_HORROR: + case APPEARANCE_TYPE_LANTERN_ARCHON: + case APPEARANCE_TYPE_QUASIT: + case APPEARANCE_TYPE_IMP: + case APPEARANCE_TYPE_MEPHIT_AIR: + case APPEARANCE_TYPE_MEPHIT_DUST: + case APPEARANCE_TYPE_MEPHIT_EARTH: + case APPEARANCE_TYPE_MEPHIT_FIRE: + case APPEARANCE_TYPE_MEPHIT_ICE: + case APPEARANCE_TYPE_MEPHIT_MAGMA: + case APPEARANCE_TYPE_MEPHIT_OOZE: + case APPEARANCE_TYPE_MEPHIT_SALT: + case APPEARANCE_TYPE_MEPHIT_STEAM: + case APPEARANCE_TYPE_MEPHIT_WATER: + case APPEARANCE_TYPE_WILL_O_WISP: + case APPEARANCE_TYPE_FALCON: + case APPEARANCE_TYPE_RAVEN: + case APPEARANCE_TYPE_SHADOW: + case APPEARANCE_TYPE_SHADOW_FIEND: + case APPEARANCE_TYPE_SPECTRE: + case APPEARANCE_TYPE_SUCCUBUS: + case APPEARANCE_TYPE_ALLIP: + case APPEARANCE_TYPE_WRAITH: + case APPEARANCE_TYPE_SEAGULL_FLYING: + case APPEARANCE_TYPE_SPHINX: + case APPEARANCE_TYPE_GYNOSPHINX: + case APPEARANCE_TYPE_MANTICORE: + case APPEARANCE_TYPE_COCKATRICE: + case APPEARANCE_TYPE_FAERIE_DRAGON: + case APPEARANCE_TYPE_PSEUDODRAGON: + case APPEARANCE_TYPE_WYRMLING_BLACK: + case APPEARANCE_TYPE_WYRMLING_BLUE: + case APPEARANCE_TYPE_WYRMLING_BRASS: + case APPEARANCE_TYPE_WYRMLING_BRONZE: + case APPEARANCE_TYPE_WYRMLING_COPPER: + case APPEARANCE_TYPE_WYRMLING_GOLD: + case APPEARANCE_TYPE_WYRMLING_GREEN: + case APPEARANCE_TYPE_WYRMLING_RED: + case APPEARANCE_TYPE_WYRMLING_SILVER: + case APPEARANCE_TYPE_WYRMLING_WHITE: + case APPEARANCE_TYPE_DEVIL: + case APPEARANCE_TYPE_BEHOLDER: + case APPEARANCE_TYPE_BEHOLDER_MAGE: + case APPEARANCE_TYPE_BEHOLDER_EYEBALL: + case APPEARANCE_TYPE_DRACOLICH: + case APPEARANCE_TYPE_HARPY: + case APPEARANCE_TYPE_DEMI_LICH: + case 455: // Wyvern: Adult + case 456: // Wyvern: Great + case 457: // Wyvern: Juvenile + case 458: // Wyvern: Young + case APPEARANCE_TYPE_BEHOLDER_MOTHER: + bFlying = TRUE; + } + if(!bFlying + && ((nWings > 0 && nWings < 79) || nWings == 90))//CEP and Project Q wing models + bFlying = TRUE; + + if (GetHasSpellEffect(MOVE_SH_BALANCE_SKY, oCreature)) + bFlying = TRUE; + + if (GetLocalInt(oCreature, "GeryonFlight")) + bFlying = TRUE; + + if(GetRacialType(oCreature) == RACIAL_TYPE_GLOURA) + bFlying = TRUE; + + if(GetRacialType(oCreature) == RACIAL_TYPE_SPIRETOPDRAGON) + bFlying = TRUE; + + return bFlying; +} + +int PRCHasFlyingMount(object oCreature) +{ + int nMount = GetCreatureTailType(oCreature); + int bFlying = FALSE; + //no flying mounts in original NWN - TODO: add check for CEP mounts + return bFlying; +} \ No newline at end of file diff --git a/src/include/prc_inc_skin.nss b/src/include/prc_inc_skin.nss new file mode 100644 index 0000000..d227d60 --- /dev/null +++ b/src/include/prc_inc_skin.nss @@ -0,0 +1,139 @@ +//:://///////////////////////////////////////////// +//:: skin include +//:: prc_inc_skin +//:://///////////////////////////////////////////// +/** @file + This include contains GetPCSkin(). If only using + this function please include this directly and not via + the entire spell engine :p + + Is included by inc_persist_loca for persistent + local variables +*/ +//::////////////////////////////////////////////// +//:: Created By: fluffyamoeba +//:: Created On: 2008-4-23 +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Sets up the pcskin object on oPC. + * If it already exists, simply return it. Otherwise, create and equip it. + * + * @param oPC The creature whose skin object to look for. + * @return Either the skin found or the skin created. + */ +object GetPCSkin(object oPC); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_debug" + +////////////////////////////////////////////////// +/* private functions */ +////////////////////////////////////////////////// +// to duplicate ForceEquip() from inc_utility + +void _ForceEquipSkin(object oPC, object oSkin, int nThCall = 0) +{ + // Make sure that the object we are attempting equipping is the latest one to be ForceEquipped into this slot + if(GetIsObjectValid(GetLocalObject(oPC, "ForceEquipToSlot_17")) + && GetLocalObject(oPC, "ForceEquipToSlot_17") != oSkin) + return; + + // Fail on non-commandable NPCs after ~1min + if(!GetIsPC(oPC) && !GetCommandable(oPC) && nThCall > 60) + { + WriteTimestampedLogEntry("ForceEquip() failed on non-commandable NPC: " + DebugObject2Str(oPC) + " for item: " + DebugObject2Str(oSkin)); + return; + } + + float fDelay; + + // Check if the equipping has already happened + if(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC) != oSkin) + { + // Test and increment the control counter + if(nThCall++ == 0) + { + // First, try to do the equipping non-intrusively and give the target a reasonable amount of time to do it + AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + fDelay = 1.0f; + + // Store the item to be equipped in a local variable to prevent contest between two different calls to ForceEquip + SetLocalObject(oPC, "ForceEquipToSlot_17", oSkin); + } + else + { + // Nuke the target's action queue. This should result in "immediate" equipping of the item + if(GetIsPC(oPC) || nThCall > 5) // Skip nuking NPC action queue at first, since that can cause problems. 5 = magic number here. May need adjustment + { + if(!GetIsObjectValid(oSkin)) + { + oSkin = GetPCSkin(oPC); + return; + } + else + { + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + } + } + // Use a lenghtening delay in order to attempt handling lag and possible other interference. From 0.1s to 1s + fDelay = (nThCall < 10 ? nThCall : 10) / 10.0f; // yes this is the same as PRCMin(nThCall, 10) + } + + // Loop + DelayCommand(fDelay, _ForceEquipSkin(oPC, oSkin, nThCall)); + } + // It has, so clean up + else + DeleteLocalObject(oPC, "ForceEquipToSlot_17"); +} + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +object GetPCSkin(object oPC) +{ + // According to a bug report, this is being called on non-creature objects. This should catch the culprit + if(DEBUG) Assert(GetObjectType(oPC) == OBJECT_TYPE_CREATURE, "GetObjectType(oPC) == OBJECT_TYPE_CREATURE", "GetPRCSkin() called on non-creature object: " + DebugObject2Str(oPC), "prc_inc_skin", "object GetPCSkin(object oPC)"); + object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC); + if(!GetIsObjectValid(oSkin)) + { + //oSkin = GetLocalObject(oPC, "PRCSkinCache"); + //if(!GetIsObjectValid(oSkin)) + { + //Added this check to prevent creation of extra skins on module entry + oSkin = GetItemPossessedBy(oPC, "base_prc_skin"); + if(GetIsObjectValid(oSkin)) + { + _ForceEquipSkin(oPC, oSkin, INVENTORY_SLOT_CARMOUR); + //AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + } + else + { + oSkin = CreateItemOnObject("base_prc_skin", oPC); + _ForceEquipSkin(oPC, oSkin, INVENTORY_SLOT_CARMOUR); + //AssignCommand(oPC, ActionEquipItem(oSkin, INVENTORY_SLOT_CARMOUR)); + + // The skin should not be droppable + SetDroppableFlag(oSkin, FALSE); + // other scripts should not be able to destroy the skin + AssignCommand(oSkin, SetIsDestroyable(FALSE)); + } + + // Cache the skin reference for further lookups during the same script + //SetLocalObject(oPC, "PRCSkinCache", oSkin); + //DelayCommand(0.0f, DeleteLocalObject(oPC, "PRCSkinCache")); + } + } + return oSkin; +} + diff --git a/src/include/prc_inc_smite.nss b/src/include/prc_inc_smite.nss new file mode 100644 index 0000000..f982f3b --- /dev/null +++ b/src/include/prc_inc_smite.nss @@ -0,0 +1,500 @@ +/* +Smite Evil: Once per day, a paladin of 2nd level or higher may +attempt to smite evil with one normal melee attack. She adds her +Charisma modifier (if positive) to her attack roll and deals 1 extra +point of damage per level. For example, a 13th-level paladin armed +with a longsword would deal 1d8+13 points of damage, plus any +additional bonuses for high Strength or magical effects that +normally apply. If the paladin accidentally smites a creature that is +not evil, the smite has no effect but it is still used up for that day. +*/ +/* +Good + Anti-paldin + Blackguard + Fiendish Template + Half-fiend Template +Evil + Paladin + Fist of Raziel + Celestial Template + Half-celestial Template +Undead + Soldier of Light +Infidel + Champion of Bane + Champion of Torm +CW Samurai + Kiai +*/ + +const int SMITE_TYPE_GOOD_ANTIPALADIN = 11; +const int SMITE_TYPE_GOOD_BLACKGUARD = 12; //not used, biowares is adequate +const int SMITE_TYPE_GOOD_TEMPLATE_FIENDISH = 13; +const int SMITE_TYPE_GOOD_TEMPLATE_HALF_FIEND = 14; + +const int SMITE_TYPE_EVIL_PALADIN = 21; //not used, biowares is adequate +const int SMITE_TYPE_EVIL_FIST_OF_RAZIEL = 22; +const int SMITE_TYPE_EVIL_TEMPLATE_CELESTIAL = 23; +const int SMITE_TYPE_EVIL_TEMPLATE_HALF_CELESTIAL = 24; + +const int SMITE_TYPE_UNDEAD = 31; + +const int SMITE_TYPE_INFIDEL = 41; + +const int SMITE_TYPE_KIAI = 51; +const int SMITE_TYPE_CRUSADER = 52; +const int SMITE_TYPE_SHADOWBANE = 53; +const int SMITE_TYPE_KILLOREN = 54; +const int SMITE_TYPE_TEMPLATE_MINERAL = 55; +const int SMITE_TYPE_CULTIST = 56; +const int SMITE_TYPE_SOULBORN = 57; +const int SMITE_TYPE_CHAOS = 58; +const int SMITE_TYPE_RAMETHENE = 59; +const int SMITE_TYPE_ANDRAS = 60; +const int SMITE_TYPE_TRIAD = 61; +const int SMITE_TYPE_DESHARIS = 62; + +//this calculates damage and stuff from class and type +//takes epic smiting feats etc into account +//takes FoR special smiteing abilities into account too +//It DOES NOT decrease smites remaining +void DoSmite(object oPC, object oTarget, int nType); + +#include "prc_inc_combat" +#include "prc_inc_racial" +#include "bnd_inc_bndfunc" +#include "prc_inc_factotum" + +void DoSmite(object oPC, object oTarget, int nType) +{ + effect eSmite; //effect OnHit + int nDamage; + int nDamageType; + int nAttack; + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + string sHit; + string sMiss; + string sFailedTarget; + string sFailedSmiter; + int nTargetInvalid = !GetIsObjectValid(oTarget); + int nSmiterInvalid = !GetIsObjectValid(oPC); + if(nType == SMITE_TYPE_EVIL_FIST_OF_RAZIEL + || nType == SMITE_TYPE_EVIL_PALADIN + || nType == SMITE_TYPE_EVIL_TEMPLATE_CELESTIAL + || nType == SMITE_TYPE_EVIL_TEMPLATE_HALF_CELESTIAL) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + + if(nType == SMITE_TYPE_EVIL_PALADIN) + { + nDamage = GetLevelByClass(CLASS_TYPE_PALADIN, oPC) + + GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oPC) + + GetLevelByClass(CLASS_TYPE_DIVINE_CHAMPION, oPC); + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 1; + } + else if(nType == SMITE_TYPE_EVIL_FIST_OF_RAZIEL) + { + nDamage = GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oPC) + + GetLevelByClass(CLASS_TYPE_PALADIN, oPC) + + GetLevelByClass(CLASS_TYPE_DIVINE_CHAMPION, oPC); + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 1; + } + else if(nType == SMITE_TYPE_EVIL_TEMPLATE_CELESTIAL) + { + nDamage = GetHitDice(oPC); + if(nDamage > 20) + nDamage = 20; + } + else if(nType == SMITE_TYPE_EVIL_TEMPLATE_HALF_CELESTIAL) + { + nDamage = GetHitDice(oPC); + if(nDamage > 20) + nDamage = 20; + } + + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Smite Failed: target is not Evil"; + sFailedSmiter = "Smite Failed: you are not Good"; + sHit = "Smite Evil Hit"; + sMiss = "Smite Evil Missed"; + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_EVIL) + nTargetInvalid = TRUE; + if(GetAlignmentGoodEvil(oPC) != ALIGNMENT_GOOD) + nSmiterInvalid = TRUE; + + //Fist of Raziel special stuff + if(nType == SMITE_TYPE_EVIL_FIST_OF_RAZIEL) + { + int nClass = GetLevelByClass(CLASS_TYPE_FISTRAZIEL, oPC); + //good aligned weapon (+1 enhancement to break DR) + if(nClass) + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, 1), oWeapon, 0.1); + + //criticals always hit + if(nClass >= 3) + SetLocalInt(oPC, "FistOfRazielSpecialSmiteCritical", TRUE); + + //2d8 vs outsiders or undead + if(nClass >= 7 && (MyPRCGetRacialType(oTarget) == RACIAL_TYPE_OUTSIDER || MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD)) + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, DAMAGE_TYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d8), oWeapon, 0.1); + + //these are either or, not both + else if(nClass >= 5) + AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, DAMAGE_TYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), oWeapon, 0.1); + + //chain bolt stuff + if(nClass >= 9) + { + int nTargetCount = 5; + int i = 1; + object oSecondTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oPC, i); + //GetFirstObjectInShape(SHAPE_SPHERE, + // RADIUS_SIZE_LARGE, GetLocation(oPC), TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oSecondTarget) && nTargetCount > 0 && GetDistanceBetween(oPC, oSecondTarget) < FeetToMeters(30.0)) + { + if(GetAlignmentGoodEvil(oSecondTarget) == ALIGNMENT_EVIL && oTarget != oSecondTarget) + { + int nDamage; + if(MyPRCGetRacialType(oSecondTarget) == RACIAL_TYPE_OUTSIDER || MyPRCGetRacialType(oSecondTarget) == RACIAL_TYPE_UNDEAD) + nDamage = d8(2); + else + nDamage = d6(2); + nDamage = PRCGetReflexAdjustedDamage(nDamage, oSecondTarget, 15+(GetAbilityModifier(ABILITY_CHARISMA, oPC)/2), SAVING_THROW_TYPE_NONE, oPC); + effect eDamage = EffectDamage(nDamage, DAMAGE_TYPE_DIVINE); + effect eBeam = EffectBeam(VFX_BEAM_HOLY, oPC, BODY_NODE_HAND); + effect eVFX = EffectVisualEffect(VFX_COM_HIT_DIVINE); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oSecondTarget); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oSecondTarget, 0.5); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oSecondTarget); + nTargetCount --; + } + i++; + oSecondTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oPC, i); + } + } + } + } + + else if(nType == SMITE_TYPE_GOOD_ANTIPALADIN + || nType == SMITE_TYPE_GOOD_BLACKGUARD) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + if(nType == SMITE_TYPE_GOOD_BLACKGUARD) + { + nDamage = GetLevelByClass(CLASS_TYPE_BLACKGUARD, oPC) + + GetLevelByClass(CLASS_TYPE_ANTI_PALADIN, oPC); + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 1; + } + else if(nType == SMITE_TYPE_GOOD_ANTIPALADIN) + { + nDamage = GetLevelByClass(CLASS_TYPE_ANTI_PALADIN, oPC) + + GetLevelByClass(CLASS_TYPE_BLACKGUARD, oPC); + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 1; + } + else if(nType == SMITE_TYPE_GOOD_TEMPLATE_FIENDISH) + { + nDamage = GetHitDice(oPC); + if(nDamage > 20) + nDamage = 20; + } + else if(nType == SMITE_TYPE_GOOD_TEMPLATE_HALF_FIEND) + { + nDamage = GetHitDice(oPC); + if(nDamage > 20) + nDamage = 20; + } + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Smite Failed: target is not Good"; + sFailedSmiter = "Smite Failed: you are not Evil"; + sHit = "Smite Good Hit"; + sMiss = "Smite Good Missed"; + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD) + nTargetInvalid = TRUE; + if(GetAlignmentGoodEvil(OBJECT_SELF) != ALIGNMENT_EVIL) + nSmiterInvalid = TRUE; + } + + else if(nType == SMITE_TYPE_UNDEAD) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + nAttack = GetAbilityModifier(ABILITY_WISDOM, oPC); + nDamage = GetLevelByClass(CLASS_TYPE_SOLDIER_OF_LIGHT, oPC); + nDamageType = DAMAGE_TYPE_POSITIVE; + sFailedTarget = "Smite Failed: target is not Undead"; + sFailedSmiter = "Smite Failed: you are not Good"; + sHit = "Smite Undead Hit"; + sMiss = "Smite Undead Missed"; + if(MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) + nTargetInvalid = TRUE; + if(GetAlignmentGoodEvil(OBJECT_SELF) != ALIGNMENT_GOOD) + nSmiterInvalid = TRUE; + } + + else if(nType == SMITE_TYPE_INFIDEL) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + nDamage = GetLevelByClass(CLASS_TYPE_DIVINE_CHAMPION, oPC) //CoT + + GetLevelByClass(CLASS_TYPE_PALADIN, oPC); + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + string sDeity = "Torm"; + nDamageType = DAMAGE_TYPE_POSITIVE; + if(nAttack < 1) + nAttack = 1; + sFailedTarget = "Smite Failed: target has the same deity"; + sFailedSmiter = "Smite Failed: you do not follow your deity"; + sHit = "Smite Infidel Hit"; + sMiss = "Smite Infidel Missed"; + if(GetStringLowerCase(GetDeity(oTarget))== GetStringLowerCase(GetDeity(oPC))) + nTargetInvalid = TRUE; + if(GetStringLowerCase(GetDeity(oPC)) != GetStringLowerCase(sDeity)) + nSmiterInvalid = TRUE; + } + + else if(nType == SMITE_TYPE_KIAI) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + nDamage = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nDamage < 1) + nDamage = 1; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 1; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Failed: you are not Lawful"; + sHit = "Kiai Smite Hit"; + sMiss = "Kiai Smite Missed"; + if(GetAlignmentLawChaos(OBJECT_SELF) != ALIGNMENT_LAWFUL) + nSmiterInvalid = TRUE; + } + + else if(nType == SMITE_TYPE_CRUSADER) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + nDamage = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nDamage < 1) + nDamage = 0; // Can't go negative + nAttack = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC); + + // Factotum Cunning Brilliance + if (GetIsAbilitySaved(oPC, FEAT_CRUSADER_SMITE)) + nAttack = GetLevelByClass(CLASS_TYPE_FACTOTUM); + + nDamageType = DAMAGE_TYPE_MAGICAL; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Failed: Report Error"; + sHit = "Crusader Smite Hit"; + sMiss = "Crusader Smite Missed"; + } + + else if(nType == SMITE_TYPE_SHADOWBANE) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + nDamage = GetLevelByClass(CLASS_TYPE_SHADOWBANE_INQUISITOR, oPC); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Failed: You don't exist"; + sHit = "Shadowbane Smite Hit"; + sMiss = "Shadowbane Smite Missed"; + } + + else if(nType == SMITE_TYPE_KILLOREN) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_DIVINE); + nDamage = GetHitDice(oPC); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_MAGICAL; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Aspect of the Destroyer Failed: You don't exist"; + sHit = "Aspect of the Destroyer Hit"; + sMiss = "Aspect of the Destroyer Missed"; + } + + else if(nType == SMITE_TYPE_TEMPLATE_MINERAL) + { + eSmite = EffectVisualEffect(VFX_IMP_ACID_L); + nDamage = GetHitDice(oPC); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CONSTITUTION, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_MAGICAL; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Earth Strike Failed"; + sHit = "Earth Strike Hit"; + sMiss = "Earth Strike Missed"; + } + + else if(nType == SMITE_TYPE_CULTIST) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_FROST); + nDamage = GetLevelByClass(CLASS_TYPE_CULTIST_SHATTERED_PEAK, oPC); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_MAGICAL; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Mage Failed: You don't exist"; + sHit = "Smite Mage Hit"; + sMiss = "Smite Mage Missed"; + } + + else if(nType == SMITE_TYPE_SOULBORN) + { + eSmite = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); + nDamage = GetLevelByClass(CLASS_TYPE_SOULBORN, oPC); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Opposition Failed: You don't exist"; + sHit = "Smite Opposition Hit"; + sMiss = "Smite Opposition Missed"; + } + + else if(nType == SMITE_TYPE_CHAOS) + { + eSmite = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); + nDamage = GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oPC); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Chaos Failed: You don't exist"; + sHit = "Smite Chaos Hit"; + sMiss = "Smite Chaos Missed"; + } + + else if(nType == SMITE_TYPE_RAMETHENE) + { + eSmite = EffectVisualEffect(VFX_COM_HIT_FIRE); + nDamage = d6(PRCGetCreatureSize(oTarget)-3); + if(nDamage < 1) + nDamage = 0; + nAttack = 0; + nDamageType = DAMAGE_TYPE_MAGICAL; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Dragon Smite Failed: You don't exist"; + sHit = "Dragon Smite Hit"; + sMiss = "Dragon Smite Missed"; + } + + else if(nType == SMITE_TYPE_ANDRAS) + { + eSmite = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); + nDamage = GetBinderLevel(oPC, VESTIGE_ANDRAS); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Good or Evil Failed: You don't exist"; + sHit = "Smite Good or Evil Hit"; + sMiss = "Smite Good or Evil Missed"; + } + + else if(nType == SMITE_TYPE_TRIAD) + { + eSmite = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); + nDamage = GetBinderLevel(oPC, VESTIGE_THETRIAD); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Evil Failed: You don't exist"; + sHit = "Smite Evil Hit"; + sMiss = "Smite Evil Missed"; + } + + else if(nType == SMITE_TYPE_DESHARIS) + { + eSmite = EffectVisualEffect(VFX_FNF_STRIKE_HOLY); + nDamage = GetBinderLevel(oPC, VESTIGE_DESHARIS); + if(nDamage < 1) + nDamage = 0; + nAttack = GetAbilityModifier(ABILITY_CHARISMA, oPC); + if(nAttack < 1) + nAttack = 0; + nDamageType = DAMAGE_TYPE_DIVINE; + sFailedTarget = "Target cannot be invalid"; + sFailedSmiter = "Smite Natural Soul Failed: You don't exist"; + sHit = "Smite Natural Soul Hit"; + sMiss = "Smite Natural Soul Missed"; + } + + //check target is valid + //and show message if not + if(nTargetInvalid) + FloatingTextStringOnCreature(sFailedTarget, oPC); + + //check smiter is valid + //and show message if not + else if(nSmiterInvalid) + FloatingTextStringOnCreature(sFailedSmiter, oPC); + + //check for ranged weapon + //ranged smite was never finished + //if it was, here would be the place to put it! + else if (GetWeaponRanged(oWeapon) && !GetHasFeat(FEAT_RANGED_SMITE, oPC)) + FloatingTextStringOnCreature("Smite Failed: cannot use ranged weapon", oPC); + + //passed checks, do the actual smite + else + { + // Extra smite damage + nDamage += GetEssentiaInvestedFeat(oPC, FEAT_SAPPHIRE_SMITE); + //add epic smiting damage + int iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_1) ? 2:1; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_2) ? 3:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_3) ? 4:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_4) ? 5:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_5) ? 6:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_6) ? 7:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_7) ? 8:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_8) ? 9:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_9) ? 10:iEpicSmite; + iEpicSmite = GetHasFeat(FEAT_EPIC_GREAT_SMITING_10)? 11:iEpicSmite; + nDamage *= iEpicSmite; + + //Double damage if has Devastating Smite active + if(GetHasSpellEffect(SPELL_DEVASTATING_SMITE, oPC)) nDamage += nDamage; + + //whew, now we can do the actual smite through the combat engine + PerformAttackRound(oTarget, oPC, eSmite, 0.0, nAttack, nDamage, nDamageType, FALSE, sHit, sMiss); + + // Target is valid and we know it's an enemy and we're in combat + DelayCommand(0.25, AssignCommand(oPC, ActionAttack(oTarget))); + } +} \ No newline at end of file diff --git a/src/include/prc_inc_sneak.nss b/src/include/prc_inc_sneak.nss new file mode 100644 index 0000000..3c75aa8 --- /dev/null +++ b/src/include/prc_inc_sneak.nss @@ -0,0 +1,730 @@ + +// * Various functions to determine sneak dice. + +// * Used to find the total sneak dice a character is capable of. +int GetTotalSneakAttackDice(object oPC); + +// * Used to find the total rogue sneak dice a character is capable of. +// ----------------------------------------------------------------------------------------- +// Future PRC's go here. DO NOT ADD ROGUE/BLACKGUARD/ASSASSIN SNEAK ATTACKS AS CLASS FEATS. +// Placeholder feats are fine, even encouraged. Example: "Ranged Sneak Attack +1d6". +// The feat should do nothing, just show that you have the bonus. +// ----------------------------------------------------------------------------------------- +int GetRogueSneak(object oPC); + +// * Used to find the total blackguard sneak dice a character is capable of. +int GetBlackguardSneak(object oPC); + +// * Used to find the total assassin sneak dice a character is capable of. +int GetAssassinSneak(object oPC); + +// * Used to find how much a character has taken "Improved Sneak Attack". +int GetEpicFeatSneak(object oPC); + +//::////////////////////////////////////////////// +//:: Sneak Attack Functions +//::////////////////////////////////////////////// + +// Checks if attacker is flanking the defender or not +int GetIsFlanked(object oDefender, object oAttacker); + +// Checks if an AoE spell is flanking the defender +int GetIsAOEFlanked(object oDefender, object oAttacker); + +// Determines if a creature is helpless. +// (effective dex modifier of 0, and can be Coup De Graced). +int GetIsHelpless(object oDefender); + +// Returns if oDefender is denied dex bonus to AC from spells +// int nIgnoreUD - ignores Uncanny Dodge +int GetIsDeniedDexBonusToAC(object oDefender, object oAttacker, int nIgnoreUD = FALSE); + +// Returns FALSE if oDefender has no concealment +// or the int amount of concealment on the defender. +int GetIsConcealed(object oDefender, object oAttacker); + +// Returns true if the Attacker can Sneak Attack the target +int GetCanSneakAttack(object oDefender, object oAttacker); + +// Returns Sneak Attack Damage +int GetSneakAttackDamage(int iSneakAttackDice); + +//Returns applicable elemental type for Dragonfire Strike +int GetDragonfireDamageType(object oPC); + +// * Used to find the total favoured enemy bonus a character is capable of. +int GetFavouredEnemyBonus(object oPC); + +//::////////////////////////////////////////////// +//:: Includes +//::////////////////////////////////////////////// + +//#include "prc_class_const" +//#include "prc_feat_const" +#include "tob_move_const" +#include "prc_x2_itemprop" + +//::////////////////////////////////////////////// +//:: Definitions +//::////////////////////////////////////////////// + +int GetTotalSneakAttackDice(object oPC) +{ + int iSneakAttackDice = GetRogueSneak(oPC) + GetBlackguardSneak(oPC) + + GetAssassinSneak(oPC) + GetEpicFeatSneak(oPC); + return iSneakAttackDice; +} + +int GetRogueSneak(object oPC) +{ + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + int nWeaponType = GetBaseItemType(oWeapon); + + int iClassLevel; + int iRogueSneak = 0; + + // Rogue + iClassLevel = GetLevelByClass(CLASS_TYPE_ROGUE, oPC); + // Daring Outlaw + if (iClassLevel && GetHasFeat(FEAT_DARING_OUTLAW, oPC)) + iClassLevel += GetLevelByClass(CLASS_TYPE_SWASHBUCKLER, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Arcane Trickster (Epic) + iClassLevel = GetLevelByClass(CLASS_TYPE_ARCTRICK, oPC); + if (iClassLevel >= 12) iRogueSneak += (iClassLevel - 10) / 2; + + // Black Flame Zealot + iClassLevel = GetLevelByClass(CLASS_TYPE_BFZ, oPC); + if (iClassLevel) iRogueSneak += iClassLevel / 3; + + // Nightshade + iClassLevel = GetLevelByClass(CLASS_TYPE_NIGHTSHADE, oPC); + if (iClassLevel) iRogueSneak += iClassLevel / 3; + + // Outlaw Crimson Road + //iClassLevel = GetLevelByClass(CLASS_TYPE_OUTLAW_CRIMSON_ROAD, oPC); + //if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Temple Raider + //iClassLevel = GetLevelByClass(CLASS_TYPE_TEMPLE_RAIDER, oPC); + //if (iClassLevel>= 2) iRogueSneak += (iClassLevel + 1) / 3; + + // Ghost-Faced Killer + iClassLevel = GetLevelByClass(CLASS_TYPE_GHOST_FACED_KILLER, oPC); + if (iClassLevel >= 2) iRogueSneak += ((iClassLevel + 1) / 3); + + // Ninja + iClassLevel = GetLevelByClass(CLASS_TYPE_NINJA, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Shadow Thief of Amn + iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOW_THIEF_AMN, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Slayer of Domiel + iClassLevel = GetLevelByClass(CLASS_TYPE_SLAYER_OF_DOMIEL, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Crinti Shadow Marauder + iClassLevel = GetLevelByClass(CLASS_TYPE_CRINTI_SHADOW_MARAUDER, oPC); + if (iClassLevel) iRogueSneak += iClassLevel / 2; + + // Cultist of the Shattered Peak + iClassLevel = GetLevelByClass(CLASS_TYPE_CULTIST_SHATTERED_PEAK, oPC); + if (iClassLevel) iRogueSneak += iClassLevel / 2; + + // Skullclan Hunter + iClassLevel = GetLevelByClass(CLASS_TYPE_SKULLCLAN_HUNTER, oPC); + if (iClassLevel) iRogueSneak += iClassLevel / 3; + + // Shadowmind + iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWMIND, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Psychic Rogue + iClassLevel = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 2) / 3; + + // Unseen Seer + iClassLevel = GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 2) / 3; + + // Fist of Dal Quor + iClassLevel = GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + // Umbral Disciple + iClassLevel = GetLevelByClass(CLASS_TYPE_UMBRAL_DISCIPLE, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 3; + + // Dragon Devotee and Hand of the Winged Masters + int nBonusFeatDice = 0; + int nCount; + + // First check for 10d6 to 6d6 (second 2DA range: 24905–24901) + for(nCount = FEAT_SPECIAL_SNEAK_ATTACK_10D6; nCount >= FEAT_SPECIAL_SNEAK_ATTACK_6D6; nCount--) + { + if (GetHasFeat(nCount, oPC)) + { + nBonusFeatDice = nCount - FEAT_SPECIAL_SNEAK_ATTACK_6D6 + 6; + //if (DEBUG) DoDebug("prc_inc_sneak: Bonus Sneak Dice: " + IntToString(nBonusFeatDice)); + break; + } + } + // If nothing found yet, check original range 5d6 to 1d6 + if (nBonusFeatDice == 0) + { + for(nCount = FEAT_SPECIAL_SNEAK_ATTACK_5D6; nCount >= FEAT_SPECIAL_SNEAK_ATTACK_1D6; nCount--) + { + if (GetHasFeat(nCount, oPC)) + { + nBonusFeatDice = nCount - FEAT_SPECIAL_SNEAK_ATTACK_1D6 + 1; + //if (DEBUG) DoDebug("prc_inc_sneak: Bonus Sneak Dice: " + IntToString(nBonusFeatDice)); + break; + } + } + } + +/* //Dragon Devotee and Hand of the Winged Masters + int nBonusFeatDice = 0; + int nCount; + for(nCount = FEAT_SPECIAL_SNEAK_ATTACK_5D6; nCount >= FEAT_SPECIAL_SNEAK_ATTACK_1D6; nCount--) + { + if (GetHasFeat(nCount,oPC)) + { + nBonusFeatDice = nCount - FEAT_SPECIAL_SNEAK_ATTACK_1D6 + 1; + //if (DEBUG) DoDebug("prc_inc_sneak: Bonus Sneak Dice: " + IntToString(nBonusFeatDice)); + break; + } + } */ + + //:: Shadowbane Inquisitor + iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBANE_INQUISITOR, oPC); + if (iClassLevel >= 4) + { + iRogueSneak += 1 + (iClassLevel - 4) / 3; + } + +/* // Shadowbane Inquisitor + iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBANE_INQUISITOR, oPC); + if (iClassLevel >= 4) iRogueSneak++; + if (iClassLevel >= 7) iRogueSneak++; + if (iClassLevel >= 10) iRogueSneak++; */ + + // Shadowbane Stalker + iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER, oPC); + if (iClassLevel) iRogueSneak += iClassLevel / 3; + + //Naztharune Rakshasa racial sneak attack + if(GetHasFeat(FEAT_RACIAL_SNEAK_6D6)) iRogueSneak += 6; + + if (GetRacialType(oPC) == RACIAL_TYPE_MARRULURK) iRogueSneak += 2; + + if(nWeaponType == BASE_ITEM_LONGBOW || nWeaponType == BASE_ITEM_SHORTBOW) + { + // Peerless Archer + iClassLevel = GetLevelByClass(CLASS_TYPE_PEERLESS, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 2) / 3; + } + else if(nWeaponType == BASE_ITEM_SLING) + { + // Halfling Warslinger + iClassLevel = GetLevelByClass(CLASS_TYPE_HALFLING_WARSLINGER, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + } + else if(nWeaponType == BASE_ITEM_WHIP) + { + // Lasher + iClassLevel = GetLevelByClass(CLASS_TYPE_LASHER, oPC); + if (iClassLevel > 0) iRogueSneak += ((iClassLevel - 1) / 4) + 1; + } + + //Justice of Weald and Woe + iClassLevel = GetLevelByClass(CLASS_TYPE_JUSTICEWW, oPC); + if(iClassLevel > 1) iRogueSneak++; + if(iClassLevel > 6) iRogueSneak++; + + //Shadowblade + iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBLADE, oPC); + if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2; + + + if(GetHasSpellEffect(MOVE_SH_ASSASSINS_STANCE, oPC)) + { + iRogueSneak += 2; + } + + if(GetLocalInt(oPC, "SacredStrike")) + { + iRogueSneak += GetLocalInt(oPC, "SacredStrike"); + } + if(GetLocalInt(oPC, "PsyRogueSneak")) + { + iRogueSneak += GetLocalInt(oPC, "PsyRogueSneak"); + } + if(GetLocalInt(oPC, "CunningStrike")) + { + iRogueSneak += 1; + } + if(GetLocalInt(oPC, "UmbralSneak")) + { + iRogueSneak += GetLocalInt(oPC, "UmbralSneak"); + } + if(GetLocalInt(oPC, "MalphasSneak")) + { + iRogueSneak += GetLocalInt(oPC, "MalphasSneak"); + } + if(GetLocalInt(oPC, "AndroSneak")) + { + iRogueSneak += GetLocalInt(oPC, "AndroSneak"); + } + if (GetLocalInt(oPC, "FactotumSneak")) + { + iRogueSneak += (GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC) + 1) / 2; + } + + if(iRogueSneak > 0) //the feats only apply if you already have Sneak Attack + iRogueSneak += nBonusFeatDice; + + // ----------------------------------------------------------------------------------------- + // Future PRC's go here. DO NOT ADD ROGUE/BLACKGUARD/ASSASSIN SNEAK ATTACKS AS CLASS FEATS. + // Placeholder feats are fine, even encouraged. Example: "Ranged Sneak Attack +1d6". + // The feat should do nothing, just show that you have the bonus. + // ----------------------------------------------------------------------------------------- + + //if (DEBUG) DoDebug("prc_inc_sneak: Rogue Sneak Dice: " + IntToString(iRogueSneak)); + return iRogueSneak; +} + +// -------------------------------------------------- +// PLEASE DO NOT ADD ANY NEW CLASSES TO THIS FUNCTION +// -------------------------------------------------- +int GetBlackguardSneak(object oPC) +{ + int iClassLevel; + int iBlackguardSneak = 0; + + // Blackguard + iClassLevel = GetLevelByClass(CLASS_TYPE_BLACKGUARD, oPC); + if (iClassLevel) iBlackguardSneak += (iClassLevel - 1) / 3; + if ((iClassLevel) && (GetLevelByClass(CLASS_TYPE_PALADIN) >= 5)) iBlackguardSneak++; // bonus for pal/bg + + // Ninja Spy + iClassLevel = GetLevelByClass(CLASS_TYPE_NINJA_SPY, oPC); + if (iClassLevel) iBlackguardSneak += (iClassLevel + 1) / 3; + + // Arcane Trickster (Pre-Epic) + iClassLevel = GetLevelByClass(CLASS_TYPE_ARCTRICK, oPC); + if ((iClassLevel >= 2) && (iClassLevel < 11)) iBlackguardSneak += iClassLevel / 2; + if (iClassLevel >= 11) iBlackguardSneak += 5; + + //:: Disciple of Baalzebul + iClassLevel = GetLevelByClass(CLASS_TYPE_DISC_BAALZEBUL, oPC); + if (iClassLevel >= 2) + { + iBlackguardSneak += 1 + (iClassLevel - 2) / 3; + } + +/* // Disciple of Baalzebul + iClassLevel = GetLevelByClass(CLASS_TYPE_DISC_BAALZEBUL, oPC); + if ((iClassLevel >= 2) && (iClassLevel < 5)) iBlackguardSneak++; + if ((iClassLevel >= 5) && (iClassLevel < 8)) iBlackguardSneak += 2; + if (iClassLevel >= 8) iBlackguardSneak += 3; */ + + //if (DEBUG) DoDebug("prc_inc_sneak: Blackguard Sneak Dice: " + IntToString(iBlackguardSneak)); + return iBlackguardSneak; +} + +// -------------------------------------------------- +// PLEASE DO NOT ADD ANY NEW CLASSES TO THIS FUNCTION +// -------------------------------------------------- +int GetAssassinSneak(object oPC) +{ + int iClassLevel; + int iAssassinSneakDice = 0; + + // Assassin + iClassLevel = GetLevelByClass(CLASS_TYPE_ASSASSIN, oPC); + if (iClassLevel) iAssassinSneakDice += (iClassLevel + 1) / 2; + + // Telflammar Shadowlord + if(GetLevelByClass(CLASS_TYPE_SHADOWLORD, oPC) > 5) iAssassinSneakDice++; + + //if (DEBUG) DoDebug("prc_inc_sneak: Assassin Sneak Dice: " + IntToString(iAssassinSneakDice)); + return iAssassinSneakDice; +} + +int GetEpicFeatSneak(object oPC) +{ + int iEpicFeatDice = 0; + int iCount; + + // Basically searches top-down for improved sneak attack feats until it finds one. + for(iCount = FEAT_EPIC_IMPROVED_SNEAK_ATTACK_10; iCount >= FEAT_EPIC_IMPROVED_SNEAK_ATTACK_1; iCount--) + { + if (GetHasFeat(iCount,oPC)) + { + iEpicFeatDice = (iCount + 1) - FEAT_EPIC_IMPROVED_SNEAK_ATTACK_1; + break; + } + } + + //if (DEBUG) DoDebug("prc_inc_sneak: Epic Sneak Dice: " + IntToString(iEpicFeatDice)); + return iEpicFeatDice; +} + +//::////////////////////////////////////////////// +//:: Sneak Attack Function Definitions +//::////////////////////////////////////////////// + +int GetIsFlanked(object oDefender, object oAttacker) +{ + int bReturnVal = FALSE; + //if (DEBUG) DoDebug("Starting GetIsFlanked"); + + if(GetIsObjectValid(oAttacker) && GetIsObjectValid(oDefender)) + { + // I am assuming that if the Defender is facing away from the + // Attacker then the Defender is flanked, as NWN "turns" an + // attacker towards the defender + + vector vDefender = AngleToVector(GetFacing(oDefender)); + vector vAttacker = AngleToVector(GetFacing(oAttacker)); + vector vResult = vDefender + vAttacker; + //if (DEBUG) DoDebug("GetIsFlanked: End Section #1"); + float iMagDefender = VectorMagnitude(vDefender); + float iMagResult = VectorMagnitude(vResult); + + // If the magnitude of the Defenders facing vector is greater than the + // result of the magnitude of the vector addition of the Attackers and + // Defenders facing then the Defender is flanked. + + if(iMagDefender < iMagResult) + { + bReturnVal = TRUE; + } + } +//if (DEBUG) DoDebug("GetIsFlanked: End Section #2"); + return bReturnVal; +} + +// Checks if an AoE spell is against someone distracted in meleee combat +int GetIsAOEFlanked(object oDefender, object oAttacker) +{ + int bReturnVal = TRUE; + + // if they are not in combat then they are automatically flanked (surprise round) + if(!PRCGetIsFighting(oDefender) || !GetIsInCombat(oDefender) ) + { + // checks if they are attacking something other than the caster + object oTarget = GetAttackTarget(oDefender); + if(oTarget == oAttacker) bReturnVal = FALSE; + } + + return bReturnVal; +} + +int GetIsHelpless(object oDefender) +{ + // Does not apply when grappled + if (GetLocalInt(oDefender, "IsGrappled") && !GetLocalInt(oDefender, "UnconsciousGrapple")) return FALSE; + + // PnP describes a helpless defender as + // A helpless foe - one who is bound, held, sleeping, paralyzed, + // unconscious, or otherwise at your mercy - is an easy target. + return ( PRCGetHasEffect(EFFECT_TYPE_PARALYZE, oDefender) + || PRCGetHasEffect(EFFECT_TYPE_SLEEP, oDefender) + || PRCGetHasEffect(EFFECT_TYPE_PETRIFY, oDefender) + || PRCGetHasEffect(EFFECT_TYPE_CUTSCENE_PARALYZE, oDefender) ); +} + +int GetIsDeniedDexBonusToAC(object oDefender, object oAttacker, int nIgnoreUD = FALSE) +{ + int bIsDeniedDex = FALSE; + int bDefenderHasTrueSight = PRCGetHasEffect(EFFECT_TYPE_TRUESEEING, oDefender); + int bDefenderCanSeeInvisble = PRCGetHasEffect(EFFECT_TYPE_SEEINVISIBLE, oDefender); + int bDefenderIsKnockedDown = GetHasFeatEffect(FEAT_KNOCKDOWN, oDefender) || GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN, oDefender); + + // if the player is helpess, they are automatically denied dex bonus. + if( GetIsHelpless(oDefender) ) return TRUE; + + // Forces it + if (GetLocalInt(oAttacker, "PRC_SB_UNEXPECTED")) return TRUE; + + // if the player is not fighting, then this is the "surprise round" + if( !PRCGetIsFighting(oDefender) || !GetIsInCombat(oDefender) ) + { + bIsDeniedDex = TRUE; + } + + // In NwN, knocked down targets are counted as denied dex bonus to AC. + if( bDefenderIsKnockedDown ) bIsDeniedDex = TRUE; + + // if defender has spell effect on them causing them to be denied dex bonus to AC. + if( PRCGetHasEffect(EFFECT_TYPE_BLINDNESS, oDefender) ) bIsDeniedDex = TRUE; + else if( PRCGetHasEffect(EFFECT_TYPE_ENTANGLE, oDefender) ) bIsDeniedDex = TRUE; + else if( PRCGetHasEffect(EFFECT_TYPE_FRIGHTENED, oDefender) ) bIsDeniedDex = TRUE; + else if( PRCGetHasEffect(EFFECT_TYPE_STUNNED, oDefender) ) bIsDeniedDex = TRUE; + + // Note: This is wrong by PnP rules... but Bioware allows auto sneaks on Dazed targets. + // to keep in tune with the game engine I'll leave this active. + else if( PRCGetHasEffect(EFFECT_TYPE_DAZED, oDefender) ) bIsDeniedDex = TRUE; + + // if attacker is invisvisible/hiding/etc. + else if( PRCGetHasEffect(EFFECT_TYPE_INVISIBILITY, oAttacker) && !bDefenderHasTrueSight && !bDefenderCanSeeInvisble ) + { + bIsDeniedDex = TRUE; + } + else if( PRCGetHasEffect(EFFECT_TYPE_IMPROVEDINVISIBILITY, oAttacker) && !bDefenderHasTrueSight && !bDefenderCanSeeInvisble ) + { + bIsDeniedDex = TRUE; + } + else if( !GetObjectSeen(oAttacker, oDefender) ) + { + bIsDeniedDex = TRUE; + } + + // Check for Uncanny Dodge Vs. Sneak Attack. + if( GetHasFeat(FEAT_UNCANNY_DODGE_2, oDefender) && !nIgnoreUD ) + { + if(GetLevelByClass(CLASS_TYPE_DWARVENDEFENDER, oDefender)) + return FALSE; + + // +4 because a rogue has to be 4 levels higher to flank + int iUncannyDodgeLevels = GetLevelByClass(CLASS_TYPE_ASSASSIN , oDefender) + + GetLevelByClass(CLASS_TYPE_BARBARIAN , oDefender) + + GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE , oDefender) + + GetLevelByClass(CLASS_TYPE_ROGUE , oDefender) + + GetLevelByClass(CLASS_TYPE_SHADOWDANCER, oDefender) + + 4; + + int iSneakAttackLevels = GetLevelByClass(CLASS_TYPE_BOWMAN , oAttacker) + + GetLevelByClass(CLASS_TYPE_NINJA , oAttacker) + + GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE , oAttacker) + + GetLevelByClass(CLASS_TYPE_ROGUE , oAttacker) + + // add other sneak attacking PrC's here + + GetLevelByClass(CLASS_TYPE_ARCTRICK , oAttacker) + + GetLevelByClass(CLASS_TYPE_ASSASSIN , oAttacker) + + GetLevelByClass(CLASS_TYPE_BFZ , oAttacker) + + GetLevelByClass(CLASS_TYPE_BLACKGUARD , oAttacker) + + GetLevelByClass(CLASS_TYPE_BLARCHER , oAttacker) + + GetLevelByClass(CLASS_TYPE_DISC_BAALZEBUL , oAttacker) + + GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR , oAttacker) + + GetLevelByClass(CLASS_TYPE_GHOST_FACED_KILLER , oAttacker) + + GetLevelByClass(CLASS_TYPE_HALFLING_WARSLINGER, oAttacker) + + GetLevelByClass(CLASS_TYPE_JUSTICEWW , oAttacker) + + GetLevelByClass(CLASS_TYPE_LASHER , oAttacker) + + GetLevelByClass(CLASS_TYPE_NIGHTSHADE , oAttacker) + + GetLevelByClass(CLASS_TYPE_NINJA_SPY , oAttacker) + + GetLevelByClass(CLASS_TYPE_PEERLESS , oAttacker) + + GetLevelByClass(CLASS_TYPE_SHADOWBLADE , oAttacker) + + GetLevelByClass(CLASS_TYPE_SHADOWLORD , oAttacker) + + GetLevelByClass(CLASS_TYPE_SHADOWMIND , oAttacker) + + GetLevelByClass(CLASS_TYPE_SKULLCLAN_HUNTER , oAttacker) + + GetLevelByClass(CLASS_TYPE_SLAYER_OF_DOMIEL , oAttacker); + + if(iUncannyDodgeLevels > iSneakAttackLevels) + { + bIsDeniedDex = FALSE; + } + } + + return bIsDeniedDex; +} + +int GetIsConcealed(object oDefender, object oAttacker) +{ + int bIsConcealed = FALSE; + + int bAttackerHasTrueSight = PRCGetHasEffect(EFFECT_TYPE_TRUESEEING, oAttacker); + int bAttackerCanSeeInvisble = PRCGetHasEffect(EFFECT_TYPE_SEEINVISIBLE, oAttacker); + int bAttackerUltraVision = PRCGetHasEffect(EFFECT_TYPE_ULTRAVISION, oAttacker); + + if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_50, oDefender) ) bIsConcealed = 50; + else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_40, oDefender) ) bIsConcealed = 40; + else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_30, oDefender) ) bIsConcealed = 30; + else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_20, oDefender) ) bIsConcealed = 20; + else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_10, oDefender) ) bIsConcealed = 10; + + // darkness, invisible, imp invisible + else if(GetStealthMode(oDefender) == STEALTH_MODE_ACTIVATED && !GetObjectSeen(oDefender, oAttacker) ) bIsConcealed = TRUE; + else if(PRCGetHasEffect(EFFECT_TYPE_SANCTUARY, oDefender) && !bAttackerHasTrueSight ) + { + // if they player is hidden you know enough to try attacking, give 50% miss chance + // as that is the highest concealment normally allowed. + // couldn't find any rules that governed this though. + bIsConcealed = 50; + } + else if(PRCGetHasEffect(EFFECT_TYPE_INVISIBILITY, oDefender) && !bAttackerHasTrueSight && !bAttackerCanSeeInvisble ) + { + bIsConcealed = 50; + } + else if(PRCGetHasEffect(EFFECT_TYPE_IMPROVEDINVISIBILITY, oDefender) && !bAttackerHasTrueSight && !bAttackerCanSeeInvisble ) + { + bIsConcealed = 50; + } + else if(PRCGetHasEffect(EFFECT_TYPE_DARKNESS, oDefender) && !bAttackerHasTrueSight && !bAttackerUltraVision) + { + bIsConcealed = 50; + } + else if(GetHasFeatEffect(FEAT_EMPTY_BODY, oDefender) ) + { + bIsConcealed = 50; + } + //else if(PRCGetHasEffect(EFFECT_TYPE_ETHEREAL, oDefender) && !bAttackerHasTrueSight && !bAttackerCanSeeInvisble ) + //{ + // bIsConcealed = TRUE; + //} + + // spell effects + else if(GetHasSpellEffect(1764 , oDefender) && !bAttackerHasTrueSight) // blur spell + { + bIsConcealed = 20; + } + else if(GetHasSpellEffect(SPELL_DISPLACEMENT , oDefender) && !bAttackerHasTrueSight) + { + bIsConcealed = 50; + } + else if(GetHasSpellEffect(SPELL_SHADOW_EVADE , oDefender) && !bAttackerHasTrueSight) + { + int iSDlevel = GetLevelByClass(CLASS_TYPE_SHADOWDANCER, oDefender); + if(iSDlevel <= 4) bIsConcealed = 5; + if(iSDlevel <= 6) bIsConcealed = 10; + if(iSDlevel <= 8) bIsConcealed = 15; + if(iSDlevel <= 10) bIsConcealed = 20; + } + + // this is the catch-all effect + else if(PRCGetHasEffect(EFFECT_TYPE_CONCEALMENT, oDefender) && !bAttackerHasTrueSight) + { + if(bIsConcealed == FALSE) bIsConcealed = TRUE; + } + + if(GetLocalInt(oAttacker, "PRC_SB_UNERRING")) + { + bIsConcealed = FALSE; + return bIsConcealed; + } + return bIsConcealed; +} + +int GetCanSneakAttack(object oDefender, object oAttacker) +{ + //cant sneak non-creatures + if(GetObjectType(oDefender) != OBJECT_TYPE_CREATURE) + return FALSE; + + // Can't sneak attack if you're in a grapple + if(GetLocalInt(oAttacker, "IsGrappled")) + return FALSE; + + int bReturnVal = FALSE; + int bIsInRange = FALSE; + int bIsFlanked = GetIsFlanked(oDefender, oAttacker); + int bIsDeniedDex = GetIsDeniedDexBonusToAC(oDefender, oAttacker); + + float fDistance = GetDistanceBetween(oAttacker, oDefender); + if(fDistance <= FeetToMeters(30.0f) ) bIsInRange = TRUE; + + // Is only run if enemy is indeed flanked or denied dex bonus to AC + // otherwise there is no reason to check further + if(bIsFlanked || bIsDeniedDex && bIsInRange) + { + // so far they can be sneaked + bReturnVal = TRUE; + + // checking for other factors that remove sneak attack + if( GetIsImmune(oDefender, IMMUNITY_TYPE_CRITICAL_HIT, OBJECT_INVALID) ) bReturnVal = FALSE; + if( GetIsImmune(oDefender, IMMUNITY_TYPE_SNEAK_ATTACK, OBJECT_INVALID) ) bReturnVal = FALSE; + // Skullclan Hunters can sneak attack undead, so they return true here. + if( GetLevelByClass(CLASS_TYPE_SKULLCLAN_HUNTER, oAttacker) && GetRacialType(oDefender) == RACIAL_TYPE_UNDEAD) bReturnVal = TRUE; + + if( GetIsConcealed(oDefender, oAttacker) ) + bReturnVal = FALSE; + } + + return bReturnVal; +} + +int GetSneakAttackDamage(int iSneakAttackDice) +{ + int iSneakAttackDamage = d6(iSneakAttackDice); + return iSneakAttackDamage; +} + +int GetDragonfireDamageType(object oPC) +{ + //Elemental Immunities for various dragon types. + int iType = GetHasFeat(FEAT_BLACK_DRAGON, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_BROWN_DRAGON, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_COPPER_DRAGON, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_GREEN_DRAGON, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_BRASS_DRAGON, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_GOLD_DRAGON, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_RED_DRAGON, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_LUNG_WANG_DRAGON, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_BATTLE_DRAGON, oPC) ? DAMAGE_TYPE_SONIC : + GetHasFeat(FEAT_EMERALD_DRAGON, oPC) ? DAMAGE_TYPE_SONIC : + GetHasFeat(FEAT_HOWLING_DRAGON, oPC) ? DAMAGE_TYPE_SONIC : + GetHasFeat(FEAT_BLUE_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_BRONZE_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_OCEANUS_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_SAPPHIRE_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_SONG_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_SHEN_LUNG_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_CRYSTAL_DRAGON, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_TOPAZ_DRAGON, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_SILVER_DRAGON, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_WHITE_DRAGON, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_DRACONIC_HERITAGE_BK, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_DRACONIC_HERITAGE_CP, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_DRACONIC_HERITAGE_GR, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_DRACONIC_HERITAGE_BS, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_DRACONIC_HERITAGE_GD, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_DRACONIC_HERITAGE_RD, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_DRACONIC_HERITAGE_EM, oPC) ? DAMAGE_TYPE_SONIC : + GetHasFeat(FEAT_DRACONIC_HERITAGE_BL, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_DRACONIC_HERITAGE_BZ, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_DRACONIC_HERITAGE_SA, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_DRACONIC_HERITAGE_CR, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_DRACONIC_HERITAGE_TP, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_DRACONIC_HERITAGE_SR, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_DRACONIC_HERITAGE_WH, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BK, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_CP, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_GR, oPC) ? DAMAGE_TYPE_ACID : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BS, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_GD, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_RD, oPC) ? DAMAGE_TYPE_FIRE : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_EM, oPC) ? DAMAGE_TYPE_SONIC : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BL, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BZ, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_SA, oPC) ? DAMAGE_TYPE_ELECTRICAL : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_CR, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_TP, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_SR, oPC) ? DAMAGE_TYPE_COLD : + GetHasFeat(FEAT_KOB_DRAGONWROUGHT_WH, oPC) ? DAMAGE_TYPE_COLD : + DAMAGE_TYPE_FIRE; // If none match, make the itemproperty invalid + + return iType; +} + +int GetFavouredEnemyBonus(object oPC) +{ + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + int nWeaponType = GetBaseItemType(oWeapon); + + int nClass; + int nFE = 0; + + // Ranger + nClass = GetLevelByClass(CLASS_TYPE_RANGER, oPC); + if (nClass) nFE += nClass/5 + 1; + + if (DEBUG) DoDebug("prc_inc_sneak: Favoured Enemy Bonus: " + IntToString(nFE)); + return nFE; +} + +//;: void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_sp_tch.nss b/src/include/prc_inc_sp_tch.nss new file mode 100644 index 0000000..45dc2b4 --- /dev/null +++ b/src/include/prc_inc_sp_tch.nss @@ -0,0 +1,169 @@ +int PRCDoRangedTouchAttack(object oTarget, int nDisplayFeedback = TRUE, object oCaster = OBJECT_SELF, int nAttackBonus = 0); +int PRCDoMeleeTouchAttack(object oTarget, int nDisplayFeedback = TRUE, object oCaster = OBJECT_SELF, int nAttackBonus = 0); + +//#include "prc_inc_sneak" +#include "prc_inc_combat" +//#include "prc_inc_template" + +int PRCDoRangedTouchAttack(object oTarget, int nDisplayFeedback = TRUE, object oCaster = OBJECT_SELF, int nAttackBonus = 0) +{ + if(GetLocalInt(oTarget, "Dymond_Deflect")) + { + DeleteLocalInt(oTarget, "Dymond_Deflect"); + return FALSE; + } + if(GetLocalInt(oCaster, "AttackHasHit")) + return GetLocalInt(oCaster, "AttackHasHit"); + string sCacheName = "AttackHasHit_"+ObjectToString(oTarget); + if(GetLocalInt(oCaster, sCacheName)) + return GetLocalInt(oCaster, sCacheName); + if(GetPersistantLocalInt(oCaster, "template_102")) // TEMPLATE_DEMILICH + nAttackBonus += GetHitDice(oCaster); + if(GetLocalInt(oCaster, "WarsoulTyrant")) // Hobgoblin Warsoul + nAttackBonus += GetLocalInt(oCaster, "WarsoulTyrant"); + + if(GetHasFeat(FEAT_SHIELD_WARD, oTarget)) + { + int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD) + nAttackBonus -= GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + } + else if(GetHasFeat(FEAT_PARRYING_SHIELD, oTarget)) // Yes, these two are mostly identical + { + int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD) + nAttackBonus -= GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + } + + int nResult = GetAttackRoll(oTarget,oCaster,OBJECT_INVALID,0,nAttackBonus,0,nDisplayFeedback,0.0,TOUCH_ATTACK_RANGED_SPELL); + //Ranged Recall only applies on misses, need a swift action + if (GetHasFeat(FEAT_RANGED_RECALL, oCaster) && nResult == 0) + { + int nRecall = GetLocalInt(oCaster, "RangedRecall"); + // Only get three uses a day + if (3 > nRecall) + { + if (TakeSwiftAction(oCaster)) + { + SetLocalInt(oCaster, "RangedRecall", nRecall+1); + // Reroll with a -5 penalty + nResult = GetAttackRoll(oTarget,oCaster,OBJECT_INVALID,0,nAttackBonus-5,0,nDisplayFeedback,0.0,TOUCH_ATTACK_MELEE_SPELL); + } + } + } + SetLocalInt(oCaster, sCacheName, nResult); + DelayCommand(1.0, DeleteLocalInt(oCaster, sCacheName)); + return nResult; +} + +int PRCDoMeleeTouchAttack(object oTarget, int nDisplayFeedback = TRUE, object oCaster = OBJECT_SELF, int nAttackBonus = 0) +{ + if(GetLocalInt(oCaster, "AttackHasHit")) + return GetLocalInt(oCaster, "AttackHasHit"); + string sCacheName = "AttackHasHit_"+ObjectToString(oTarget); + if(GetLocalInt(oCaster, sCacheName)) + return GetLocalInt(oCaster, sCacheName); + if(GetPersistantLocalInt(oCaster, "template_102")) // TEMPLATE_DEMILICH + nAttackBonus += GetHitDice(oCaster); + if(GetLocalInt(oCaster, "WarsoulTyrant")) // Hobgoblin Warsoul + nAttackBonus += GetLocalInt(oCaster, "WarsoulTyrant"); + if(GetHasFeat(FEAT_SHIELD_WARD, oTarget)) + { + int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD) + nAttackBonus -= GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + } + else if(GetHasFeat(FEAT_PARRYING_SHIELD, oTarget)) // Yes, these two are mostly identical + { + int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD) + nAttackBonus -= GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)); + } + int nResult = GetAttackRoll(oTarget,oCaster,OBJECT_INVALID,0,nAttackBonus,0,nDisplayFeedback,0.0,TOUCH_ATTACK_MELEE_SPELL); + SetLocalInt(oCaster, sCacheName, nResult); + DelayCommand(1.0, DeleteLocalInt(oCaster, sCacheName)); + return nResult; +} + +// return sneak attack damage for a spell +// requires caster, target, and spell damage type +int SpellSneakAttackDamage(object oCaster, object oTarget) +{ + if (GetLocalInt(oCaster, "NoSpellSneak")) + return 0; + + int numDice = GetTotalSneakAttackDice(oCaster); + + if(numDice != 0 && GetCanSneakAttack(oTarget, oCaster) ) + { + FloatingTextStringOnCreature("*Sneak Attack Spell*", oCaster, TRUE); + return GetSneakAttackDamage(numDice); + } + else + { + return 0; + } +} + +//Applies damage from touch attacks, +// returns result of attack roll +// +// object oCaster, the attacker +// object oTarget, the victim +// int iAttackRoll, the result of a touch +// attack roll, 1 for hit, 2 for +// critical hit +// int iDamage, the normal amount of damage done +// int iDamageType, the damage type +// int iDamageType2, the 2nd damage type +// if 2 types of damage are applied +int ApplyTouchAttackDamage(object oCaster, object oTarget, int iAttackRoll, int iDamage, int iDamageType, int iDamageType2 = -1) +{ + iDamage *= iAttackRoll; + if(iDamage) + { + if(!GetPRCSwitch(PRC_SPELL_SNEAK_DISABLE)) + iDamage += SpellSneakAttackDamage(oCaster, oTarget); + + effect eDamage; + if(iDamageType2 == -1) + eDamage = PRCEffectDamage(oTarget, iDamage, iDamageType); + else + { //for touch attacks with 2 damage types, 1st damage type has priority + eDamage = PRCEffectDamage(oTarget, iDamage / 2, iDamageType); + eDamage = EffectLinkEffects(eDamage, EffectDamage(iDamage - (iDamage / 2), iDamageType2)); + } + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget); + } + + return iAttackRoll; +} + +//routes to DoRacialSLA, but checks that the ray hits first +//not sure how this will work if the spell does multiple touch attack, hopefully that shouldnt apply +//this is Base DC, not total DC. SLAs are still spells, so spell focus should still apply. +void DoSpellRay(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0) +{ + int nAttack = PRCDoRangedTouchAttack(PRCGetSpellTargetObject()); + if(nAttack) + { + ActionDoCommand(SetLocalInt(OBJECT_SELF, "AttackHasHit", nAttack)); //preserve crits + if(DEBUG) DoDebug("Spell DC passed to DoSpellRay: " + IntToString(nTotalDC)); + DoRacialSLA(nSpellID, nCasterlevel, nTotalDC, TRUE); + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "AttackHasHit")); + } +} + +//routes to DoRacialSLA, but checks that the rouch hits first +//not sure how this will work if the spell does multiple touch attack, hopefully that shouldnt apply +//this is Base DC, not total DC. SLAs are still spells, so spell focus should still apply. +void DoSpellMeleeTouch(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0) +{ + int nAttack = PRCDoMeleeTouchAttack(PRCGetSpellTargetObject()); + if(nAttack) + { + ActionDoCommand(SetLocalInt(OBJECT_SELF, "AttackHasHit", nAttack)); //preserve crits + DoRacialSLA(nSpellID, nCasterlevel, nTotalDC); + ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "AttackHasHit")); + } +} \ No newline at end of file diff --git a/src/include/prc_inc_spells.nss b/src/include/prc_inc_spells.nss new file mode 100644 index 0000000..19e73d7 --- /dev/null +++ b/src/include/prc_inc_spells.nss @@ -0,0 +1,3236 @@ +/* + ---------------- + prc_inc_spells + ---------------- + + 7/25/04 by WodahsEht + + Contains many useful functions for determining caster level, mostly. The goal + is to consolidate all caster level functions to this -- existing caster level + functions will be wrapped around the main function. + + In the future, all new PrC's that add to caster levels should be added to + the GetArcanePRCLevels and GetDivinePRCLevels functions. Very little else should + be necessary, except when new casting feats are created. +*/ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +//:: Handles psuedo-Foritifcation +void DoFortification(object oPC = OBJECT_SELF, int nFortification = 25); + +/** + * Adjusts the base class level (NOT caster level) of the class by any spellcasting PrCs + * @param nClass a base casting class (divine or arcane) + * @return The level of the class, adjusted for any appropriate PrC levels + */ +int GetPrCAdjustedClassLevel(int nClass, object oCaster = OBJECT_SELF); + +/** + * Adjusts the base caster level of the class by any spellcasting PrCs plus Practised Spellcasting feats if appropriate + * @param nClass a base casting class + * @param bAdjustForPractisedSpellcaster add practiced spellcaster feat to caster level. TRUE by default + * @return the caster level in the class, adjusted by any PrC levels and practised spellcaster feats as appropriate + */ +int GetPrCAdjustedCasterLevel(int nClassType, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE); + +/** + * finds the highest arcane or divine caster level, adjusting the base caster level of the class by any + * spellcasting PrCs plus Practised Spellcasting feats if appropriate + * @param nClassType TYPE_DIVINE or TYPE_ARCANE + * @param bAdjustForPractisedSpellcaster add practiced spellcaster feat to caster level. TRUE by default + * @return the highest arcane/divine caster level adjusted by any PrC levels and practised spellcaster feats as appropriate + */ +int GetPrCAdjustedCasterLevelByType(int nClassType, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE); + +// Returns the best "feat-adjusted" arcane levels of the PC in question. +// Considers feats that situationally adjust caster level. +int GetLevelByTypeArcaneFeats(object oCaster = OBJECT_SELF, int iSpellID = -1); + +// Returns the best "feat-adjusted" divine levels of the PC in question. +// Considers feats that situationally adjust caster level. +int GetLevelByTypeDivineFeats(object oCaster = OBJECT_SELF, int iSpellID = -1); + +//Returns Reflex Adjusted Damage. Is a wrapper function that allows the +//DC to be adjusted based on conditions that cannot be done using iprops +//such as saves vs spellschools, or other adjustments +int PRCGetReflexAdjustedDamage(int nDamage, object oTarget, int nDC, int nSaveType=SAVING_THROW_TYPE_NONE, object oSaveVersus=OBJECT_SELF); + +//Is a wrapper function that allows the DC to be adjusted based on conditions +//that cannot be done using iprops, such as saves vs spellschool. +//If bImmunityCheck == FALSE: returns 0 if failure or immune, 1 if success - same as MySavingThrow +//If bImmunityCheck == TRUE: returns 0 if failure, 1 if success, 2 if immune +int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType=SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0, int bImmunityCheck = FALSE); + +// Finds caster levels by specific types (see the constants below). +int GetCasterLvl(int iTypeSpell, object oCaster = OBJECT_SELF); + +// Calculates bonus damage to a spell for Spell Betrayal Ability +int SpellBetrayalDamage(object oTarget, object oCaster); + +// Calculates damage to a spell for Spellstrike Ability +int SpellStrikeDamage(object oTarget, object oCaster); + +// Create a Damage effect +// - oTarget: spell target +// - nDamageAmount: amount of damage to be dealt. This should be applied as an +// instantaneous effect. +// - nDamageType: DAMAGE_TYPE_* +// - nDamagePower: DAMAGE_POWER_* +// Used to add Warmage's Edge to spells. +effect PRCEffectDamage(object oTarget, int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL, int nMetaMagic=METAMAGIC_NONE); + +/** + * Adds damage to all arcane spells based on the number of dice + */ +int SpellDamagePerDice(object oCaster, int nDice); + +int IsSpellDamageElemental(int nDamageType); + +// Get altered damage type for energy sub feats. +// nDamageType - The DAMAGE_TYPE_xxx constant of the damage. All types other +// than elemental damage types are ignored. +// oCaster - caster object. +// moved from spinc_common, possibly somewhat redundant +int PRCGetElementalDamageType(int nDamageType, object oCaster = OBJECT_SELF); + +// Adds the bonus damage from both Spell Betrayal and Spellstrike together +int ApplySpellBetrayalStrikeDamage(object oTarget, object oCaster, int bShowTextString = TRUE); + +// wrapper for DecrementRemainingSpellUses, works for newspellbook 'fake' spells too +void PRCDecrementRemainingSpellUses(object oCreature, int nSpell); + +/** + * Determines and applies the alignment shift for using spells/powers with the + * [Evil] descriptor. The amount of adjustment is equal to the square root of + * the caster's distance from pure evil. + * In other words, the amount of shift is higher the farther the caster is from + * pure evil, with the extremes being 10 points of shift at pure good and 0 + * points of shift at pure evil. + * + * Does nothing if the PRC_SPELL_ALIGNMENT_SHIFT switch is not set. + * + * @param oPC The caster whose alignment to adjust + */ +//void SPEvilShift(object oPC); + +/** + * Determines and applies the alignment shift for using spells/powers with the + * [Good] descriptor. The amount of adjustment is equal to the square root of + * the caster's distance from pure good. + * In other words, the amount of shift is higher the farther the caster is from + * pure good, with the extremes being 10 points of shift at pure evil and 0 + * points of shift at pure good. + * + * Does nothing if the PRC_SPELL_ALIGNMENT_SHIFT switch is not set. + * + * @param oPC The caster whose alignment to adjust + */ +//void SPGoodShift(object oPC); + +/** + * Applies the corruption cost for Corrupt spells. + * + * @param oPC The caster of the Corrupt spell + * @param oTarget The target of the spell. + * Not used for anything, should probably remove - Ornedan + * @param nAbility ABILITY_* of the ability to apply the cost to + * @param nCost The amount of stat damage or drain to apply + * @param bDrain If this is TRUE, the cost is applied as ability drain. + * If FALSE, as ability damage. + */ +void DoCorruptionCost(object oPC, int nAbility, int nCost, int bDrain); + +// This function is used in the spellscripts +// It functions as Evasion for Fortitude and Will partial saves +// This means the "partial" section is ignored +// nSavingThrow takes either SAVING_THROW_WILL or SAVING_THROW_FORT +int GetHasMettle(object oTarget, int nSavingThrow = SAVING_THROW_WILL); + +// Applies all of the effects needed to set a creature incorporeal +// You need to set the creature incorporeal in the feat/spell/effect itself if using nPermanent = TRUE +// nSuperOrEx: 0 is normal, 1 is Supernatural, 2 is Extraordinary +void SetIncorporeal(object oTarget, float fDuration, int nSuperOrEx, int nPermanent = FALSE); + +// Test for incorporeallity of the target +// useful for targetting loops when incorporeal creatures +// wouldnt be affected +int GetIsIncorporeal(object oTarget); + +/** Tests if a creature is living. Should be called on creatures. + * Dead and not-alive creatures return FALSE + * Returns FALSE for non-creature objects. + */ +int PRCGetIsAliveCreature(object oTarget); + +// Gets the total number of HD of controlled undead +// i.e from Animate Dead, Ghoul Gauntlet or similar +// Dominated undead from Turn Undead do not count +int GetControlledUndeadTotalHD(object oPC = OBJECT_SELF); + +// Gets the total number of HD of controlled evil outsiders +// i.e from call dretch, call lemure, or similar +// Dominated outsiders from Turn Undead etc do not count +int GetControlledFiendTotalHD(object oPC = OBJECT_SELF); + +// Gets the total number of HD of controlled good outsiders +// i.e from call favoured servants +// Dominated outsiders from Turn Undead etc do not count +int GetControlledCelestialTotalHD(object oPC = OBJECT_SELF); + +/** + * Multisummon code, to be run before the summoning effect is applied. + * Normally, this will only perform the multisummon trick of setting + * pre-existing summons indestructable if PRC_MULTISUMMON is set. + * + * @param oPC The creature casting the summoning spell + * @param bOverride If this is set, ignores the value of PRC_MULTISUMMON switch + */ +void MultisummonPreSummon(object oPC = OBJECT_SELF, int bOverride = FALSE); + +/** + * Sets up all of the AoE's variables, but only if they aren't already set. + * + * This sets many things that would have been checked against GetAreaOfEffectCreator() + * as local ints making it so the AoE can now function entirely independantly of its caster. + * - The only problem is that this will never be called until the AoE does a heartbeat or + * something. + * + * Since some functions (ie PRCGetLastSpellcastClass() can not work outside of the spell script + * sometimes SetAllAoEInts() was storing incorrect vaules for 'new spellbook' classes. + * I modified the function and moved it to main spell script, so it should work correctly now. - x + * + * @param SpellID Spell ID to store on the AoE. + * @param oAoE AoE object to store the variables on + * @param nBaseSaveDC save DC to store on the AoE + * @param SpecDispel Stored on the AoE (dunno what it's for) + * @param nCasterLevel Caster level to store on the AoE. If default used, gets + * caster level from the AoE creator. + */ +void SetAllAoEInts(int SpellID, object oAoE, int nBaseSaveDC, int SpecDispel = 0 , int nCasterLevel = 0); + +// * Applies the effects of FEAT_AUGMENT_SUMMON to summoned creatures. +void AugmentSummonedCreature(string sResRef); + +// ----------------- +// BEGIN SPELLSWORD +// ----------------- + +//This function returns 1 only if the object oTarget is the object +//the weapon hit when it channeled the spell sSpell or if there is no +//channeling at all +int ChannelChecker(string sSpell, object oTarget); + +//If a spell is being channeled, we store its target and its name +void StoreSpellVariables(string sString,int nDuration); + +//Replacement for The MaximizeOrEmpower function +int PRCMaximizeOrEmpower(int nDice, int nNumberOfDice, int nMeta, int nBonus = 0); + +//This checks if the spell is channeled and if there are multiple spells +//channeled, which one is it. Then it checks in either case if the spell +//has the metamagic feat the function gets and returns TRUE or FALSE accordingly +//int CheckMetaMagic(int nMeta,int nMMagic); +//not needed now there is PRCGetMetaMagicFeat() + +//wrapper for biowares GetMetaMagicFeat() +//used for spellsword and items +// bClearFeatFlags - clear metamagic feat info (sudden meta, divine meta etc.)- set it to FALSE if you're +// going to use PRCGetMetaMagicFeat() more than once for a single spell +// (ie. first in spellhook code, next in spell script) +int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE); + +// This function rolls damage and applies metamagic feats to the damage. +// nDamageType - The DAMAGE_TYPE_xxx constant for the damage, or -1 for no +// a non-damaging effect. +// nDice - number of dice to roll. +// nDieSize - size of dice, i.e. d4, d6, d8, etc. +// nBonusPerDie - Amount of bonus damage per die. +// nBonus - Amount of overall bonus damage. +// nMetaMagic - metamagic constant, if -1 GetMetaMagic() is called. +// returns - the damage rolled with metamagic applied. +int PRCGetMetaMagicDamage(int nDamageType, int nDice, int nDieSize, + int nBonusPerDie = 0, int nBonus = 0, int nMetaMagic = -1); + +// Function to save the school of the currently cast spell in a variable. This should be +// called at the beginning of the script to set the spell school (passing the school) and +// once at the end of the script (with no arguments) to delete the variable. +// nSchool - SPELL_SCHOOL_xxx constant to set, if general then the variable is +// deleted. +// moved from spinc_common and renamed +void PRCSetSchool(int nSchool = SPELL_SCHOOL_GENERAL); + +/** + * Signals a spell has been cast. Acts as a wrapper to fire EVENT_SPELL_CAST_AT + * via SignalEvent() + * @param oTarget Target of the spell. + * @param bHostile TRUE if the spell is a hostile act. + * @param nSpellID Spell ID or -1 if PRCGetSpellId() should be used. + * @param oCaster Object doing the casting. + */ +void PRCSignalSpellEvent(object oTarget, int bHostile = TRUE, int nSpellID = -1, object oCaster = OBJECT_SELF); + +//GetFirstObjectInShape wrapper for changing the AOE of the channeled spells (Spellsword Channel Spell) +object MyFirstObjectInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0]); + +//GetNextObjectInShape wrapper for changing the AOE of the channeled spells (Spellsword Channel Spell) +object MyNextObjectInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0]); + +// * Kovi. removes any effects from this type of spell +// * i.e., used in Mage Armor to remove any previous +// * mage armors +void PRCRemoveEffectsFromSpell(object oTarget, int SpellID); + +// * Get Scaled Effect +effect PRCGetScaledEffect(effect eStandard, object oTarget); + +// * Searchs through a persons effects and removes all those of a specific type. +void PRCRemoveSpecificEffect(int nEffectTypeID, object oTarget); + +// * Returns true if Target is a humanoid +int PRCAmIAHumanoid(object oTarget); + +// * Get Difficulty Duration +int PRCGetScaledDuration(int nActualDuration, object oTarget); + +// * Will pass back a linked effect for all the protection from alignment spells. The power represents the multiplier of strength. +// * That is instead of +3 AC and +2 Saves a power of 2 will yield +6 AC and +4 Saves. +effect PRCCreateProtectionFromAlignmentLink(int nAlignment, int nPower = 1); + +// * Returns the time in seconds that the effect should be delayed before application. +float PRCGetSpellEffectDelay(location SpellTargetLocation, object oTarget); + +// * This is a wrapper for how Petrify will work in Expansion Pack 1 +// * Scripts affected: flesh to stone, breath petrification, gaze petrification, touch petrification +// * nPower : This is the Hit Dice of a Monster using Gaze, Breath or Touch OR it is the Caster Spell of +// * a spellcaster +// * nFortSaveDC: pass in this number from the spell script +void PRCDoPetrification(int nPower, object oSource, object oTarget, int nSpellID, int nFortSaveDC); + +int PRCGetDelayedSpellEffectsExpired(int nSpell_ID, object oTarget, object oCaster); + +int PRCGetSpellUsesLeft(int nRealSpellID, object oCreature = OBJECT_SELF); +// ----------------- +// END SPELLSWORD +// ----------------- + +// Functions mostly only useful within the scope of this include +int BWSavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0); + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int X2PreSpellCastCode(); + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// this could also be put into in prc_inc_switches +const string PRC_SAVEDC_OVERRIDE = "PRC_SAVEDC_OVERRIDE"; + +const int TYPE_ARCANE = -1; +const int TYPE_DIVINE = -2; + + +//Changed to use CLASS_TYPE_* instead +//const int TYPE_SORCERER = 2; +//const int TYPE_WIZARD = 3; +//const int TYPE_BARD = 4; +//const int TYPE_CLERIC = 11; +//const int TYPE_DRUID = 12; +//const int TYPE_RANGER = 13; +//const int TYPE_PALADIN = 14; + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "inc_abil_damage" +//#include "prc_inc_castlvl" +//#include "lookup_2da_spell" + +// ** THIS ORDER IS IMPORTANT ** + +//#include "inc_lookups" // access via prc_inc_core +#include "inc_newspellbook" + +//#include "prc_inc_core" // Compiler won't allow access via inc_newspellbook + +#include "inc_vfx_const" +#include "spinc_necro_cyst" +#include "true_utter_const" +//#include "shd_myst_const" +//#include "prc_spellhook" +#include "prc_inc_sneak" +#include "prcsp_engine" +//#include "prc_inc_descrptr" +#include "inc_item_props" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetPrCAdjustedClassLevel(int nClass, object oCaster = OBJECT_SELF) +{ + int iTemp; + // is it arcane, divine or neither? + if(GetIsArcaneClass(nClass, oCaster) && nClass != CLASS_TYPE_SUBLIME_CHORD) + { + if (GetPrimaryArcaneClass(oCaster) == nClass) // adjust for any PrCs + iTemp = GetArcanePRCLevels(oCaster, nClass); + } + else if(GetIsDivineClass(nClass, oCaster)) + { + if (GetPrimaryDivineClass(oCaster) == nClass) // adjust for any PrCs + iTemp = GetDivinePRCLevels(oCaster, nClass); + } + else // a non-caster class or a PrC + { + return 0; + } + // add the caster class levels + return iTemp += GetLevelByClass(nClass, oCaster); +} + +int GetPrCAdjustedCasterLevel(int nClass, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE) +{ + int iTemp; + iTemp = GetPrCAdjustedClassLevel(nClass, oCaster); + iTemp = iTemp / GetCasterLevelModifier(nClass); + if (bAdjustForPractisedSpellcaster) + iTemp += PracticedSpellcasting(oCaster, nClass, iTemp); + return iTemp; +} + +int GetPrCAdjustedCasterLevelByType(int nClassType, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE) +{ + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCaster); + nClass2 = GetClassByPosition(2, oCaster); + nClass3 = GetClassByPosition(3, oCaster); + nClass4 = GetClassByPosition(4, oCaster); + nClass5 = GetClassByPosition(5, oCaster); + nClass6 = GetClassByPosition(6, oCaster); + nClass7 = GetClassByPosition(7, oCaster); + nClass8 = GetClassByPosition(8, oCaster); + + if(nClassType == TYPE_ARCANE && (GetFirstArcaneClassPosition(oCaster) > 0)) + { + if (GetIsArcaneClass(nClass1, oCaster)) nClass1Lvl = GetPrCAdjustedCasterLevel(nClass1, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass2, oCaster)) nClass2Lvl = GetPrCAdjustedCasterLevel(nClass2, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass3, oCaster)) nClass3Lvl = GetPrCAdjustedCasterLevel(nClass3, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass4, oCaster)) nClass4Lvl = GetPrCAdjustedCasterLevel(nClass4, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass5, oCaster)) nClass5Lvl = GetPrCAdjustedCasterLevel(nClass5, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass6, oCaster)) nClass6Lvl = GetPrCAdjustedCasterLevel(nClass6, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass7, oCaster)) nClass7Lvl = GetPrCAdjustedCasterLevel(nClass7, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsArcaneClass(nClass8, oCaster)) nClass8Lvl = GetPrCAdjustedCasterLevel(nClass8, oCaster, bAdjustForPractisedSpellcaster); + } + else if (nClassType == TYPE_DIVINE && (GetFirstDivineClassPosition(oCaster) > 0)) + { + if (GetIsDivineClass(nClass1, oCaster)) nClass1Lvl = GetPrCAdjustedCasterLevel(nClass1, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass2, oCaster)) nClass2Lvl = GetPrCAdjustedCasterLevel(nClass2, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass3, oCaster)) nClass3Lvl = GetPrCAdjustedCasterLevel(nClass3, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass4, oCaster)) nClass4Lvl = GetPrCAdjustedCasterLevel(nClass4, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass5, oCaster)) nClass5Lvl = GetPrCAdjustedCasterLevel(nClass5, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass6, oCaster)) nClass6Lvl = GetPrCAdjustedCasterLevel(nClass6, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass7, oCaster)) nClass7Lvl = GetPrCAdjustedCasterLevel(nClass7, oCaster, bAdjustForPractisedSpellcaster); + if (GetIsDivineClass(nClass8, oCaster)) nClass8Lvl = GetPrCAdjustedCasterLevel(nClass8, oCaster, bAdjustForPractisedSpellcaster); + } + int nHighest = nClass1Lvl; + if (nClass2Lvl > nHighest) nHighest = nClass2Lvl; + if (nClass3Lvl > nHighest) nHighest = nClass3Lvl; + if (nClass4Lvl > nHighest) nHighest = nClass4Lvl; + if (nClass5Lvl > nHighest) nHighest = nClass5Lvl; + if (nClass6Lvl > nHighest) nHighest = nClass6Lvl; + if (nClass7Lvl > nHighest) nHighest = nClass7Lvl; + if (nClass8Lvl > nHighest) nHighest = nClass8Lvl; + return nHighest; +} + +int GetLevelByTypeArcaneFeats(object oCaster = OBJECT_SELF, int iSpellID = -1) +{ + int iFirstArcane = GetPrimaryArcaneClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iSpellID = -1) iSpellID = PRCGetSpellId(oCaster); + + int iBoost = ShadowWeave(oCaster, iSpellID) + + FireAdept(oCaster, iSpellID) + + DomainPower(oCaster, iSpellID) + + StormMagic(oCaster) + + CormanthyranMoonMagic(oCaster) + + DraconicPower(oCaster); + + if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0; + if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0; + if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0; + if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0; + if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0; + if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0; + if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0; + if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0; + + if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster, iClass1); + if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster, iClass2); + if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster, iClass3); + if (iClass4 == iFirstArcane) iClass4Lev += GetArcanePRCLevels(oCaster, iClass4); + if (iClass5 == iFirstArcane) iClass5Lev += GetArcanePRCLevels(oCaster, iClass5); + if (iClass6 == iFirstArcane) iClass6Lev += GetArcanePRCLevels(oCaster, iClass6); + if (iClass7 == iFirstArcane) iClass7Lev += GetArcanePRCLevels(oCaster, iClass7); + if (iClass8 == iFirstArcane) iClass8Lev += GetArcanePRCLevels(oCaster, iClass8); + + iClass1Lev += iBoost; + iClass2Lev += iBoost; + iClass3Lev += iBoost; + iClass4Lev += iBoost; + iClass5Lev += iBoost; + iClass6Lev += iBoost; + iClass7Lev += iBoost; + iClass8Lev += iBoost; + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass7Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass8Lev); + + if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +int GetLevelByTypeDivineFeats(object oCaster = OBJECT_SELF, int iSpellID = -1) +{ + int iFirstDivine = GetPrimaryDivineClass(oCaster); + int iBest = 0; + int iClass1 = GetClassByPosition(1, oCaster); + int iClass2 = GetClassByPosition(2, oCaster); + int iClass3 = GetClassByPosition(3, oCaster); + int iClass4 = GetClassByPosition(4, oCaster); + int iClass5 = GetClassByPosition(5, oCaster); + int iClass6 = GetClassByPosition(6, oCaster); + int iClass7 = GetClassByPosition(7, oCaster); + int iClass8 = GetClassByPosition(8, oCaster); + + int iClass1Lev = GetLevelByPosition(1, oCaster); + int iClass2Lev = GetLevelByPosition(2, oCaster); + int iClass3Lev = GetLevelByPosition(3, oCaster); + int iClass4Lev = GetLevelByPosition(4, oCaster); + int iClass5Lev = GetLevelByPosition(5, oCaster); + int iClass6Lev = GetLevelByPosition(6, oCaster); + int iClass7Lev = GetLevelByPosition(7, oCaster); + int iClass8Lev = GetLevelByPosition(8, oCaster); + + if (iSpellID = -1) iSpellID = PRCGetSpellId(oCaster); + + int iBoost = ShadowWeave(oCaster, iSpellID) + + FireAdept(oCaster, iSpellID) + + DomainPower(oCaster, iSpellID) + + CormanthyranMoonMagic(oCaster) + + StormMagic(oCaster); + + if (iClass1 == CLASS_TYPE_PALADIN + || iClass1 == CLASS_TYPE_RANGER + || iClass1 == CLASS_TYPE_ANTI_PALADIN) + iClass1Lev = iClass1Lev / 2; + if (iClass2 == CLASS_TYPE_PALADIN + || iClass2 == CLASS_TYPE_RANGER + || iClass2 == CLASS_TYPE_ANTI_PALADIN) + iClass2Lev = iClass2Lev / 2; + if (iClass3 == CLASS_TYPE_PALADIN + || iClass3 == CLASS_TYPE_RANGER + || iClass3 == CLASS_TYPE_ANTI_PALADIN) + iClass3Lev = iClass3Lev / 2; + if (iClass4 == CLASS_TYPE_PALADIN + || iClass4 == CLASS_TYPE_RANGER + || iClass4 == CLASS_TYPE_ANTI_PALADIN) + iClass4Lev = iClass4Lev / 2; + if (iClass5 == CLASS_TYPE_PALADIN + || iClass5 == CLASS_TYPE_RANGER + || iClass5 == CLASS_TYPE_ANTI_PALADIN) + iClass5Lev = iClass5Lev / 2; + if (iClass6 == CLASS_TYPE_PALADIN + || iClass6 == CLASS_TYPE_RANGER + || iClass6 == CLASS_TYPE_ANTI_PALADIN) + iClass6Lev = iClass6Lev / 2; + if (iClass7 == CLASS_TYPE_PALADIN + || iClass7 == CLASS_TYPE_RANGER + || iClass7 == CLASS_TYPE_ANTI_PALADIN) + iClass7Lev = iClass7Lev / 2; + if (iClass8 == CLASS_TYPE_PALADIN + || iClass8 == CLASS_TYPE_RANGER + || iClass8 == CLASS_TYPE_ANTI_PALADIN) + iClass8Lev = iClass7Lev / 2; + + if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster, iClass1); + if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster, iClass2); + if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster, iClass3); + if (iClass4 == iFirstDivine) iClass4Lev += GetDivinePRCLevels(oCaster, iClass4); + if (iClass5 == iFirstDivine) iClass5Lev += GetDivinePRCLevels(oCaster, iClass5); + if (iClass6 == iFirstDivine) iClass6Lev += GetDivinePRCLevels(oCaster, iClass6); + if (iClass7 == iFirstDivine) iClass7Lev += GetDivinePRCLevels(oCaster, iClass7); + if (iClass8 == iFirstDivine) iClass8Lev += GetDivinePRCLevels(oCaster, iClass8); + + iClass1Lev += iBoost; + iClass2Lev += iBoost; + iClass3Lev += iBoost; + iClass4Lev += iBoost; + iClass5Lev += iBoost; + iClass6Lev += iBoost; + iClass7Lev += iBoost; + iClass8Lev += iBoost; + + iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev); + iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev); + iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev); + iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev); + iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev); + iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev); + iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass7Lev); + iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass8Lev); + + if (!GetIsDivineClass(iClass1, oCaster)) iClass1Lev = 0; + if (!GetIsDivineClass(iClass2, oCaster)) iClass2Lev = 0; + if (!GetIsDivineClass(iClass3, oCaster)) iClass3Lev = 0; + if (!GetIsDivineClass(iClass4, oCaster)) iClass4Lev = 0; + if (!GetIsDivineClass(iClass5, oCaster)) iClass5Lev = 0; + if (!GetIsDivineClass(iClass6, oCaster)) iClass6Lev = 0; + if (!GetIsDivineClass(iClass7, oCaster)) iClass7Lev = 0; + if (!GetIsDivineClass(iClass8, oCaster)) iClass8Lev = 0; + + if (iClass1Lev > iBest) iBest = iClass1Lev; + if (iClass2Lev > iBest) iBest = iClass2Lev; + if (iClass3Lev > iBest) iBest = iClass3Lev; + if (iClass4Lev > iBest) iBest = iClass4Lev; + if (iClass5Lev > iBest) iBest = iClass5Lev; + if (iClass6Lev > iBest) iBest = iClass6Lev; + if (iClass7Lev > iBest) iBest = iClass7Lev; + if (iClass8Lev > iBest) iBest = iClass8Lev; + + return iBest; +} + +// looks up the spell level for the arcane caster classes (Wiz_Sorc, Bard) in spells.2da. +// Caveat: some onhitcast spells don't have any spell-levels listed for any class +int GetIsArcaneSpell (int iSpellId) +{ + return Get2DACache("spells", "Wiz_Sorc", iSpellId) != "" + || Get2DACache("spells", "Bard", iSpellId) != ""; +} + +// looks up the spell level for the divine caster classes (Cleric, Druid, Ranger, Paladin) in spells.2da. +// Caveat: some onhitcast spells don't have any spell-levels listed for any class +int GetIsDivineSpell (int iSpellId) +{ + return Get2DACache("spells", "Cleric", iSpellId) != "" + || Get2DACache("spells", "Druid", iSpellId) != "" + || Get2DACache("spells", "Ranger", iSpellId) != "" + || Get2DACache("spells", "Paladin", iSpellId) != ""; +} + +int BWSavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0) +{ + // GZ: sanity checks to prevent wrapping around + if (nDC < 1) nDC = 1; + else if (nDC > 255) nDC = 255; + + int bValid = 0; + int nEff; + + //some maneuvers allow people to use skill check instead of a save + if(nSavingThrow == SAVING_THROW_FORT) + { + bValid = GetLocalInt(oTarget, "MindOverBody") ? + GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) : + FortitudeSave(oTarget, nDC, nSaveType, oSaveVersus); + if(bValid == 1) nEff = VFX_IMP_FORTITUDE_SAVING_THROW_USE; + } + else if(nSavingThrow == SAVING_THROW_REFLEX) + { + bValid = GetLocalInt(oTarget, "ActionBeforeThought") ? + GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) : + ReflexSave(oTarget, nDC, nSaveType, oSaveVersus); + if(bValid == 1) nEff = VFX_IMP_REFLEX_SAVE_THROW_USE; + } + else if(nSavingThrow == SAVING_THROW_WILL) + { + bValid = GetLocalInt(oTarget, "MomentOfPerfectMind") ? + GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) : + WillSave(oTarget, nDC, nSaveType, oSaveVersus); + if(bValid == 1) nEff = VFX_IMP_WILL_SAVING_THROW_USE; + } + + /* + return 0 = FAILED SAVE + return 1 = SAVE SUCCESSFUL + return 2 = IMMUNE TO WHAT WAS BEING SAVED AGAINST + */ + if(bValid == 0) + { + int nSpellID = PRCGetSpellId(oSaveVersus); + if(nSaveType == SAVING_THROW_TYPE_DEATH + || nSpellID == SPELL_WEIRD + || nSpellID == SPELL_FINGER_OF_DEATH) + //&& nSpellID != SPELL_HORRID_WILTING) + { + if(fDelay > 0.0f) DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oTarget)); + else ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oTarget); + } + } + else //if(bValid == 1 || bValid == 2) + { + if(bValid == 2) nEff = VFX_IMP_MAGIC_RESISTANCE_USE; + + if(fDelay > 0.0f) DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nEff), oTarget)); + else ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nEff), oTarget); + } + return bValid; +} + +void PRCBonusDamage(object oTarget, object oCaster = OBJECT_SELF) +{ + // Does nothing, left here so files don't need to be edited. +} + +// Bonus damage to a spell for Spell Betrayal Ability +int SpellBetrayalDamage(object oTarget, object oCaster) +{ + int iDam = 0; + + // Combine caster and spell ID into a unique key + int nSpellId = PRCGetSpellId(); + string sFlag = "BETRAYAL_" + ObjectToString(oCaster) + "_" + IntToString(nSpellId); + + // Only apply once per spell cast from this caster + if (GetLocalInt(oTarget, sFlag)) + return 0; + + int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster); + + if (ThrallLevel >= 2) + { + if (GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE)) + { + ThrallLevel /= 2; + iDam = d6(ThrallLevel); + + // Mark target as affected for this spell instance by this caster + SetLocalInt(oTarget, sFlag, TRUE); + DelayCommand(2.5, DeleteLocalInt(oTarget, sFlag)); + } + } + + return iDam; +} + + +/* int SpellBetrayalDamage(object oTarget, object oCaster) +{ + int iDam = 0; + int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster); + + if(ThrallLevel >= 2) + { + if( GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE) ) + { + ThrallLevel /= 2; + iDam = d6(ThrallLevel); + } + } + + return iDam; +} */ + +// Bonus damage to a spell for Spellstrike Ability +int SpellStrikeDamage(object oTarget, object oCaster) +{ + int iDam = 0; + int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster); + + // Combine caster and spell ID into a unique key + int nSpellId = PRCGetSpellId(); + string sFlag = "SPELL_STRIKE_" + ObjectToString(oCaster) + "_" + IntToString(nSpellId); + + if(ThrallLevel >= 6) + { + if( GetIsAOEFlanked(oTarget, oCaster) ) + { + ThrallLevel /= 4; + iDam = d6(ThrallLevel); + // Mark target as affected for this spell instance by this caster + SetLocalInt(oTarget, sFlag, TRUE); + DelayCommand(2.5, DeleteLocalInt(oTarget, sFlag)); + } + } + + return iDam; +} + +// Adds the bonus damage from both Spell Betrayal and Spellstrike together +int ApplySpellBetrayalStrikeDamage(object oTarget, object oCaster, int bShowTextString = TRUE) +{ + int iDam = 0; + int iBetrayalDam = SpellBetrayalDamage(oTarget, oCaster); + int iStrikeDam = SpellStrikeDamage(oTarget, oCaster); + string sMes = ""; + + if(iStrikeDam > 0 && iBetrayalDam > 0) sMes ="*Spellstrike Betrayal Sucess*"; + else if(iBetrayalDam > 0) sMes ="*Spell Betrayal Sucess*"; + else if(iStrikeDam > 0) sMes ="*Spellstrike Sucess*"; + + if(bShowTextString) FloatingTextStringOnCreature(sMes, oCaster, TRUE); + + iDam = iBetrayalDam + iStrikeDam; + + // debug code + //sMes = "Spell Betrayal / Spellstrike Bonus Damage: " + IntToString(iBetrayalDam) + " + " + IntToString(iStrikeDam) + " = " + IntToString(iDam); + //DelayCommand( 1.0, FloatingTextStringOnCreature(sMes, oCaster, TRUE) ); + + return iDam; +} + +int SpellDamagePerDice(object oCaster, int nDice) +{ + // Arcane only + if (!GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster))) + return 0; + + int nDam = 0; + nDam += GetLocalInt(oCaster, "StrengthFromMagic") * nDice * 2; + + if (GetLocalInt(oCaster, "WarsoulTyrant")) + nDam += nDice * GetHitDice(oCaster); + + if (DEBUG) DoDebug("SpellDamagePerDice returning "+IntToString(nDam)+" for "+GetName(oCaster)); + + return nDam; +} + +int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0, int bImmunityCheck = FALSE) +{ + int nSpell = PRCGetSpellId(oSaveVersus); + int nSpellSchool = GetSpellSchool(nSpell); + + //If bImmunityCheck == FALSE: returns 0 if failure or immune, 1 if success - same as MySavingThrow + //If bImmunityCheck == TRUE: returns 0 if failure, 1 if success, 2 if immune + + // Enigma Helm Crown Bind + if (GetIsMeldBound(oTarget, MELD_ENIGMA_HELM) == CHAKRA_CROWN && GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM)) + return 2; + + // Planar Ward + if (GetHasSpellEffect(MELD_PLANAR_WARD, oTarget) && (GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM) || GetIsOfSubschool(nSpell, SUBSCHOOL_COMPULSION))) + return 2; + + // Chucoclops influence makes you autofail fear saves + if (GetHasSpellEffect(VESTIGE_CHUPOCLOPS, oTarget) && !GetLocalInt(oTarget, "PactQuality"+IntToString(VESTIGE_CHUPOCLOPS)) && nSaveType == SAVING_THROW_TYPE_FEAR) + return 1; + + // Iron Mind's Mind Over Body, allows them to treat other saves as will saves up to 3/day. + // No point in having it trigger when its a will save. + if(nSavingThrow != SAVING_THROW_WILL && GetLocalInt(oTarget, "IronMind_MindOverBody")) + { + nSavingThrow = SAVING_THROW_WILL; + DeleteLocalInt(oTarget, "IronMind_MindOverBody"); + } + + // Handle the target having Force of Will and being targeted by a psionic power + if(nSavingThrow != SAVING_THROW_WILL && + ((nSpell > 14000 && nSpell < 14360) || + (nSpell > 15350 && nSpell < 15470) + ) && + GetHasFeat(FEAT_FORCE_OF_WILL, oTarget) && + !GetLocalInt(oTarget, "ForceOfWillUsed") && + // Only use will save if it's better + ((nSavingThrow == SAVING_THROW_FORT ? GetFortitudeSavingThrow(oTarget) : GetReflexSavingThrow(oTarget)) > GetWillSavingThrow(oTarget)) + ) + { + nSavingThrow = SAVING_THROW_WILL; + SetLocalInt(oTarget, "ForceOfWillUsed", TRUE); + DelayCommand(6.0f, DeleteLocalInt(oTarget, "ForceOfWillUsed")); + // "Force of Will used for this round." + FloatingTextStrRefOnCreature(16826670, oTarget, FALSE); + } + + //TouchOfDistraction + if (GetLocalInt(oTarget, "HasTouchOfDistraction") && (nSavingThrow = SAVING_THROW_REFLEX)) + { + nDC += 2; + DeleteLocalInt(oTarget, "HasTouchOfDistraction"); + } + + //Diamond Defense + if(GetLocalInt(oTarget, "PRC_TOB_DIAMOND_DEFENSE")) + nDC -= GetLocalInt(oTarget, "PRC_TOB_DIAMOND_DEFENSE"); + + // Master of Nine + if(GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oSaveVersus) > 2) + { + int nLastClass = GetLocalInt(oSaveVersus, "PRC_CurrentManeuver_InitiatingClass") - 1; + if(nLastClass == CLASS_TYPE_WARBLADE + || nLastClass == CLASS_TYPE_SWORDSAGE + || nLastClass == CLASS_TYPE_CRUSADER) + { + // Increases maneuver DCs by 1 + nDC += 1; + } + } + + // Incarnum Resistance + if(GetHasFeat(FEAT_INCARNUM_RESISTANCE, oTarget) && nSpell > 18900 && nSpell < 18969) + nDC -= 2; + + // This is done here because it is possible to tell the saving throw type here + // it works by lowering the DC rather than adding to the save + // same net effect but slightly different numbers + if(nSaveType == SAVING_THROW_TYPE_MIND_SPELLS) + { + //Thayan Knights auto-fail mind spells cast by red wizards + if(GetLevelByClass(CLASS_TYPE_RED_WIZARD, oSaveVersus) + && GetLevelByClass(CLASS_TYPE_THAYAN_KNIGHT, oTarget)) + return 0; + // Tyranny Domain increases the DC of mind spells by +2. + if(GetHasFeat(FEAT_DOMAIN_POWER_TYRANNY, oSaveVersus)) + nDC += 2; + // +2 bonus on saves against mind affecting, done here + if(GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oTarget) > 1) + nDC -= 2; + // Scorpion's Resolve gives a +4 bonus on mind affecting saves + if(GetHasFeat(FEAT_SCORPIONS_RESOLVE, oTarget)) + nDC -= 4; + // Warped Mind gives a bonus on mind affecting equal to half aberrant + if(GetHasFeat(FEAT_ABERRANT_WARPED_MIND, oTarget)) + nDC -= GetAberrantFeatCount(oTarget)/2; + } + else if(nSaveType == SAVING_THROW_TYPE_FEAR) + { + // Unnatural Will adds Charisma to saves against fear + if(GetHasFeat(FEAT_UNNATURAL_WILL, oTarget)) + nDC -= GetAbilityModifier(ABILITY_CHARISMA, oTarget); + // Krinth have +4 saves against Fear + if(GetRacialType(oTarget) == RACIAL_TYPE_KRINTH) + nDC -= 4; + // Turlemoi/Lashemoi have -4 saves against Fear + if(GetRacialType(oTarget) == RACIAL_TYPE_LASHEMOI || GetRacialType(oTarget) == RACIAL_TYPE_TURLEMOI || GetLocalInt(oTarget, "SpeedFromPain") >= 3) + nDC += 4; + } + else if(nSaveType == SAVING_THROW_TYPE_NEGATIVE) + { + // +4 bonus on saves against negative energy, done here + if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 8) + nDC -= 4; + // Jergal's Pact gives a +2 bonus on negative energy saves + if(GetHasFeat(FEAT_JERGALS_PACT, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_DISEASE) + { + // Plague Resistant gives a +4 bonus on disease saves + if(GetHasFeat(FEAT_PLAGUE_RESISTANT, oTarget)) + nDC -= 4; + // +4/+2 bonus on saves against disease, done here + if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13) + nDC -= 4; + else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_POISON) + { + // +4/+2 bonus on saves against poison, done here + if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13) + nDC -= 4; + else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3) + nDC -= 2; + if(GetHasFeat(FEAT_POISON_4, oTarget)) + nDC -= 4; + if(GetHasFeat(FEAT_POISON_3, oTarget)) + nDC -= 3; + if(GetRacialType(oTarget) == RACIAL_TYPE_EXTAMINAAR && nSavingThrow == SAVING_THROW_FORT) + nDC -= 2; + if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK) + nDC -= 1; + } + else if(nSaveType == SAVING_THROW_TYPE_FIRE) + { + // Bloodline of Fire gives a +4 bonus on fire saves + if(GetHasFeat(FEAT_BLOODLINE_OF_FIRE, oTarget)) + nDC -= 4; + if(GetHasFeat(FEAT_HARD_FIRE, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + // +2 vs fire for Halfgiant + if(GetHasFeat(FEAT_ACCLIMATED_FIRE, oTarget)) + nDC -= 2; + // +2 vs fire for Heat Endurance feat + if(GetHasFeat(FEAT_HEAT_ENDURANCE, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_COLD) + { + if(GetHasFeat(FEAT_HARD_WATER, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + // +2 vs cold for Cold Endurance feat + if(GetHasFeat(FEAT_COLD_ENDURANCE, oTarget)) + nDC -= 2; + // +2 vs cold for Winterhide Shifter trait + if(GetHasFeat(FEAT_SHIFTER_WINTERHIDE, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_ELECTRICITY) + { + if(GetHasFeat(FEAT_HARD_AIR, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + else if(GetHasFeat(FEAT_HARD_ELEC, oTarget)) + nDC -= 2; + } + else if(nSaveType == SAVING_THROW_TYPE_SONIC) + { + if(GetHasFeat(FEAT_HARD_AIR, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + } + else if(nSaveType == SAVING_THROW_TYPE_ACID) + { + if(GetHasFeat(FEAT_HARD_EARTH, oTarget)) + nDC -= 1 + (GetHitDice(oTarget) / 5); + } + + // This is done here because it is possible to tell the spell school here + // it works by lowering the DC rather than adding to the save + // same net effect but slightly different numbers + if(nSpellSchool == SPELL_SCHOOL_TRANSMUTATION) + { + // Full Moon's Trick - +2 vs Transmutation spels + if(GetLocalInt(oTarget, "FullMoon_Trans")) + nDC -= 2; + if(GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oTarget) >= 2) + nDC -= 4; + } + + // Sapphire Heirarch gets +4 against Chaos and Transmutation effects + if(GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oTarget) >= 2 && GetHasDescriptor(nSpell, DESCRIPTOR_CHAOTIC)) + nDC -= 4; + + // Charming Veil Brow bind grants Essentia bonus on saves against charm and compulsion + if(GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM) || GetIsOfSubschool(nSpell, SUBSCHOOL_COMPULSION)) + { + if (GetIsMeldBound(oTarget, MELD_CHARMING_VEIL) == CHAKRA_BROW) + nDC -= GetEssentiaInvested(oTarget, MELD_CHARMING_VEIL); + } + + // Krinth get +1 save against Shadow spells + if (GetIsOfSubschool(nSpell, SUBSCHOOL_SHADOW) && GetRacialType(oTarget) == RACIAL_TYPE_KRINTH) + nDC -= 1; + + //Psionic race save boosts - +1 vs all spells for Xeph + if(GetHasFeat(FEAT_XEPH_SPELLHARD, oTarget)) + nDC -= 1; + + // Apostate - 1/2 HD bonus to resist divine spells + if(GetHasFeat(FEAT_APOSTATE, oTarget)) + { + //if divine + if(GetIsDivineClass(PRCGetLastSpellCastClass(oSaveVersus))) + nDC -= GetHitDice(oTarget) / 2; + } + + // Hammer of Witches - +1 bonus against Arcane spells + if(GetIsObjectValid(GetItemPossessedBy(oTarget, "WOL_HammerWitches"))) + { + if(GetIsArcaneClass(PRCGetLastSpellCastClass(oSaveVersus))) + nDC -= 1; + } + + // Cultist of the Shattered Peak - +1 bonus against Arcane spells + if(GetLevelByClass(CLASS_TYPE_CULTIST_SHATTERED_PEAK, oTarget)) + { + if(GetIsArcaneClass(PRCGetLastSpellCastClass(oSaveVersus))) + nDC -= 1; + } + + //Insightful Divination + if(GetLocalInt(oTarget, "Insightful Divination")) + { + nDC -= GetLocalInt(oTarget, "Insightful Divination"); + DeleteLocalInt(oTarget, "Insightful Divination"); + } + + // Phierans Resolve - +4 bonus vs spells with evil descriptor + if(GetHasSpellEffect(SPELL_PHIERANS_RESOLVE, oTarget) && GetHasDescriptor(nSpell, DESCRIPTOR_EVIL)) + nDC -= 4; + + //spell school modificators + int nSchool = GetLocalInt(oSaveVersus, "X2_L_LAST_SPELLSCHOOL_VAR"); + if(nSchool == SPELL_SCHOOL_NECROMANCY) + { + // Necrotic Cyst penalty on Necromancy spells + if(GetPersistantLocalInt(oTarget, NECROTIC_CYST_MARKER)) + nDC += 2; + } + else if(nSchool == SPELL_SCHOOL_ILLUSION) + { + // Piercing Sight gives a +4 bonus on illusion saves + if(GetHasFeat(FEAT_PIERCING_SIGHT, oTarget)) + nDC -= 4; + // Adds +2 to Illusion DCs + if(GetLocalInt(oSaveVersus, "ShadowTrickster")) + nDC += 2; + // Illusion Veil Meld + if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oSaveVersus)) + nDC += 1; + // Illusion Veil Meld + if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oTarget)) + nDC -= GetEssentiaInvested(oTarget, MELD_ILLUSION_VEIL); + if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK) + nDC -= 1; + } + else if(nSchool == SPELL_SCHOOL_ENCHANTMENT) + { + //check if Unsettling Enchantment applies + if(GetHasFeat(FEAT_UNSETTLING_ENCHANTMENT, oSaveVersus) && !GetIsImmune(oTarget, IMMUNITY_TYPE_MIND_SPELLS)) + { + effect eLink = EffectLinkEffects(EffectACDecrease(2), EffectAttackDecrease(2)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, 6.0); + } + if(GetRacialType(oTarget) == RACIAL_TYPE_KILLOREN) + nDC -= 2; + if(GetLocalInt(oTarget, "KillorenAncient")) + nDC -= 2; + if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK) + nDC -= 1; + } + + // Hexblade gets a bonus against spells equal to his Charisma (Min +1) + if(nSchool && GetLevelByClass(CLASS_TYPE_HEXBLADE, oTarget)) + { + int nHexCha = GetAbilityModifier(ABILITY_CHARISMA, oTarget); + if (nHexCha < 1) nHexCha = 1; + nDC -= nHexCha; + } + + // Totemist gets a save v magical beasts + if(MyPRCGetRacialType(oSaveVersus) == RACIAL_TYPE_MAGICAL_BEAST && GetLevelByClass(CLASS_TYPE_TOTEMIST, oTarget) >= 3) + nDC -= 3; + + int nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // If the spell is save immune then the link must be applied in order to get the true immunity + // to be resisted. That is the reason for returing false and not true. True blocks the + // application of effects. + if(nSaveRoll == 2 && !bImmunityCheck) + return 0; + + // Failed the save - check if we can reroll + if(!nSaveRoll) + { + if(nSaveType == SAVING_THROW_TYPE_MIND_SPELLS) + { + // Bond Of Loyalty + if(GetLocalInt(oTarget, "BondOfLoyalty")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + DeleteLocalInt(oTarget, "BondOfLoyalty"); + } + } + else if(nSaveType == SAVING_THROW_TYPE_FEAR) + { + // Call To Battle Reroll + if(GetLocalInt(oTarget, "CallToBattle")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + DeleteLocalInt(oTarget, "CallToBattle"); + } + } + + // Second Chance power reroll + if(!nSaveRoll && GetLocalInt(oTarget, "PRC_Power_SecondChance_Active") // Second chance is active + && !GetLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound")) // And hasn't yet been used for this round + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // Can't use this ability again for a round + SetLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound", TRUE); + DelayCommand(6.0f, DeleteLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound")); + } + + // Zealous Surge Reroll + if(!nSaveRoll && GetLocalInt(oTarget, "ZealousSurge")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // Ability Used + DeleteLocalInt(oTarget, "ZealousSurge"); + } + + // Balam's Cunning Reroll + if(!nSaveRoll && GetLocalInt(oTarget, "BalamCunning")) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + + // Ability Used + DeleteLocalInt(oTarget, "BalamCunning"); + } + + if(!nSaveRoll) + { + if(nSavingThrow == SAVING_THROW_REFLEX) + { + // Dive for Cover reroll + if(GetHasFeat(FEAT_DIVE_FOR_COVER, oTarget)) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 6.0); + } + } + // Impetuous Endurance - fortitude or will save + else if(GetLevelByClass(CLASS_TYPE_KNIGHT, oTarget) > 16) + { + // Reroll + nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay); + } + } + } + //Serene Guardian Unclouded Mind + int nSerene = GetLevelByClass(CLASS_TYPE_SERENE_GUARDIAN, oTarget); + //Applies on failed will saves from 9th level on + if (nSaveRoll == 1 && nSavingThrow == SAVING_THROW_WILL && nSerene >= 9) + { + if (GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC)) // Concentration check to beat the DC + nSaveRoll = 0; + } + + // Iron Mind Barbed Mind ability + if(nSaveRoll == 1 && nSaveType == SAVING_THROW_TYPE_MIND_SPELLS) + { + // Only works in Heavy Armour + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oTarget) > 9 + && GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget)) > 5) + { + // Spell/Power caster takes 1d6 damage and 1 Wisdom damage + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(), DAMAGE_TYPE_MAGICAL), oSaveVersus); + ApplyAbilityDamage(oSaveVersus, ABILITY_WISDOM, 1, DURATION_TYPE_TEMPORARY, TRUE, -1.0); + } + } + + // Hobgoblin Warsoul spell eater + if(nSaveRoll == 1 && GetRacialType(oTarget) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + { + //Apply the Aid VFX impact and effects + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HOLY_AID), oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAttackIncrease(2), oTarget, 6.0); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectTemporaryHitpoints(5), oTarget, 60.0); + } + + return nSaveRoll; +} + + +int PRCGetReflexAdjustedDamage(int nDamage, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF) +{ + int nEvasion; + if(GetHasFeat(FEAT_IMPROVED_EVASION, oTarget)) + nEvasion = 2; + else if(GetHasFeat(FEAT_EVASION, oTarget)) + nEvasion = 1; + + // Grants evasion against dragons, don't override if they've already gotit + object oWOL = GetItemPossessedBy(oTarget, "WOL_CrimsonRuination"); + if(oWOL != GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget) && MyPRCGetRacialType(oSaveVersus) == RACIAL_TYPE_DRAGON && 1 > nEvasion) + nEvasion = 1; + + // This ability removes evasion from the target + if(GetLocalInt(oTarget, "TrueConfoundingResistance")) + { + if(nEvasion) + nEvasion -= 1; + else + nDC += 2; + } + + //Get saving throw result + int nSaveRoll = PRCMySavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, nSaveType, oSaveVersus); + + //save success + if(nSaveRoll) + nDamage = nEvasion ? 0 : nDamage / 2; + //save failed - check improved evasion + else if(nEvasion == 2) + nDamage = nDamage / 2; + + return nDamage; +} + +// function for internal use in GetCasterLvl + +// caster level for arcane base classes (with practiced spellcaster feats) +int GetCasterLvlArcane(int iClassType, object oCaster) +{ + if (GetPrimaryArcaneClass(oCaster) == iClassType) + return GetLevelByTypeArcane(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster); + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +// caster level for classes with half progression +int GetCasterLvlArcaneSemi(int iClassType, object oCaster) +{ + if (GetPrimaryArcaneClass(oCaster) == iClassType) + return GetLevelByTypeArcane(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster) / 2; + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +// caster level for divine base classes (with practiced spellcaster feats) +int GetCasterLvlDivine(int iClassType, object oCaster) +{ + if (GetPrimaryDivineClass(oCaster) == iClassType) + return GetLevelByTypeDivine(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster); + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +// caster level for classes with half progression +int GetCasterLvlDivineSemi(int iClassType, object oCaster) +{ + if (GetPrimaryDivineClass(oCaster) == iClassType) + return GetLevelByTypeDivine(oCaster); + + int iTemp = GetLevelByClass(iClassType, oCaster) / 2; + iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp); + return iTemp; +} + +int GetCasterLvl(int iTypeSpell, object oCaster = OBJECT_SELF) +{ + switch (iTypeSpell) + { + case TYPE_ARCANE: + return GetLevelByTypeArcane(oCaster); + + case TYPE_DIVINE: + return GetLevelByTypeDivine(oCaster); + + case CLASS_TYPE_SORCERER: + { + if (GetPrimaryArcaneClass(oCaster) == CLASS_TYPE_SORCERER) + return GetLevelByTypeArcane(oCaster); + + if(!GetLevelByClass(CLASS_TYPE_SORCERER, oCaster)) + { + int iTemp; + int nRace = GetRacialType(oCaster); + + //Aranea include shapechanger HD as sorc + if(nRace == RACIAL_TYPE_ARANEA) + iTemp = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oCaster); + + //Rakshasa include outsider HD as sorc + if(nRace == RACIAL_TYPE_RAKSHASA) + iTemp = GetLevelByClass(CLASS_TYPE_OUTSIDER, oCaster); + + //Drider include aberration HD as sorc + if(nRace == RACIAL_TYPE_DRIDER) + iTemp = GetLevelByClass(CLASS_TYPE_ABERRATION, oCaster); + + //Arkamoi + Redspawn include MH HD as sorc + if(nRace == RACIAL_TYPE_ARKAMOI) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster); + if(nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster); + if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster)*3/4; + if(nRace == RACIAL_TYPE_MARRUTACT) + iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster)*6/7; + + iTemp += PracticedSpellcasting(oCaster, CLASS_TYPE_SORCERER, iTemp); + iTemp += DraconicPower(oCaster); + return iTemp; + } + } + + case CLASS_TYPE_BARD: + { + if (GetPrimaryArcaneClass(oCaster) == CLASS_TYPE_BARD) + return GetLevelByTypeArcane(oCaster); + + if(!GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + int iTemp; + int nRace = GetRacialType(oCaster); + + //Gloura include fey HD as bard + if(nRace == RACIAL_TYPE_GLOURA) + iTemp = GetLevelByClass(CLASS_TYPE_FEY, oCaster); + + iTemp += PracticedSpellcasting(oCaster, CLASS_TYPE_BARD, iTemp); + iTemp += DraconicPower(oCaster); + return iTemp; + } + } + + case CLASS_TYPE_ASSASSIN: + case CLASS_TYPE_CELEBRANT_SHARESS: + case CLASS_TYPE_BEGUILER: + case CLASS_TYPE_CULTIST_SHATTERED_PEAK: + case CLASS_TYPE_DREAD_NECROMANCER: + case CLASS_TYPE_DUSKBLADE: + case CLASS_TYPE_SHADOWLORD: + case CLASS_TYPE_SUEL_ARCHANAMACH: + case CLASS_TYPE_WARMAGE: + case CLASS_TYPE_WIZARD: + return GetCasterLvlArcane(iTypeSpell, oCaster); + + case CLASS_TYPE_HEXBLADE: + return GetCasterLvlArcaneSemi(iTypeSpell, oCaster); + + case CLASS_TYPE_ARCHIVIST: + case CLASS_TYPE_BLACKGUARD: + case CLASS_TYPE_BLIGHTER: + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_DRUID: + case CLASS_TYPE_FAVOURED_SOUL: + case CLASS_TYPE_HEALER: + case CLASS_TYPE_KNIGHT_CHALICE: + case CLASS_TYPE_KNIGHT_MIDDLECIRCLE: + case CLASS_TYPE_NENTYAR_HUNTER: + case CLASS_TYPE_OCULAR: + case CLASS_TYPE_SHAMAN: + case CLASS_TYPE_SLAYER_OF_DOMIEL: + case CLASS_TYPE_SOLDIER_OF_LIGHT: + case CLASS_TYPE_UR_PRIEST: + case CLASS_TYPE_VASSAL: + return GetCasterLvlDivine(iTypeSpell, oCaster); + + case CLASS_TYPE_PALADIN: + case CLASS_TYPE_RANGER: + case CLASS_TYPE_SOHEI: + return GetCasterLvlDivineSemi(iTypeSpell, oCaster); + } + return 0; +} + + +////////////////Begin Spellsword////////////////// + +void SetAllAoEInts(int SpellID, object oAoE, int nBaseSaveDC, int SpecDispel = 0, int nCasterLevel = 0) +{ + if(!GetLocalInt(oAoE, "X2_AoE_BaseSaveDC"))//DC should always be > 0 + { + SetLocalInt(oAoE, "X2_AoE_SpellID", SpellID); + SetLocalInt(oAoE, "X2_AoE_BaseSaveDC", nBaseSaveDC); + if(SpecDispel) SetLocalInt(oAoE, "X2_AoE_SpecDispel", SpecDispel); + if(!nCasterLevel) nCasterLevel = PRCGetCasterLevel(); + SetLocalInt(oAoE, "X2_AoE_Caster_Level", nCasterLevel); + if(GetHasFeat(FEAT_SHADOWWEAVE)) SetLocalInt(oAoE, "X2_AoE_Weave", TRUE); + } +} + +//GetNextObjectInShape wrapper for changing the AOE of the channeled spells +object MyNextObjectInShape(int nShape, + float fSize, location lTarget, + int bLineOfSight = FALSE, + int nObjectFilter = OBJECT_TYPE_CREATURE, + vector vOrigin=[0.0, 0.0, 0.0]) +{ + object oCaster = OBJECT_SELF; + + // War Wizard of Cormyr's Widen Spell ability + float fMulti = GetLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER); + //if(DEBUG) DoDebug("Original Spell Size: " + FloatToString(fSize)+"\n" + "Widened Multiplier: " + FloatToString(fMulti)); + + float fHFInfusion = GetLocalFloat(oCaster, "PRC_HF_Infusion_Wid"); + if(fHFInfusion > 0.0f) + { + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster) + { + //Hellfire Infusion - doesn't work on scrolls and potions + int nType = GetBaseItemType(oItem); + if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION + && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL) + { + fMulti = fHFInfusion; + DelayCommand(1.0, DeleteLocalFloat(oCaster, "PRC_HF_Infusion_Wid")); + } + } + } + + if(fMulti > 0.0) + { + fSize *= fMulti; + if(DEBUG) DoDebug("New Spell Size: " + FloatToString(fSize)); + } + + if(GetLocalInt(oCaster, "spellswd_aoe") == 1) + { + return OBJECT_INVALID; + } + + return GetNextObjectInShape(nShape,fSize,lTarget,bLineOfSight,nObjectFilter,vOrigin); +} + +//GetFirstObjectInShape wrapper for changing the AOE of the channeled spells +object MyFirstObjectInShape(int nShape, + float fSize, location lTarget, + int bLineOfSight = FALSE, + int nObjectFilter = OBJECT_TYPE_CREATURE, + vector vOrigin=[0.0, 0.0, 0.0]) +{ + object oCaster = OBJECT_SELF; + + //int on caster for the benefit of spellfire wielder resistance + // string sName = "IsAOE_" + IntToString(GetSpellId()); + string sName = "IsAOE_" + IntToString(PRCGetSpellId(oCaster)); + + SetLocalInt(oCaster, sName, 1); + DelayCommand(0.1, DeleteLocalInt(oCaster, sName)); + + // War Wizard of Cormyr's Widen Spell ability + float fMulti = GetLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER); + //if(DEBUG) DoDebug("Original Spell Size: " + FloatToString(fSize)+"\n" + "Widened Multiplier: " + FloatToString(fMulti)); + + float fHFInfusion = GetLocalFloat(oCaster, "PRC_HF_Infusion_Wid"); + if(fHFInfusion > 0.0f) + { + object oItem = PRCGetSpellCastItem(oCaster); + if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster) + { + //Hellfire Infusion - doesn't work on scrolls and potions + int nType = GetBaseItemType(oItem); + if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION + && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL) + { + fMulti = fHFInfusion; + DelayCommand(1.0, DeleteLocalFloat(oCaster, "PRC_HF_Infusion_Wid")); + } + } + } + + if(fMulti > 0.0) + { + fSize *= fMulti; + if(DEBUG) DoDebug("New Spell Size: " + FloatToString(fSize)); + + // This allows it to affect the entire casting + DelayCommand(1.0, DeleteLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER)); + } + + if(GetLocalInt(oCaster, "spellswd_aoe") == 1) + { + return PRCGetSpellTargetObject(oCaster); + } + + return GetFirstObjectInShape(nShape,fSize,lTarget,bLineOfSight,nObjectFilter,vOrigin); +} + + +//This checks if the spell is channeled and if there are multiple spells +//channeled, which one is it. Then it checks in either case if the spell +//has the metamagic feat the function gets and returns TRUE or FALSE accordingly +//Also used by the new spellbooks for the same purpose +/* replaced by wrapper for GetMetaMagicFeat instead + Not necessarily. This may still be a usefule level of abstraction - Ornedan + */ +int CheckMetaMagic(int nMeta,int nMMagic) +{ + return nMeta & nMMagic; +} + +int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE) +{ + int nOverride = GetLocalInt(oCaster, PRC_METAMAGIC_OVERRIDE); + if(nOverride) + { + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found override metamagic = "+IntToString(nOverride)+", original = "+IntToString(GetMetaMagicFeat())); + return nOverride; + } + + object oItem = PRCGetSpellCastItem(oCaster); + + // we assume that we are casting from an item, if the item is valid and the item belongs to oCaster + // however, we cannot be sure because of Bioware's inadequate implementation of GetSpellCastItem + if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster) + { + int nFeat; + + //check item for metamagic + itemproperty ipTest = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_METAMAGIC) + { + int nSubType = GetItemPropertySubType(ipTest); + nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType)); + if(nSubType == PRCGetSpellId(oCaster)) + { + int nCostValue = GetItemPropertyCostTableValue(ipTest); + if(nCostValue == -1 && DEBUG) + DoDebug("Problem examining itemproperty"); + switch(nCostValue) + { + //bitwise "addition" equivalent to nFeat = (nFeat | nSSFeat) + case 1: nFeat |= METAMAGIC_QUICKEN; break; + case 2: nFeat |= METAMAGIC_EMPOWER; break; + case 3: nFeat |= METAMAGIC_EXTEND; break; + case 4: nFeat |= METAMAGIC_MAXIMIZE; break; + case 5: nFeat |= METAMAGIC_SILENT; break; + case 6: nFeat |= METAMAGIC_STILL; break; + } + } + } + ipTest = GetNextItemProperty(oItem); + } + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: item casting with item = "+GetName(oItem)+", found metamagic = "+IntToString(nFeat)); + + //Hellfire Infusion - doesn't work on scrolls and potions + int nType = GetBaseItemType(oItem); + if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION + && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL) + { + nFeat |= GetLocalInt(oCaster, "PRC_HF_Infusion"); + if(bClearFeatFlags) DeleteLocalInt(oCaster, "PRC_HF_Infusion"); + } + + //apply metamagic adjustment (chanell spell) + nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT); + return nFeat; + } + + if(GetLocalInt(oCaster, "PRC_SPELL_EVENT")) + { + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found spell event metamagic = "+IntToString(GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC"))+", original = "+IntToString(GetMetaMagicFeat())); + return GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC"); + } + + int nFeat = GetMetaMagicFeat(); + if(nFeat == METAMAGIC_ANY) + // work around for spontaneous casters (bard or sorcerer) having all metamagic turned on when using ActionCastSpell* + nFeat = METAMAGIC_NONE; + + nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT); + + int nClass = PRCGetLastSpellCastClass(oCaster); + // Suel Archanamach's Extend spells they cast on themselves. + // Only works for Suel Spells, and not any other caster type they might have + // Since this is a spellscript, it assumes OBJECT_SELF is the caster + if(nClass == CLASS_TYPE_SUEL_ARCHANAMACH + && GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH) >= 3) + { + // Check that they cast on themselves + // if (oCaster == PRCGetSpellTargetObject()) + if(oCaster == PRCGetSpellTargetObject(oCaster)) + { + // Add extend to the metamagic feat using bitwise math + nFeat |= METAMAGIC_EXTEND; + } + } + //Code for the Abjurant Champion. Works similar to the Suel Archamamach but + //their extend only works on abjuration spells they cast. + if(GetHasFeat(FEAT_EXTENDED_ABJURATION, oCaster) + && GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION) >= 1) + { + int nSpellSchool = GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR"); + // Check that they cast an abjuration spell + if(nSpellSchool == SPELL_SCHOOL_ABJURATION) + { + // Add extend to the metamagic feat using bitwise math + nFeat |= METAMAGIC_EXTEND; + if (DEBUG) FloatingTextStringOnCreature("Extended Abjuration Applied", oCaster, FALSE); + } + } + // Magical Contraction, Truenaming Utterance + if(GetHasSpellEffect(UTTER_MAGICAL_CONTRACTION_R, oCaster)) + //(GetLocalInt(oCaster, "TrueMagicalContraction")) + { + nFeat |= METAMAGIC_EMPOWER; + } + // Sudden Metamagic + int nSuddenMeta = GetLocalInt(oCaster, "SuddenMeta"); + if(nSuddenMeta) + { + nFeat |= nSuddenMeta; + if(bClearFeatFlags) + DeleteLocalInt(oCaster, "SuddenMeta"); + } + + int nDivMeta = GetLocalInt(oCaster, "DivineMeta"); + if(nDivMeta) + { + if(GetIsDivineClass(nClass, oCaster)) + { + nFeat |= nDivMeta; + if(bClearFeatFlags) + DeleteLocalInt(oCaster, "DivineMeta"); + } + } + + int nSpelldance = GetLocalInt(oCaster, "Spelldance"); + if(nSpelldance) + { + nFeat |= nSpelldance; + if (DEBUG) FloatingTextStringOnCreature("Metamagic Spelldances applied", oCaster, FALSE); + } + + int nSpellLevel = PRCGetSpellLevel(oCaster, PRCGetSpellId()); + if (GetLocalInt(oCaster, "Aradros_Extend")/* && GetIsArcaneClass(nClass, oCaster)*/) + { + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: GetIsArcaneClass is TRUE"); + int nHD = GetHitDice(oCaster); + if (nHD >= 12 && 6 >= nSpellLevel) + { + nFeat |= METAMAGIC_EXTEND; + DeleteLocalInt(oCaster, "Aradros_Extend"); + if (DEBUG) FloatingTextStringOnCreature("Aradros Extend Applied", oCaster, FALSE); + } + else if (3 >= nSpellLevel) + { + nFeat |= METAMAGIC_EXTEND; + DeleteLocalInt(oCaster, "Aradros_Extend"); + if (DEBUG) FloatingTextStringOnCreature("Aradros Extend Applied", oCaster, FALSE); + } + } + + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: nSpellLevel " +IntToString(nSpellLevel)+", PRCGetSpellId " +IntToString(PRCGetSpellId())+", nClass " +IntToString(nClass)); + if (DEBUG) DoDebug("PRCGetMetaMagicFeat: returning " +IntToString(nFeat)); + return nFeat; +} + +//Wrapper for The MaximizeOrEmpower function +int PRCMaximizeOrEmpower(int nDice, int nNumberOfDice, int nMeta, int nBonus = 0) +{ + int i, nDamage; + for (i = 1; i <= nNumberOfDice; i++) + { + nDamage = nDamage + Random(nDice) + 1; + } + + //Resolve metamagic + if(nMeta & METAMAGIC_MAXIMIZE) + nDamage = nDice * nNumberOfDice; + if(nMeta & METAMAGIC_EMPOWER) + nDamage = nDamage + nDamage / 2; + + return nDamage + nBonus; +} + +float PRCGetMetaMagicDuration(float fDuration, int nMeta = -1) +{ + if(nMeta == -1) // no metamagic value was passed, so get it here + nMeta = PRCGetMetaMagicFeat(); + + if(nMeta & METAMAGIC_EXTEND) + fDuration *= 2; + + return fDuration; +} + +int PRCGetMetaMagicDamage(int nDamageType, int nDice, int nDieSize, + int nBonusPerDie = 0, int nBonus = 0, int nMetaMagic = -1) +{ + // If the metamagic argument wasn't given get it. + if (-1 == nMetaMagic) nMetaMagic = PRCGetMetaMagicFeat(); + + // Roll the damage, applying metamagic. + int nDamage = PRCMaximizeOrEmpower(nDieSize, nDice, nMetaMagic, (nBonusPerDie * nDice) + nBonus); + return nDamage; +} + +void PRCSetSchool(int nSchool = SPELL_SCHOOL_GENERAL) +{ + // Remove the last value in case it is there and set the new value if it is NOT general. + if(SPELL_SCHOOL_GENERAL != nSchool) + SetLocalInt(OBJECT_SELF, "X2_L_LAST_SPELLSCHOOL_VAR", nSchool); + else + DeleteLocalInt(OBJECT_SELF, "X2_L_LAST_SPELLSCHOOL_VAR"); +} + +void PRCSignalSpellEvent(object oTarget, int bHostile = TRUE, int nSpellID = -1, object oCaster = OBJECT_SELF) +{ + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(oCaster, nSpellID, bHostile)); +} +/* +void SPEvilShift(object oPC) +{ + // Check for alignment shift switch being active + if(GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT)) + { + // Amount of adjustment is equal to the square root of your distance from pure evil. + // In other words, the amount of shift is higher the farther you are from pure evil, with the + // extremes being 10 points of shift at pure good and 0 points of shift at pure evil. + AdjustAlignment(oPC, ALIGNMENT_EVIL, FloatToInt(sqrt(IntToFloat(GetGoodEvilValue(oPC)))), FALSE); + } +} + +void SPGoodShift(object oPC) +{ + // Check for alignment shift switch being active + if(GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT)) + { + // Amount of adjustment is equal to the square root of your distance from pure good. + // In other words, the amount of shift is higher the farther you are from pure good, with the + // extremes being 10 points of shift at pure evil and 0 points of shift at pure good. + AdjustAlignment(oPC, ALIGNMENT_GOOD, FloatToInt(sqrt(IntToFloat(100 - GetGoodEvilValue(oPC)))), FALSE); + } +}*/ + +void DoCorruptionCost(object oPC, int nAbility, int nCost, int bDrain) +{ + // Undead redirect all damage & drain to Charisma, sez http://www.wizards.com/dnd/files/BookVileFAQ12102002.zip + if(MyPRCGetRacialType(oPC) == RACIAL_TYPE_UNDEAD) + nAbility = ABILITY_CHARISMA; + + //Exalted Raiment + if(GetHasSpellEffect(SPELL_EXALTED_RAIMENT, GetItemInSlot(INVENTORY_SLOT_CHEST, oPC))) + { + nCost -= 1; + + if(nCost < 1) + nCost = 1; + } + + if (GetHasFeat(FEAT_FAVORED_ZULKIRS, oPC)) nCost -= 1; + + // Is it ability drain? + if(bDrain) + ApplyAbilityDamage(oPC, nAbility, nCost, DURATION_TYPE_PERMANENT, TRUE); + // Or damage + else + ApplyAbilityDamage(oPC, nAbility, nCost, DURATION_TYPE_TEMPORARY, TRUE, -1.0f); +} + +//:: Fix by TiredByFirelight +void MultisummonPreSummon(object oPC = OBJECT_SELF, int bOverride = FALSE) +{ + if(!GetPRCSwitch(PRC_MULTISUMMON) && !bOverride) + return; + int nCount = GetPRCSwitch(PRC_MULTISUMMON); + if(bOverride) + nCount = bOverride; + if(nCount < 0 + || nCount == 1) + nCount = 99; + if(nCount > 99) + nCount = 99; + + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, nCount); + if(!GetIsObjectValid(oSummon) && !GetLocalInt(oSummon, "RFSummonedElemental")) + { + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, 1); + AssignCommand(oSummon, SetIsDestroyable(FALSE, FALSE, FALSE)); + AssignCommand(oSummon, DelayCommand(0.3, SetIsDestroyable(TRUE, FALSE, FALSE))); + } +} + +/* void MultisummonPreSummon(object oPC = OBJECT_SELF, int bOverride = FALSE) +{ + if(!GetPRCSwitch(PRC_MULTISUMMON) && !bOverride) + return; + int i=1; + int nCount = GetPRCSwitch(PRC_MULTISUMMON); + if(bOverride) + nCount = bOverride; + if(nCount < 0 + || nCount == 1) + nCount = 99; + if(nCount > 99) + nCount = 99; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummon) && !GetLocalInt(oSummon, "RFSummonedElemental") && i < nCount) + { + AssignCommand(oSummon, SetIsDestroyable(FALSE, FALSE, FALSE)); + AssignCommand(oSummon, DelayCommand(0.3, SetIsDestroyable(TRUE, FALSE, FALSE))); + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } +} */ + + +//This function returns 1 only if the object oTarget is the object +//the weapon hit when it channeled the spell sSpell or if there is no +//channeling at all +int ChannelChecker(string sSpell, object oTarget) +{ + int nSpell = GetLocalInt(GetAreaOfEffectCreator(), sSpell+"channeled"); + int nTarget = GetLocalInt(oTarget, sSpell+"target"); + if(nSpell == 1 && nTarget == 1) + { + return 1; + } + else if(nSpell != 1 && nTarget != 1) + { + return 1; + } + else + { + return 0; + } +} + +//If a spell is being channeled, we store its target and its name +void StoreSpellVariables(string sString,int nDuration) +{ + object oCaster = OBJECT_SELF; + object oTarget = GetSpellTargetObject(); //using prc version could cause problems + + if(GetLocalInt(oCaster,"spellswd_aoe") == 1) + { + SetLocalInt(oCaster, sString+"channeled",1); + SetLocalInt(oTarget, sString+"target",1); + } + DelayCommand(RoundsToSeconds(nDuration),DeleteLocalInt(oTarget, sString+"target")); + DelayCommand(RoundsToSeconds(nDuration),DeleteLocalInt(oCaster, sString+"channeled")); +} + +effect ChannelingVisual() +{ + return EffectVisualEffect(VFX_DUR_SPELLTURNING); +} + +////////////////End Spellsword////////////////// + + +int GetHasMettle(object oTarget, int nSavingThrow = SAVING_THROW_WILL) +{ + if(GetLevelByClass(CLASS_TYPE_HEXBLADE, oTarget) > 2) return TRUE; + if(GetLevelByClass(CLASS_TYPE_SOHEI, oTarget) > 8) return TRUE; + if(GetLevelByClass(CLASS_TYPE_CRUSADER, oTarget) > 12) return TRUE; + if(GetLocalInt(oTarget, "FactotumMettle")) return TRUE; + + if(nSavingThrow = SAVING_THROW_WILL) + { + // Iron Mind's ability only functions in Heavy Armour + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oTarget) > 4 + && GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget)) > 5) + return TRUE; + // Fill out the line below to add another class with Will mettle + // if (GetLevelByClass(CLASS_TYPE_X, oTarget) >= X) return TRUE; + } + /*else if(nSavingThrow = SAVING_THROW_FORT) + { + // Add Classes with Fort mettle here + // if (GetLevelByClass(CLASS_TYPE_X, oTarget) >= X) return TRUE; + }*/ + + return FALSE; +} + +void DoCommandSpell(object oCaster, object oTarget, int nSpellId, int nDuration, int nCaster) +{ + if(DEBUG) DoDebug("DoCommandSpell: SpellId: " + IntToString(nSpellId)); + if(DEBUG) DoDebug("Command Spell: Begin"); + if(nSpellId == SPELL_COMMAND_APPROACH + || nSpellId == SPELL_GREATER_COMMAND_APPROACH + || nSpellId == 18359 //MYST_VOICE_SHADOW_APPROACH + || nSpellId == SPELL_DOA_COMMAND_APPROACH + || nSpellId == SPELL_DOA_GREATER_COMMAND_APPROACH) + { + // Force the target to approach the caster + if(DEBUG) DoDebug("Command Spell: Approach"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + AssignCommand(oTarget, ActionForceMoveToObject(oCaster, TRUE)); + } + // Creatures that can't be disarmed ignore this + else if(nSpellId == SPELL_COMMAND_DROP + || nSpellId == SPELL_GREATER_COMMAND_DROP + || nSpellId == 18360 //MYST_VOICE_SHADOW_DROP + || nSpellId == SPELL_DOA_COMMAND_DROP + || nSpellId == SPELL_DOA_GREATER_COMMAND_DROP) + { + if(GetIsCreatureDisarmable(oTarget) && !GetPRCSwitch(PRC_PNP_DISARM)) + { + // Force the target to drop what its holding + if(DEBUG) DoDebug("Command Spell: Drop"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + object oTargetWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + SetDroppableFlag(oTargetWep, TRUE); + SetStolenFlag(oTargetWep, FALSE); + AssignCommand(oTarget, ActionPutDownItem(oTargetWep)); + /*oTargetWep = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + SetDroppableFlag(oTargetWep, TRUE); + SetStolenFlag(oTargetWep, FALSE); + AssignCommand(oTarget, ActionPutDownItem(oTargetWep)); */ + } + else + { + FloatingTextStringOnCreature(GetName(oTarget) + " is not disarmable.", oCaster, FALSE); + } + } + else if(nSpellId == SPELL_COMMAND_FALL + || nSpellId == SPELL_GREATER_COMMAND_FALL + || nSpellId == 18361 //MYST_VOICE_SHADOW_FALL + || nSpellId == SPELL_DOA_COMMAND_FALL + || nSpellId == SPELL_DOA_GREATER_COMMAND_FALL) + { + // Force the target to fall down + if(DEBUG) DoDebug("Command Spell: Fall"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, RoundsToSeconds(nDuration),TRUE,-1,nCaster); + } + else if(nSpellId == SPELL_COMMAND_FLEE + || nSpellId == SPELL_GREATER_COMMAND_FLEE + || nSpellId == 18362 //MYST_VOICE_SHADOW_FLEE + || nSpellId == SPELL_DOA_COMMAND_FLEE + || nSpellId == SPELL_DOA_GREATER_COMMAND_FLEE) + { + // Force the target to flee the caster + if(DEBUG) DoDebug("Command Spell: Flee"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + AssignCommand(oTarget, ActionMoveAwayFromObject(oCaster, TRUE)); + } + else if(nSpellId == SPELL_COMMAND_HALT + || nSpellId == SPELL_GREATER_COMMAND_HALT + || nSpellId == 18363 //MYST_VOICE_SHADOW_HALT + || nSpellId == SPELL_DOA_COMMAND_HALT + || nSpellId == SPELL_DOA_GREATER_COMMAND_HALT) + { + // Force the target to stand still + if(DEBUG) DoDebug("Command Spell: Stand"); + AssignCommand(oTarget, ClearAllActions(TRUE)); + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectCutsceneParalyze(), oTarget, RoundsToSeconds(nDuration),TRUE,-1,nCaster); + } + else // Catch errors here + { + FloatingTextStringOnCreature("sp_command/sp_greatcommand: Error, Unknown SpellId", oCaster, FALSE); + } + if(DEBUG) DoDebug("Command Spell: End"); +} + +void SetIncorporeal(object oTarget, float fDuration, int nSuperOrEx, int nPermanent = FALSE) +{ + if (!GetIsObjectValid(oTarget)) + return; //No point working + + // Immune to non-magical weapons, ignore physical objects + effect eIncorporeal = EffectLinkEffects(EffectDamageReduction(100, DAMAGE_POWER_PLUS_ONE, 0), EffectCutsceneGhost()); + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_BLUDGEONING, 50)); // 50% chance of magical weapons not working. Done as 50% Damage Immunity + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_SLASHING, 50)); + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_PIERCING, 50)); + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectMissChance(50, MISS_CHANCE_TYPE_VS_MELEE)); // 50% melee miss chance to hit non-incorporeal targets. That's going to be everything + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectSkillIncrease(SKILL_MOVE_SILENTLY, 99)); // Cannot be heard + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE)); // Visual effect + + // No Strength Score, use Dex for melee attacks too + int nStr = GetAbilityScore(oTarget, ABILITY_STRENGTH); + int nDex = GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + int nPen; + + if (nStr>10) + { + nPen = nStr - 10; + eIncorporeal = EffectLinkEffects(eIncorporeal, EffectAbilityDecrease(ABILITY_STRENGTH, nPen)); // Reduce Strength to 10 + + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if (GetIsObjectValid(oWeapon) && IPGetIsMeleeWeapon(oWeapon)) + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (GetIsObjectValid(oWeapon) && IPGetIsMeleeWeapon(oWeapon)) + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget); + if (GetIsObjectValid(oWeapon)) // We know these are melee weapons + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget); + if (GetIsObjectValid(oWeapon)) // We know these are melee weapons + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget); + if (GetIsObjectValid(oWeapon)) // We know these are melee weapons + { + IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus + } + } + + SetLocalInt(oTarget, "Incorporeal", TRUE); + if (!nPermanent) DelayCommand(fDuration, DeleteLocalInt(oTarget, "Incorporeal")); + + if (nSuperOrEx == 1) + eIncorporeal = SupernaturalEffect(eIncorporeal); + else if (nSuperOrEx == 2) + eIncorporeal = ExtraordinaryEffect(eIncorporeal); + + if (nPermanent) + SPApplyEffectToObject(DURATION_TYPE_PERMANENT, eIncorporeal, oTarget); + else + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eIncorporeal, oTarget, fDuration, FALSE, -1, -1); +} + +int GetIsIncorporeal(object oTarget) +{ + //Check for local int + if(GetPersistantLocalInt(oTarget, "Is_Incorporeal")) + return TRUE; + + //Check for local int + if(GetLocalInt(oTarget, "Incorporeal")) + return TRUE; + + //check for feat + if(GetHasFeat(FEAT_INCORPOREAL, oTarget)) + return TRUE; + + //base it on appearance + int nAppear = GetAppearanceType(oTarget); + if(nAppear == APPEARANCE_TYPE_ALLIP + || nAppear == APPEARANCE_TYPE_SHADOW + || nAppear == APPEARANCE_TYPE_SHADOW_FIEND + || nAppear == APPEARANCE_TYPE_SPECTRE + || nAppear == APPEARANCE_TYPE_WRAITH) + return TRUE; + + //Return value + return FALSE; +} + +int GetIsEthereal(object oTarget) +{ + return GetPersistantLocalInt(oTarget, "Is_Ethereal") + || GetHasFeat(FEAT_ETHEREAL, oTarget); +} + +int PRCGetIsAliveCreature(object oTarget) +{ + int bAlive = TRUE; + // non-creatures aren't alive + if (GetObjectType(oTarget) != OBJECT_TYPE_CREATURE) + return FALSE; // night of the living waypoints :p + + int nType = MyPRCGetRacialType(oTarget); + + //Non-living races, excluding warforged + if(nType == RACIAL_TYPE_UNDEAD || + (nType == RACIAL_TYPE_CONSTRUCT && !GetIsWarforged(oTarget))) bAlive = FALSE; + + //If they're dead :P + if(GetIsDead(oTarget)) bAlive = FALSE; + + //return + return bAlive; +} + +int GetControlledUndeadTotalHD(object oPC = OBJECT_SELF) +{ + int nTotalHD; + int i = 1; + object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummonTest)) + { + if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_UNDEAD) + nTotalHD += GetHitDice(oSummonTest); + if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC); + i++; + oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } + return nTotalHD; +} + + +int GetControlledFiendTotalHD(object oPC = OBJECT_SELF) +{ + int nTotalHD; + int i = 1; + object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummonTest)) + { + if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_OUTSIDER + && GetAlignmentGoodEvil(oSummonTest) == ALIGNMENT_EVIL) + nTotalHD += GetHitDice(oSummonTest); + if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC); + i++; + oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } + return nTotalHD; +} + +int GetControlledCelestialTotalHD(object oPC = OBJECT_SELF) +{ + int nTotalHD; + int i = 1; + object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + while(GetIsObjectValid(oSummonTest)) + { + if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_OUTSIDER + && GetAlignmentGoodEvil(oSummonTest) == ALIGNMENT_GOOD) + nTotalHD += GetHitDice(oSummonTest); + if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC); + i++; + oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i); + } + return nTotalHD; +} + +//:: Handles psuedo-Foritifcation +void DoFortification(object oPC = OBJECT_SELF, int nFortification = 25) +{ + if(DEBUG) DoDebug("prc_inc_spells >> DoFortification() is running."); + + // Get or create the player's skin object + object oHide = GetPCSkin(oPC); + + int nRace = GetRacialType(oPC); + + //else if (nRace == RACIAL_TYPE_WARFORGED && !GetHasFeat(FEAT_IMPROVED_FORTIFICATION, oPC) && !GetHasFeat(FEAT_UNARMORED_BODY, oPC)) + if (nFortification == FORTIFICATION_LIGHT) + { + // Apply immunity properties for 1 seconds + if(DEBUG) DoDebug("Applying Light Fortification"); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS), 1.0f); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB), 1.0f); + + // Schedule the next toggle in 4 seconds + DelayCommand(4.0f, DoFortification(oPC, FORTIFICATION_LIGHT)); + } + + else if (nFortification == FORTIFICATION_MEDIUM) //nRace == RACIAL_TYPE_RETH_DEKALA) + { + // Apply immunity properties for 2 seconds + if(DEBUG) DoDebug("Applying Medium Fortification"); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS), 2.0f); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB), 2.0f); + + // Schedule the next toggle in 4 seconds + DelayCommand(4.0f, DoFortification(oPC, FORTIFICATION_MEDIUM)); + } + + //else if (nRace == RACIAL_TYPE_WARFORGED_CHARGER && !GetHasFeat(FEAT_IMPROVED_FORTIFICATION, oPC) && !GetHasFeat(FEAT_UNARMORED_BODY, oPC)) + else if (nFortification == FORTIFICATION_MODERATE) + { + // Apply immunity properties for 3 seconds + if(DEBUG) DoDebug("Applying Moderate Fortification"); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS), 3.0f); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB), 3.0f); + + // Schedule the next toggle in 4 seconds + DelayCommand(4.0f, DoFortification(oPC, FORTIFICATION_MODERATE)); + } + else if (nFortification == FORTIFICATION_HEAVY) + { + // Apply immunity properties permenently + if(DEBUG) DoDebug("Applying Heavy Fortification"); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS)); + IPSafeAddItemProperty(oHide, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB)); + } +} + + +// wrapper for DecrementRemainingSpellUses, works for newspellbook 'fake' spells too +// should also find and decrement metamagics for newspellbooks +void PRCDecrementRemainingSpellUses(object oCreature, int nSpell) +{ + if (!UseNewSpellBook(oCreature) && GetHasSpell(nSpell, oCreature)) + { + DecrementRemainingSpellUses(oCreature, nSpell); + return; + } + + int nClass, nSpellbookID, nCount, nMeta, i, j; + int nSpellbookType, nSpellLevel; + string sFile, sFeat; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oCreature); + sFile = GetFileForClass(nClass); + nSpellbookType = GetSpellbookTypeForClass(nClass); + nSpellbookID = RealSpellToSpellbookID(nClass, nSpell); + nMeta = RealSpellToSpellbookIDCount(nClass, nSpell); + if (nSpellbookID != -1) + { //non-spellbook classes should return -1 + for(j = nSpellbookID; j <= nSpellbookID + nMeta; j++) + { + sFeat = Get2DACache(sFile, "ReqFeat", j); + if(sFeat != "") + { + if(!GetHasFeat(StringToInt(sFeat), oCreature)) + continue; + } + nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j)); + + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j); + if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + persistant_array_set_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j, nCount - 1); + return; + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + persistant_array_set_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel, nCount - 1); + return; + } + } + } + } + } + if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: Spell " + IntToString(nSpell) + " not found"); +} + +// +// This function determines if spell damage is elemental +// +int IsSpellDamageElemental(int nDamageType) +{ + return nDamageType & 2480;// 2480 = (DAMAGE_TYPE_ACID | DAMAGE_TYPE_COLD | DAMAGE_TYPE_ELECTRICAL | DAMAGE_TYPE_FIRE | DAMAGE_TYPE_SONIC) +} + + +// This function converts spell damage into the correct type +// TODO: Change the name to consistent (large churn project). +// +// Updated by: TiredByFirelight +int ChangedElementalDamage(object oCaster, int nDamageType) +{ + + // None of the stuff here works when items are involved + if (GetIsObjectValid(PRCGetSpellCastItem())) return nDamageType; + + int nNewType; + + //eldritch spellweave + if(GetIsObjectValid(GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))) + { + int nEssence = GetLocalInt(oCaster, "BlastEssence"); + + if(nEssence == INVOKE_BRIMSTONE_BLAST) + nNewType = DAMAGE_TYPE_FIRE; + + else if(nEssence == INVOKE_HELLRIME_BLAST) + nNewType = DAMAGE_TYPE_COLD; + + else if(nEssence == INVOKE_UTTERDARK_BLAST) + nNewType = DAMAGE_TYPE_NEGATIVE; + + else if(nEssence == INVOKE_VITRIOLIC_BLAST) + nNewType = DAMAGE_TYPE_ACID; + + //save new type for other functions + if(nNewType) + SetLocalInt(oCaster, "archmage_mastery_elements", nNewType); + } + else + // Check if an override is set + nNewType = GetLocalInt(oCaster, "archmage_mastery_elements"); + + // If so, check if the spell qualifies for a change + if (!nNewType || !IsSpellDamageElemental(nDamageType)) + nNewType = nDamageType; + + if (GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) >= 4 && ((nDamageType == DAMAGE_TYPE_COLD)||nNewType == DAMAGE_TYPE_COLD)) + { + nNewType = DAMAGE_TYPE_CUSTOM8; // Untyped + SendMessageToPC(oCaster, "Piercing Cold - Spell ignores resistance or immunities to cold."); + + } + + return nNewType; +} +/* int ChangedElementalDamage(object oCaster, int nDamageType) +{ + + // None of the stuff here works when items are involved + if (GetIsObjectValid(PRCGetSpellCastItem())) return nDamageType; + + int nNewType; + + //eldritch spellweave + if(GetIsObjectValid(GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))) + { + int nEssence = GetLocalInt(oCaster, "BlastEssence"); + + if(nEssence == INVOKE_BRIMSTONE_BLAST) + nNewType = DAMAGE_TYPE_FIRE; + + else if(nEssence == INVOKE_HELLRIME_BLAST) + nNewType = DAMAGE_TYPE_COLD; + + else if(nEssence == INVOKE_UTTERDARK_BLAST) + nNewType = DAMAGE_TYPE_NEGATIVE; + + else if(nEssence == INVOKE_VITRIOLIC_BLAST) + nNewType = DAMAGE_TYPE_ACID; + + //save new type for other functions + if(nNewType) + SetLocalInt(oCaster, "archmage_mastery_elements", nNewType); + } + else + // Check if an override is set + nNewType = GetLocalInt(oCaster, "archmage_mastery_elements"); + + // If so, check if the spell qualifies for a change + if (!nNewType || !IsSpellDamageElemental(nDamageType)) + nNewType = nDamageType; + + return nNewType; +} + */ +//used in scripts after ChangedElementalDamage() to determine saving throw type +int ChangedSaveType(int nDamageType) +{ + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: return SAVING_THROW_TYPE_ACID; + case DAMAGE_TYPE_COLD: return SAVING_THROW_TYPE_COLD; + case DAMAGE_TYPE_ELECTRICAL: return SAVING_THROW_TYPE_ELECTRICITY; + case DAMAGE_TYPE_FIRE: return SAVING_THROW_TYPE_FIRE; + case DAMAGE_TYPE_SONIC: return SAVING_THROW_TYPE_SONIC; + case DAMAGE_TYPE_DIVINE: return SAVING_THROW_TYPE_DIVINE; + case DAMAGE_TYPE_NEGATIVE: return SAVING_THROW_TYPE_NEGATIVE; + case DAMAGE_TYPE_POSITIVE: return SAVING_THROW_TYPE_POSITIVE; + } + return SAVING_THROW_TYPE_NONE;//if it ever gets here, than the function was used incorrectly +} + +// this is possibly used in variations elsewhere +int PRCGetElementalDamageType(int nDamageType, object oCaster = OBJECT_SELF) +{ + switch(nDamageType) + { + case DAMAGE_TYPE_ACID: + case DAMAGE_TYPE_COLD: + case DAMAGE_TYPE_ELECTRICAL: + case DAMAGE_TYPE_FIRE: + case DAMAGE_TYPE_SONIC: + nDamageType = ChangedElementalDamage(oCaster, nDamageType); + } + return nDamageType; +} + +int GetHasBaneMagic(int nRace) +{ + switch(nRace) + { + case RACIAL_TYPE_ABERRATION: return GetHasFeat(FEAT_BANE_MAGIC_ABERRATION); + case RACIAL_TYPE_ANIMAL: return GetHasFeat(FEAT_BANE_MAGIC_ANIMAL); + case RACIAL_TYPE_BEAST: return GetHasFeat(FEAT_BANE_MAGIC_BEAST); + case RACIAL_TYPE_CONSTRUCT: return GetHasFeat(FEAT_BANE_MAGIC_CONSTRUCT); + case RACIAL_TYPE_DRAGON: return GetHasFeat(FEAT_BANE_MAGIC_DRAGON); + case RACIAL_TYPE_DWARF: return GetHasFeat(FEAT_BANE_MAGIC_DWARF); + case RACIAL_TYPE_ELEMENTAL: return GetHasFeat(FEAT_BANE_MAGIC_ELEMENTAL); + case RACIAL_TYPE_ELF: return GetHasFeat(FEAT_BANE_MAGIC_ELF); + case RACIAL_TYPE_FEY: return GetHasFeat(FEAT_BANE_MAGIC_FEY); + case RACIAL_TYPE_GIANT: return GetHasFeat(FEAT_BANE_MAGIC_GIANT); + case RACIAL_TYPE_GNOME: return GetHasFeat(FEAT_BANE_MAGIC_GNOME); + case RACIAL_TYPE_HALFELF: return GetHasFeat(FEAT_BANE_MAGIC_HALFELF); + case RACIAL_TYPE_HALFLING: return GetHasFeat(FEAT_BANE_MAGIC_HALFLING); + case RACIAL_TYPE_HALFORC: return GetHasFeat(FEAT_BANE_MAGIC_HALFORC); + case RACIAL_TYPE_HUMAN: return GetHasFeat(FEAT_BANE_MAGIC_HUMAN); + case RACIAL_TYPE_HUMANOID_GOBLINOID: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_GOBLINOID); + case RACIAL_TYPE_HUMANOID_MONSTROUS: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_MONSTROUS); + case RACIAL_TYPE_HUMANOID_ORC: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_ORC); + case RACIAL_TYPE_HUMANOID_REPTILIAN: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_REPTILIAN); + case RACIAL_TYPE_MAGICAL_BEAST: return GetHasFeat(FEAT_BANE_MAGIC_MAGICAL_BEAST); + case RACIAL_TYPE_OUTSIDER: return GetHasFeat(FEAT_BANE_MAGIC_OUTSIDER); + case RACIAL_TYPE_SHAPECHANGER: return GetHasFeat(FEAT_BANE_MAGIC_SHAPECHANGER); + case RACIAL_TYPE_UNDEAD: return GetHasFeat(FEAT_BANE_MAGIC_UNDEAD); + case RACIAL_TYPE_VERMIN: return GetHasFeat(FEAT_BANE_MAGIC_VERMIN); + } + return FALSE; +} + +void DoPiercingCold(object oCaster, object oTarget, int nDamageAmount, int nCurrentHP) +{ + // Get change in HP from spell damage being applied + int nTest = nCurrentHP - GetCurrentHitPoints(oTarget); + // If there's no damage resistance, nTest and nDamageAmount should be equal + if (nDamageAmount > nTest) + { + // Apply the difference to ignore resist + effect eDam = EffectDamage(nDamageAmount - nTest, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + effect eVis = EffectVisualEffect(VFX_IMP_FROST_L); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + //FloatingTextStringOnCreature("Piercing Cold Triggered", oCaster, FALSE); + } +} + +void BreakDR(object oCaster, object oTarget, int nDamageAmount, int nCurrentHP) +{ + // Get change in HP from spell damage being applied + int nTest = nCurrentHP - GetCurrentHitPoints(oTarget); + // If there's no damage resistance, nTest and nDamageAmount should be equal + if (nDamageAmount > nTest) + { + // Apply the difference to ignore resist + effect eDam = EffectDamage(nDamageAmount - nTest, DAMAGE_TYPE_POSITIVE, DAMAGE_POWER_ENERGY); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget); + } +} + +effect PRCEffectDamage(object oTarget, int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL, int nMetaMagic=METAMAGIC_NONE) +{ + object oCaster = OBJECT_SELF; + + // Incorporeal creatures have a 50% chance of not being hurt by damage other than Magical/Divine/Negative + if (GetLocalInt(oTarget, "Incorporeal") && nDamageType != DAMAGE_TYPE_MAGICAL && nDamageType != DAMAGE_TYPE_NEGATIVE && nDamageType != DAMAGE_TYPE_DIVINE) + { + if (d2() == 1) // 50% chance + { + if (GetIsPC(oTarget)) + FloatingTextStringOnCreature("Spell missed due to Incorporeality", oTarget, FALSE); + effect eEffect; + return eEffect; // Null return + } + } + + // None of the stuff here works when items are involved + if (!GetIsObjectValid(PRCGetSpellCastItem())) + { + if(PRCGetLastSpellCastClass(oCaster) == CLASS_TYPE_WARMAGE && !GetLocalInt(oTarget, "WarmageEdgeDelay")) + { + // Warmage Edge + nDamageAmount += GetAbilityModifier(ABILITY_INTELLIGENCE); + if(GetHasFeat(FEAT_TYPE_EXTRA_EDGE)) + { + // Extra Edge feat. + nDamageAmount += (GetLevelByClass(CLASS_TYPE_WARMAGE) / 4) + 1; + } + SetLocalInt(oTarget, "WarmageEdgeDelay", TRUE); + DelayCommand(0.25, DeleteLocalInt(oTarget, "WarmageEdgeDelay")); + } + + if(GetHasSpellEffect(MELD_ARCANE_FOCUS, oCaster) && !GetLocalInt(oTarget, "ArcaneFocusDelay")) + { + nDamageAmount += 1 + GetEssentiaInvested(oCaster, MELD_ARCANE_FOCUS); + SetLocalInt(oTarget, "ArcaneFocusDelay", TRUE); + DelayCommand(0.25, DeleteLocalInt(oTarget, "ArcaneFocusDelay")); + if (DEBUG) DoDebug("ArcaneFocus Damage Applied"); + } + + // Thrall of Grazzt damage + nDamageAmount += SpellBetrayalDamage(oTarget, oCaster); + + int nRace = MyPRCGetRacialType(oTarget); + + //Bane Magic + if(GetHasBaneMagic(nRace)) + nDamageAmount += d6(2); + + //Eldritch Spellweave + if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET")) + { + //Bane blast essence is active ;) + if(nRace == ((GetLocalInt(oCaster, "EssenceData") >>> 16) & 0xFF) - 1) + { + DeleteLocalObject(oCaster, "SPELLWEAVE_TARGET"); + nDamageAmount += d6(2); + } + } + + // Piercing Evocation + if(GetHasFeat(FEAT_PIERCING_EVOCATION) && GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR") == SPELL_SCHOOL_EVOCATION) + { + // Elemental damage only + if(IsSpellDamageElemental(nDamageType)) + { + // Damage magical, max 10 to magical + if(nDamageAmount > 10) + { + nDamageAmount -= 10; + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(10), oTarget); + } + else + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamageAmount), oTarget); + effect eEffect; + return eEffect; // Null return + } + } + } + + // This is done here so it affects all spells + if(GetLocalInt(oCaster, "Diabolism")) + { + //FloatingTextStringOnCreature("Diabolism is active", oCaster, FALSE); + int iDiabolistLevel = GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster); + DelayCommand(3.0, DeleteLocalInt(oCaster, "Diabolism")); + + if(iDiabolistLevel) + { + int nDice = (iDiabolistLevel + 5) / 5; + int nDamage = d6(nDice); + int nDamageType = DAMAGE_TYPE_DIVINE; + + if(GetLocalInt(oCaster, "VileDiabolism")) + { + //FloatingTextStringOnCreature("Vile Diabolism is active", oCaster, FALSE); + nDamage /= 2; + nDamageType = DAMAGE_TYPE_POSITIVE; + DeleteLocalInt(oCaster, "VileDiabolism"); + } + + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage, nDamageType), oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_LOS_EVIL_10), oTarget); + } + } + + //:: Get the current spell being cast + int nCurrentSpell = PRCGetSpellId(); + + //:: Skip Reserve Feat spellIDs + if (nCurrentSpell < 19359 || nCurrentSpell > 19396) + { + //:: Piercing Cold for the Frost Mage + if (GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) >= 4 && nDamageType == DAMAGE_TYPE_COLD) + { + int nCurrentHP = GetCurrentHitPoints(oTarget); + DelayCommand(0.1, DoPiercingCold(oCaster, oTarget, nDamageAmount, nCurrentHP)); + } + } + +/* // Piercing Cold for the Frost Mage + if (GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) >= 4 && nDamageType == DAMAGE_TYPE_COLD) + { + int nCurrentHP = GetCurrentHitPoints(oTarget); + DelayCommand(0.1, DoPiercingCold(oCaster, oTarget, nDamageAmount, nCurrentHP)); + } */ + + // Die DR die + if (GetLocalInt(oCaster, "MoveIgnoreDR")) + { + int nCurrentHP = GetCurrentHitPoints(oTarget); + DelayCommand(0.1, BreakDR(oCaster, oTarget, nDamageAmount, nCurrentHP)); + } + } + + // Frostrager heals on cold damage when raging. 1 heal for every 2 cold damage. + if (GetLocalInt(oTarget, "Frostrage") && nDamageType == DAMAGE_TYPE_COLD) + { + nDamageAmount /= 2; + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nDamageAmount), oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FROST_L), oTarget); + + FloatingTextStringOnCreature("Absorb Cold healed " + IntToString(nDamageAmount), oTarget, FALSE); + effect eEffect; + return eEffect; //Doesn't hurt him + } + // Phoenix Belt gains fast healing when hit by fire damage + if (GetHasSpellEffect(MELD_PHOENIX_BELT, oTarget) && nDamageType == DAMAGE_TYPE_FIRE) + { + int nEssentia = GetEssentiaInvested(oTarget, MELD_PHOENIX_BELT); + int nResist = nEssentia * 5; + int nDur; + if (nDamageAmount >= nResist) nDur = nResist; + else nDur = nDamageAmount; + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectRegenerate(1, 6.0), oTarget, RoundsToSeconds(nDur+1)); + } + + return EffectDamage(nDamageAmount, nDamageType, nDamagePower); +} + +// * Kovi. removes any effects from this type of spell +// * i.e., used in Mage Armor to remove any previous +// * mage armors +void PRCRemoveEffectsFromSpell(object oTarget, int SpellID) +{ + effect eLook = GetFirstEffect(oTarget); + while(GetIsEffectValid(eLook)) + { + if(GetEffectSpellId(eLook) == SpellID) + RemoveEffect(oTarget, eLook); + + eLook = GetNextEffect(oTarget); + } +} + +void PRCRemoveSpecificEffect(int nEffectTypeID, object oTarget) +{ + //Search through the valid effects on the target. + effect eAOE = GetFirstEffect(oTarget); + while (GetIsEffectValid(eAOE)) + { + if (GetEffectType(eAOE) == nEffectTypeID) + { + //If the effect was created by the spell then remove it + RemoveEffect(oTarget, eAOE); + } + //Get next effect on the target + eAOE = GetNextEffect(oTarget); + } +} + +effect PRCGetScaledEffect(effect eStandard, object oTarget) +{ + int nDiff = GetGameDifficulty(); + int nEffType = GetEffectType(eStandard); + + if(GetIsPC(oTarget) || GetIsPC(GetMaster(oTarget))) + { + if(nEffType == EFFECT_TYPE_FRIGHTENED) + { + if(nDiff == GAME_DIFFICULTY_VERY_EASY) + { + return EffectAttackDecrease(-2); + } + else if(nDiff == GAME_DIFFICULTY_EASY) + { + return EffectAttackDecrease(-4); + } + } + if(nDiff == GAME_DIFFICULTY_VERY_EASY && + (nEffType == EFFECT_TYPE_PARALYZE || + nEffType == EFFECT_TYPE_STUNNED || + nEffType == EFFECT_TYPE_CONFUSED)) + { + return EffectDazed(); + } + if(nEffType == EFFECT_TYPE_CHARMED + || nEffType == EFFECT_TYPE_DOMINATED) + { + return EffectDazed(); + } + } + return eStandard; +} + +int PRCAmIAHumanoid(object oTarget) +{ + int nRacial = MyPRCGetRacialType(oTarget); + if(nRacial == RACIAL_TYPE_DWARF + || nRacial == RACIAL_TYPE_HALFELF + || nRacial == RACIAL_TYPE_HALFORC + || nRacial == RACIAL_TYPE_ELF + || nRacial == RACIAL_TYPE_GNOME + || nRacial == RACIAL_TYPE_HUMANOID_GOBLINOID + || nRacial == RACIAL_TYPE_HALFLING + || nRacial == RACIAL_TYPE_HUMAN + || nRacial == RACIAL_TYPE_HUMANOID_MONSTROUS + || nRacial == RACIAL_TYPE_HUMANOID_ORC + || nRacial == RACIAL_TYPE_HUMANOID_REPTILIAN) + { + return TRUE; + } + return FALSE; +} + +int PRCGetScaledDuration(int nActualDuration, object oTarget) +{ + int nDiff = GetGameDifficulty(); + int nNew = nActualDuration; + if(GetIsPC(oTarget) && nActualDuration > 3) + { + if(nDiff == GAME_DIFFICULTY_VERY_EASY + || nDiff == GAME_DIFFICULTY_EASY) + { + nNew = nActualDuration / 4; + } + else if(nDiff == GAME_DIFFICULTY_NORMAL) + { + nNew = nActualDuration / 2; + } + if(nNew == 0) + { + nNew = 1; + } + } + return nNew; +} + +effect PRCCreateProtectionFromAlignmentLink(int nAlignment, int nPower = 1) +{ + int nFinal = nPower * 2; + int nAlignmentLC; + int nAlignmentGE; + effect eDur; + + switch(nAlignment) + { + case ALIGNMENT_LAWFUL:{ + nAlignmentLC = ALIGNMENT_LAWFUL; + nAlignmentGE = ALIGNMENT_ALL; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR); + break;} + case ALIGNMENT_CHAOTIC:{ + nAlignmentLC = ALIGNMENT_CHAOTIC; + nAlignmentGE = ALIGNMENT_ALL; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR); + break;} + case ALIGNMENT_GOOD:{ + nAlignmentLC = ALIGNMENT_ALL; + nAlignmentGE = ALIGNMENT_GOOD; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR); + break;} + case ALIGNMENT_EVIL:{ + nAlignmentLC = ALIGNMENT_ALL; + nAlignmentGE = ALIGNMENT_EVIL; + eDur = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR); + break;} + } + + //construct final effect + effect eLink = EffectACIncrease(nFinal, AC_DEFLECTION_BONUS); + eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_ALL, nFinal)); + eLink = EffectLinkEffects(eLink, EffectImmunity(IMMUNITY_TYPE_MIND_SPELLS)); + //make it vs alignment + eLink = VersusAlignmentEffect(eLink, nAlignmentLC, nAlignmentGE); + //add duration vfx + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE)); + eLink = EffectLinkEffects(eLink, eDur); + + return eLink; +} + +float PRCGetSpellEffectDelay(location SpellTargetLocation, object oTarget) +{ + float fDelay = GetDistanceBetweenLocations(SpellTargetLocation, GetLocation(oTarget))/20; + return fDelay; +} + +// * returns true if the creature has flesh +int PRCIsImmuneToPetrification(object oCreature) +{ + int nAppearance = GetAppearanceType(oCreature); + int bImmune = FALSE; + + if (GetHasSpellEffect(VESTIGE_HAAGENTI, oCreature) && GetLocalInt(oCreature, "ExploitVestige") != VESTIGE_HAAGENTI_IMMUNE_TRANS) bImmune = TRUE; + + 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 + bImmune = TRUE; + } + + int nRacialType = MyPRCGetRacialType(oCreature); + switch(nRacialType) + { + case RACIAL_TYPE_ELEMENTAL: + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_OOZE: + case RACIAL_TYPE_UNDEAD: + bImmune = TRUE; + } + + // 01/08/07 Racial feat for petrification immunity + if(GetHasFeat(FEAT_IMMUNE_PETRIFICATION)) bImmune = TRUE; + + // 03/07/2005 CraigW - Petrification immunity can also be granted as an item property. + if ( ResistSpell(OBJECT_SELF,oCreature) == 2 ) + { + bImmune = TRUE; + } + + // * GZ: Sept 2003 - Prevent people from petrifying DM, resulting in GUI even when + // effect is not successful. + if (!GetPlotFlag(oCreature) && GetIsDM(oCreature)) + { + bImmune = FALSE; + } + return bImmune; +} + +// * This is a wrapper for how Petrify will work in Expansion Pack 1 +// * Scripts affected: flesh to stone, breath petrification, gaze petrification, touch petrification +// * nPower : This is the Hit Dice of a Monster using Gaze, Breath or Touch OR it is the Caster Spell of +// * a spellcaster +// * nFortSaveDC: pass in this number from the spell script +void PRCDoPetrification(int nPower, object oSource, object oTarget, int nSpellID, int nFortSaveDC) +{ + + if(!GetIsReactionTypeFriendly(oTarget)) + { + // * exit if creature is immune to petrification + if(PRCIsImmuneToPetrification(oTarget)) + return; + + float fDifficulty = 0.0; + int bIsPC = GetIsPC(oTarget); + int bShowPopup = FALSE; + + // * calculate Duration based on difficulty settings + int nGameDiff = GetGameDifficulty(); + switch (nGameDiff) + { + case GAME_DIFFICULTY_VERY_EASY: + case GAME_DIFFICULTY_EASY: + case GAME_DIFFICULTY_NORMAL: + fDifficulty = RoundsToSeconds(nPower); // One Round per hit-die or caster level + break; + case GAME_DIFFICULTY_CORE_RULES: + case GAME_DIFFICULTY_DIFFICULT: + bShowPopup = TRUE; + break; + } + + int nSaveDC = nFortSaveDC; + effect ePetrify = EffectPetrify(); + + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + + effect eLink = EffectLinkEffects(eDur, ePetrify); + + // Let target know the negative spell has been cast + SignalEvent(oTarget, + EventSpellCastAt(OBJECT_SELF, nSpellID)); + //SpeakString(IntToString(nSpellID)); + + // Do a fortitude save check + if (!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC)) + { + // Save failed; apply paralyze effect and VFX impact + + /// * The duration is permanent against NPCs but only temporary against PCs + if (bIsPC == TRUE) + { + if (bShowPopup == TRUE) + { + // * under hardcore rules or higher, this is an instant death + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget); + //only pop up death panel if switch is not set + if(!GetPRCSwitch(PRC_NO_PETRIFY_GUI)) + DelayCommand(2.75, PopUpDeathGUIPanel(oTarget, FALSE , TRUE, 40579)); + //run the PRC Ondeath code + //no way to run the normal module ondeath code too + //so a execute script has been added for builders to take advantage of + DelayCommand(2.75, ExecuteScript("prc_ondeath", oTarget)); + DelayCommand(2.75, ExecuteScript("prc_pw_petrific", oTarget)); + // if in hardcore, treat the player as an NPC + bIsPC = FALSE; + //fDifficulty = TurnsToSeconds(nPower); // One turn per hit-die + } + else + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDifficulty); + } + else + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget); + } + // April 2003: Clearing actions to kick them out of conversation when petrified + AssignCommand(oTarget, ClearAllActions(TRUE)); + } + } + +} + +//------------------------------------------------------------------------------ +// GZ: 2003-Oct-15 +// A different approach for timing these spells that has the positive side +// effects of making the spell dispellable as well. +// I am using the VFX applied by the spell to track the remaining duration +// instead of adding the remaining runtime on the stack +// +// This function returns FALSE if a delayed Spell effect from nSpell_ID has +// expired. See x2_s0_bigby4.nss for details +//------------------------------------------------------------------------------ +int PRCGetDelayedSpellEffectsExpired(int nSpell_ID, object oTarget, object oCaster) +{ + + if (!GetHasSpellEffect(nSpell_ID,oTarget) ) + { + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID)); + return TRUE; + } + + //-------------------------------------------------------------------------- + // GZ: 2003-Oct-15 + // If the caster is dead or no longer there, cancel the spell, as it is + // directed + //-------------------------------------------------------------------------- + if( !GetIsObjectValid(oCaster)) + { + GZPRCRemoveSpellEffects(nSpell_ID, oTarget); + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID)); + return TRUE; + } + + if (GetIsDead(oCaster)) + { + DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID)); + GZPRCRemoveSpellEffects(nSpell_ID, oTarget); + return TRUE; + } + + return FALSE; + +} + +// Much similar to PRCGetHasSpell, but used for JPM to get spells left not counting metamagic +int PRCGetSpellUsesLeft(int nRealSpellID, object oCreature = OBJECT_SELF) +{ + if(!PRCGetIsRealSpellKnown(nRealSpellID, oCreature)) + return 0; + int nUses = GetHasSpell(nRealSpellID, oCreature); + + int nClass, nSpellbookID, nCount, i, j; + int nSpellbookType, nSpellLevel; + string sFile, sFeat; + for(i = 1; i <= 8; i++) + { + nClass = GetClassByPosition(i, oCreature); + sFile = GetFileForClass(nClass); + nSpellbookType = GetSpellbookTypeForClass(nClass); + nSpellbookID = RealSpellToSpellbookID(nClass, nRealSpellID); + if (nSpellbookID != -1) + { //non-spellbook classes should return -1 + sFeat = Get2DACache(sFile, "ReqFeat", j); + if(sFeat != "") + { + if(!GetHasFeat(StringToInt(sFeat), oCreature)) + continue; + } + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j); + if(DEBUG) DoDebug("PRCGetHasSpell(Prepared Caster): NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j)); + nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel); + if(DEBUG) DoDebug("PRCGetHasSpell(Spontaneous Caster): NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount)); + if(nCount > 0) + { + nUses += nCount; + } + } + } + } + + if(DEBUG) DoDebug("PRCGetHasSpell: RealSpellID = " + IntToString(nRealSpellID) + ", Uses = " + IntToString(nUses)); + return nUses; +} + +// * Applies the effects of FEAT_AUGMENT_SUMMON to summoned creatures. +void AugmentSummonedCreature(string sResRef) +{ + if(GetHasFeat(FEAT_AUGMENT_SUMMON)) + { + int i = 1; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF); + while(GetIsObjectValid(oSummon)) + { + if(GetResRef(oSummon) == sResRef && !GetLocalInt(oSummon, "Augmented")) + { + effect eLink = EffectAbilityIncrease(ABILITY_STRENGTH, 4); + eLink = EffectLinkEffects(eLink, EffectAbilityIncrease(ABILITY_CONSTITUTION, 4)); + eLink = UnyieldingEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oSummon); + SetLocalInt(oSummon, "Augmented", TRUE); + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i); + } + } + if(sResRef == "prc_sum_treant") + { + int i = 1; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF); + while(GetIsObjectValid(oSummon)) + { + if(GetResRef(oSummon) == sResRef) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_PROT_BARKSKIN), oSummon); + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i); + } + } + if(GetHasFeat(FEAT_BECKON_THE_FROZEN)) + { + int i = 1; + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF); + while(GetIsObjectValid(oSummon)) + { + if(GetResRef(oSummon) == sResRef && !GetLocalInt(oSummon, "BeckonTheFrozen")) + { + effect eLink = EffectVisualEffect(VFX_DUR_CHILL_SHIELD); + eLink = EffectLinkEffects(eLink, EffectDamageImmunityDecrease(DAMAGE_TYPE_FIRE, 50)); + eLink = EffectLinkEffects(eLink, EffectDamageImmunityIncrease(DAMAGE_TYPE_COLD, 100)); + eLink = EffectLinkEffects(eLink, EffectDamageIncrease(DAMAGE_BONUS_1d6, DAMAGE_TYPE_COLD)); + eLink = UnyieldingEffect(eLink); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oSummon); + SetLocalInt(oSummon, "BeckonTheFrozen", TRUE); + } + i++; + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i); + } + } +} + +object GetAreaOfEffectObject(location lTarget, string sTag, object oCaster = OBJECT_SELF) +{ + object oAoE = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + while(GetIsObjectValid(oAoE)) + { + if((GetAreaOfEffectCreator(oAoE) == oCaster) //was created by oCaster + && GetTag(oAoE) == sTag //has required tag + && !GetLocalInt(oAoE, "X2_AoE_BaseSaveDC")) //and wasn't setup before + { + return oAoE; + } + oAoE = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + } + return OBJECT_INVALID; +} + +string GetAreaOfEffectTag(int nAoE) +{ + return Get2DACache("vfx_persistent", "LABEL", nAoE); +} + +int CheckTurnUndeadUses(object oPC, int nUses) +{ + int i; + while(i < nUses) + { + if(GetHasFeat(FEAT_TURN_UNDEAD, oPC)) + { + DecrementRemainingFeatUses(oPC, FEAT_TURN_UNDEAD); + i++; + } + else + break; + } + if(i < nUses) + { + while(i) + { + IncrementRemainingFeatUses(oPC, FEAT_TURN_UNDEAD); + i--; + } + return FALSE; + } + return TRUE; +} + +// this will execute the prespellcastcode, whose full functionality is incoded in X2PreSpellCastCode2(), +// as a script, to save loading time for spells scripts and reduce memory usage of NWN +// the prespellcode takes up roughly 250 kByte compiled code, meaning that every spell script that +// calls it directly as a function (e.g.: X2PreSpellCastCode2) will be between 100 kByte to 250 kByte +// larger, than a spell script calling the prespellcode via ExecuteScript (e.g. X2PreSpellCastCode) +// Although ExecuteScript is slightly slower than a direct function call, quite likely overall performance is +// increased, because for every new spell 100-250 kByte less code need to be loaded into memory +// and NWN has more free memory available to keep more spells scripts (and other crucial scripts) +//in RAM + +int X2PreSpellCastCode() +{ + object oCaster = OBJECT_SELF; + + // SetLocalInt(oCaster, "PSCC_Ret", 0); + ExecuteScript("prc_prespell", oCaster); + + int nReturn = GetLocalInt(oCaster, "PSCC_Ret"); + // DeleteLocalInt(oCaster, "PSCC_Ret"); + + return nReturn; +} + +//:: Test Void +// void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_stunfist.nss b/src/include/prc_inc_stunfist.nss new file mode 100644 index 0000000..748367f --- /dev/null +++ b/src/include/prc_inc_stunfist.nss @@ -0,0 +1,191 @@ +///////////////////////////////////////////////////////////////// +// notes: +// "normal" stunning fist uses are what is normally given, not taking into account the PrC +// "extra" stunning fist uses are what uses that come from PrC classes, they can be converted into "normal" uses and dont do anything by themselves +// this is to work around the hardcoded limit of the "normal" stunning fist uses + +// Try to expend a number of stunning fist uses, returns true if succesfull +int ExpendStunfistUses(object oPC, int nUses); + +// Reset extra stunning fist uses to the extra uses/day a character has (use on rest) +void ResetExtraStunfistUses(object oPC); + +// Get remaining "normal" stunning fist uses +int GetNormalRemainingStunfistUses(object oPC); + +// Get remaining "extra" stunning fist uses +int GetExtraRemainingStunfistUses(object oPC); + +// Get total remaining stunning fist uses +int GetTotalRemainingStunfistUses(object oPC); + +// Get amount of "normal" stunning fist uses/day a character has +int GetNormalStunfistUsesPerDay(object oPC); + +// Get amount of "extra" stunning fist uses/day a character has +int GetExtraStunfistUsesPerDay(object oPC); + +// Get total amount of stunning fist uses/day a character has +int GetTotalStunFistUsesPerDay(object oPC); + +// Set remaining "normal" stunning fist uses +void SetNormalRemainingStunfistUses(object oPC, int nUses); + +// Set remaining "extra" stunning fist uses +void SetExtraRemainingStunfistUses(object oPC, int nUses); + +// Convert "extra" stunning fist uses to "normal" (BW) stunning fist uses +void ConvertStunFistUses(object oPC); + +//================================ + +// Minimalist includes + +#include "prc_feat_const" +#include "prc_class_const" +#include "inc_item_props" +#include "prc_ipfeat_const" +//#include "prc_alterations" + +// Try to expend a number of stunning fist uses, returns true if succesfull +int ExpendStunfistUses(object oPC, int nUses) +{ + if (GetTotalRemainingStunfistUses(oPC) < nUses) + { + SendMessageToPC(oPC, "You don't have enough uses for this feat!"); + return FALSE; + } + + ConvertStunFistUses(oPC); + + while (nUses) + { + nUses--; + DecrementRemainingFeatUses(oPC, FEAT_STUNNING_FIST); + ConvertStunFistUses(oPC); + } + + return TRUE; +} + +// Reset extra stunning fist uses to the extra uses/day a character has (use on rest) +void ResetExtraStunfistUses(object oPC) +{ + object oSkin = GetPCSkin(oPC); + int nUses = GetExtraStunfistUsesPerDay(oPC); + + if (nUses && !GetLocalInt(oPC, "PRCExtraStunningMessage")) + { + SetLocalInt(oPC, "PRCExtraStunningMessage", TRUE); + DelayCommand(3.001f, SendMessageToPC(oPC, "You gained extra stunning fist uses per day, use the feat 'PrC Extra Stunning'")); + DelayCommand(3.002f, SendMessageToPC(oPC, "to convert those uses into normal stunning fist uses")); + } + + SetExtraRemainingStunfistUses(oPC, nUses); +} + +void ConvertStunFistUses(object oPC) +{ + while (GetNormalRemainingStunfistUses(oPC) < GetNormalStunfistUsesPerDay(oPC) && GetHasFeat(FEAT_PRC_EXTRA_STUNNING, oPC)) + { + IncrementRemainingFeatUses(oPC, FEAT_STUNNING_FIST); + DecrementRemainingFeatUses(oPC, FEAT_PRC_EXTRA_STUNNING); + } +} + +// Get remaining "normal" stunning fist uses +int GetNormalRemainingStunfistUses(object oPC) +{ + int nUses = 0; + while (GetHasFeat(FEAT_STUNNING_FIST, oPC)) + { + nUses++; + DecrementRemainingFeatUses(oPC, FEAT_STUNNING_FIST); + } + + SetNormalRemainingStunfistUses(oPC, nUses); + return nUses; +} + +// Get remaining "extra" stunning fist uses +int GetExtraRemainingStunfistUses(object oPC) +{ + int nUses = 0; + while (GetHasFeat(FEAT_PRC_EXTRA_STUNNING, oPC)) + { + nUses++; + DecrementRemainingFeatUses(oPC, FEAT_PRC_EXTRA_STUNNING); + } + + SetExtraRemainingStunfistUses(oPC, nUses); + + return nUses; +} + +// Get total remaining stunning fist uses +int GetTotalRemainingStunfistUses(object oPC) +{ + return GetNormalRemainingStunfistUses(oPC) + GetExtraRemainingStunfistUses(oPC); +} + +// Get amount of "normal" stunning fist uses/day a character has +int GetNormalStunfistUsesPerDay(object oPC) +{ + int nUses = GetNormalRemainingStunfistUses(oPC); + int nMaxUses = nUses; + while (nMaxUses == GetNormalRemainingStunfistUses(oPC)) + { + IncrementRemainingFeatUses(oPC, FEAT_STUNNING_FIST); + nMaxUses++; + } + nMaxUses--; + SetNormalRemainingStunfistUses(oPC, nUses); + return nMaxUses; +} + +// Get amount of "extra" stunning fist uses/day a character has +int GetExtraStunfistUsesPerDay(object oPC) +{ + int nUses = 0; + + // classes/effects which give extra stunning fist uses/day should be added here + nUses += GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oPC); + nUses += GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oPC); + nUses += GetLocalInt(oPC, "LiPengStun"); + + return nUses; +} + +// Get total amount of stunning fist uses/day a character has +int GetTotalStunFistUsesPerDay(object oPC) +{ + return GetNormalStunfistUsesPerDay(oPC) + GetExtraStunfistUsesPerDay(oPC); +} + +// Set remaining "normal" stunning fist uses +void SetNormalRemainingStunfistUses(object oPC, int nUses) +{ + while (GetHasFeat(FEAT_STUNNING_FIST, oPC)) + { + DecrementRemainingFeatUses(oPC, FEAT_STUNNING_FIST); + } + + while (nUses--) + { + IncrementRemainingFeatUses(oPC, FEAT_STUNNING_FIST); + } +} + +// Set remaining "extra" stunning fist uses +void SetExtraRemainingStunfistUses(object oPC, int nUses) +{ + while (GetHasFeat(FEAT_PRC_EXTRA_STUNNING, oPC)) + { + DecrementRemainingFeatUses(oPC, FEAT_PRC_EXTRA_STUNNING); + } + + while (nUses--) + { + IncrementRemainingFeatUses(oPC, FEAT_PRC_EXTRA_STUNNING); + } +} \ No newline at end of file diff --git a/src/include/prc_inc_switch.nss b/src/include/prc_inc_switch.nss new file mode 100644 index 0000000..4913e3d --- /dev/null +++ b/src/include/prc_inc_switch.nss @@ -0,0 +1,2969 @@ +/** + * @file + * This file defines names of switches that can be used to modify + * the behaviour of certain parts of the PRC pack. + * It also contains functions for getting and setting the values of + * these switches and in addition some functions dealing with the + * implementation of certain switches. + */ + + /* + + Creating your personal switch settings + For singleplayer, you can create a 2da file and place it in the overide + Then via the PRC Options switch you can read that 2da and it will + use it to set switches for you. + This will not work in multiplayer. + An example is below. Copy and paste it into a plain text file saved + as personal_switch.2da + + If there is a file named personal_switch.2da then it will be loaded + at module load and the switches set accordingly. + + +2DA V2.0 + + SwitchName SwitchType SwitchValue +0 FOO float 3.14159 +1 BAR int 12321 +2 BAZ string "Go For The Eyes Boo, Go For The Eyes!" +3 PRC_PNP_TRUESEEING int 1 +4 PRC_TIMESTOP_LOCAL int 1 +5 PRC_TIMESTOP_NO_HOSTILE int 1 +6 PRC_TIMESTOP_BLANK_PC int 1 +7 PRC_PNP_ELEMENTAL_SWARM int 1 +8 PRC_PNP_TENSERS_TRANSFORMATION int 1 +9 PRC_PNP_BLACK_BLADE_OF_DISASTER int 1 +10 PRC_PNP_FIND_TRAPS int 1 +11 PRC_PNP_DARKNESS int 1 +12 PRC_PNP_DARKNESS_35ED int 1 +13 PRC_PNP_ANIMATE_DEAD int 1 +14 PRC_35ED_WORD_OF_FAITH int 1 +15 PRC_CREATE_UNDEAD_UNCONTROLLED int 1 +16 PRC_CREATE_UNDEAD_PERMANENT int 1 +17 PRC_SLEEP_NO_HD_CAP int 1 +18 PRC_USE_NEW_IMBUE_ARROW int 1 +19 PRC_ORC_WARLORD_COHORT int 1 +20 PRC_LICH_ALTER_SELF_DISABLE int 1 +21 PRC_TRUE_NECROMANCER_ALTERNATE_VISUAL int 1 +22 PRC_THRALLHERD_LEADERSHIP int 1 +23 PRC_PNP_UNIMPINGED int 1 +24 PRC_PNP_IMPENETRABILITY int 1 +25 PRC_PNP_DULLBLADES int 1 +26 PRC_PNP_CHAMPIONS_VALOR int 1 +27 PRC_STAFF_CASTER_LEVEL int 1 +28 PRC_PNP_ABILITY_DAMAGE_EFFECTS int 1 +29 PRC_PNP_REST_HEALING int 1 +30 PRC_PNP_SOMATIC_COMPOMENTS int 1 +31 PRC_PNP_SOMATIC_ITEMS int 1 +32 PRC_MULTISUMMON int 1 +33 PRC_SUMMON_ROUND_PER_LEVEL int 1 +34 PRC_PNP_FAMILIAR_FEEDING int 1 +35 PRC_PNP_HOLY_AVENGER_IPROP int 1 +36 PRC_PNP_SLINGS int 1 +37 PRC_PNP_RACIAL_SPEED int 1 +38 PRC_PNP_ARMOR_SPEED int 1 +39 PRC_REMOVE_PLAYER_SPEED int 1 +40 PRC_BREW_POTION_CASTER_LEVEL int 1 +41 PRC_SCRIBE_SCROLL_CASTER_LEVEL int 1 +42 PRC_CRAFT_WAND_CASTER_LEVEL int 1 +43 PRC_CRAFTING_BASE_ITEMS int 1 +44 PRC_XP_USE_SIMPLE_LA int 1 +45 PRC_XP_USE_SIMPLE_RACIAL_HD int 1 + */ + + /* This variable MUST be updated with every new version of the PRC!!! */ + + const string PRC_VERSION = "PRC 3.9.0"; + + /* This variable MUST be updated every time 'assemble_spellbooks.bat' is run!!! */ + + const string AMS_VERSION = "AMS_1.7.6"; + +/******************************************************************************\ +* Spell switches * +\******************************************************************************/ + +/** Material Components + * Set switch to 1 to activate this + * This allows material components in NWN through the materialcomp.2da + * Just put the SpellID and UTC resref in, MINUS the .utc on the end. + * This also requires the names of the items, formatted like so ("" included): "Object Name" + * + * Set switch to 2 to activate this + * Deducts gold instead of requiring material components + * Put the gold value in the Cost column + + * Set switch to 3 to activate both at the same time + * WARNING: This will slow spellcasting down due to 2da reads and inventory loops + */ +const string PRC_MATERIAL_COMPONENTS = "PRC_MATERIAL_COMPONENTS"; + +const string PRC_DISABLE_COMPONENTS_SHOP = "PRC_DISABLE_COMPONENTS_SHOP"; + +/** Bioware True Seeing can see stealthed creatures. + * This replaces the trueseeing effect with a See Invisible + Ultravision + Spot bonus. + * This affects the spell and power True Seeing and the Dragon Disciple class + */ +const string PRC_PNP_TRUESEEING = "PRC_PNP_TRUESEEING"; + +/** + * PRC_PNP_TRUESEEING must be on. + * Value of spot skill bonus that True Seeing grants. + * Defaults to +15 if not set. + */ +const string PRC_PNP_TRUESEEING_SPOT_BONUS = "PRC_PNP_TRUESEEING_SPOT_BONUS"; + +/** Remove the cap PRC added to this spell */ +const string PRC_BIOWARE_GRRESTORE = "PRC_BIOWARE_GRRESTORE"; +/** Remove the cap PRC added to this spell */ +const string PRC_BIOWARE_HEAL = "PRC_BIOWARE_HEAL"; +/** Remove the cap PRC added to this spell */ +const string PRC_BIOWARE_MASS_HEAL = "PRC_BIOWARE_MASS_HEAL"; +/** Remove the cap PRC added to this spell */ +const string PRC_BIOWARE_HARM = "PRC_BIOWARE_HARM"; +/** Remove the cap PRC added to this spell */ +const string PRC_BIOWARE_NEUTRALIZE_POISON = "PRC_BIOWARE_NEUTRALIZE_POISON"; +/** Remove the cap PRC added to this spell */ +const string PRC_BIOWARE_REMOVE_DISEASE = "PRC_BIOWARE_REMOVE_DISEASE"; + +/** + * This replaces the 3.0 Spell Focus bonuses with the 3.5 edition ones + */ +const string PRC_35_SPELL_FOCUS = "PRC_35_SPELL_FOCUS"; + +/* Blindness/Deafness effect is permanent */ +const string PRC_PNP_BLINDNESS_DEAFNESS = "PRC_PNP_BLINDNESS_DEAFNESS"; + +/** + * Sets caltrops duration in seconds. + * Defaults = permanent + */ +const string PRC_CALTROPS_DURATION = "PRC_CALTROPS_DURATION"; + +/*** + * Timestop has Bioware durations (9 seconds or 18 for Greater Timestop) rather + * than PnP durations (1d4+1 or 2d4+2) + */ +const string PRC_TIMESTOP_BIOWARE_DURATION = "PRC_TIMESTOP_BIOWARE_DURATION"; + +/** + * Timestop has only a local affect, i.e doesn't stop people on the other areas of the module. + * Note that AOEs continue to act during a timestop, and durations/delayed events still occur. + */ +const string PRC_TIMESTOP_LOCAL = "PRC_TIMESTOP_LOCAL"; + +/** + * PRC_TIMESTOP_LOCAL must be enabled. + * Caster can't perform any hostile actions while in timestop. + */ +const string PRC_TIMESTOP_NO_HOSTILE = "PRC_TIMESTOP_NO_HOSTILE"; + +/** + * PRC_TIMESTOP_LOCAL must be enabled. + * PCs can't see anything while stopped. + * This might look to the player like their game crashed. + */ +const string PRC_TIMESTOP_BLANK_PC = "PRC_TIMESTOP_BLANK_PC"; + +/** + * Instead of Bioware's sequential summons it creates multiple elementals. + * Only works if PRC_MULTISUMMON is on + */ +const string PRC_PNP_ELEMENTAL_SWARM = "PRC_PNP_ELEMENTAL_SWARM"; + +/** + * If you pass a save, you can't be affected by that aura for 24h. + * NOTE: Not implemented yet + */ +const string PRC_PNP_FEAR_AURAS = "PRC_PNP_FEAR_AURAS"; + +/** + * Not a polymorph but ability bonuses instead. + */ +const string PRC_PNP_TENSERS_TRANSFORMATION = "PRC_PNP_TENSERS_TRANSFORMATION"; + +/** + * Less powerful, more PnP accurate version. + * Caster must concentrate to maintain it. + */ +const string PRC_PNP_BLACK_BLADE_OF_DISASTER = "PRC_PNP_BLACK_BLADE_OF_DISASTER"; + +/** + * Traps are only shown, not disarmed + */ +const string PRC_PNP_FIND_TRAPS = "PRC_PNP_FIND_TRAPS"; + +/** + * PnP Darkness + * Is a mobile AOE based off an item + */ +const string PRC_PNP_DARKNESS = "PRC_PNP_DARKNESS"; + +/** + * Bioware Invisibility, Improved Invisibility + */ +const string PRC_BIOWARE_INVISIBILITY = "PRC_BIOWARE_INVISIBILITY"; + +/** + * 3.5ed Darkness + * Gives 20% concelement rather than bioware darkness + */ +const string PRC_PNP_DARKNESS_35ED = "PRC_PNP_DARKNESS_35ED"; + +/** + * Undead summons are permanent, but can only have 4HD/casterlevel in total + * Does not enforce the requirement for a corpse + * Also applies to ghoul gauntlet which otherwise will create one ghoul + * if you dont already have a summon + */ +const string PRC_PNP_ANIMATE_DEAD = "PRC_PNP_ANIMATE_DEAD"; + +/** + * This sets the HP limit for Symbol of Death, Symbol of Fear and Symbol of Stunning + * spells. Default = 150. + */ +const string PRC_SYMBOL_HP_LIMIT = "PRC_SYMBOL_HP_LIMIT"; + +/** + * "Word of Faith" spells use 3.5 ed rules rather than 3.0ed + * basically instead of 12+ / <12 / <8 / <4 its relative to caster level + * at >=CL / = 1. + * Default: 1 + */ +const string PRC_USES_PER_WEAPON_POISON_COUNT = "PRC_USES_PER_WEAPON_POISON_COUNT"; + +/** + * Size of the die rolled when determining the amount of hits the poison will + * work on. If this is set, the value should be at least 2. + * Default: Dice aren't rolled. + */ +const string PRC_USES_PER_WEAPON_POISON_DIE = "PRC_USES_PER_WEAPON_POISON_DIE"; + +/** + * This is the name of the script to be run when someone attempts to poison food to + * check if the targeted item is food. The default script returns FALSE for everything, + * so you must define your own to have this functionality. + * + * This switch has string values instead of integers. + * + * Default: poison_is_food <- an example script, just returns false + * + * @see poison_is_food + */ +const string PRC_POISON_IS_FOOD_SCRIPT_NAME = "PRC_POISON_IS_FOOD_SCRIPT_NAME"; + +/** + * This switch determines whether a creature equipping a poisoned item is assumed to be + * acting smartly in that it attempts to clean the item first. If it's not set, the + * creature just directly equips the item and gets poisoned. + * + * Default: Off, the creature gets poisoned without any checks + * + * @see poison_onequip + */ +const string PRC_POISON_ALLOW_CLEAN_IN_EQUIP = "PRC_POISON_ALLOW_CLEAN_IN_EQUIP"; + +/** + * + * Default: crafting requires only gold and xp + */ +const string PRC_CRAFT_POISON_USE_INGREDIENST = "PRC_CRAFT_POISON_USE_INGREDIENST"; + +/******************************************************************************\ +* PRGT system switches * +\******************************************************************************/ + +//these three are strings not switches +const string PRC_PRGT_XP_SCRIPT_TRIGGERED = "PRC_PRGT_XP_SCRIPT_TRIGGERED"; +const string PRC_PRGT_XP_SCRIPT_DISARMED = "PRC_PRGT_XP_SCRIPT_DISARMED"; +const string PRC_PRGT_XP_SCRIPT_RECOVERED = "PRC_PRGT_XP_SCRIPT_RECOVERED"; + +/** + * @TODO: Write description. + */ +const string PRC_PRGT_XP_AWARD_FOR_TRIGGERED = "PRC_PRGT_XP_AWARD_FOR_TRIGGERED"; + +/** + * @TODO: Write description. + */ +const string PRC_PRGT_XP_AWARD_FOR_RECOVERED = "PRC_PRGT_XP_AWARD_FOR_RECOVERED"; + +/** + * @TODO: Write description. + */ +const string PRC_PRGT_XP_AWARD_FOR_DISARMED = "PRC_PRGT_XP_AWARD_FOR_DISARMED"; + + + +/******************************************************************************\ +* Psionics switches * +\******************************************************************************/ + +/** + * If this is set, use ac_appearances.2da to determine an Astral Construct's + * appearance instead of the values hardcoded into the script. + */ +const string PRC_PSI_ASTRAL_CONSTRUCT_USE_2DA = "ASTRAL_CONSTRUCT_USE_2DA"; + +/** + * If this is set, Astral Construct's duration will be multiplied by given + * number. Examples: + * 10 - duration is equal to 1 minute per manifester level + * 600 - duration is equal to 1 hour per manifester level + */ +const string PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD = "PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD"; + + +/** + * If this is set, The Astral Seed power will attempt to use the provided string as + * the ResRef to create the Astral Seed object instead of the of the phylactery + * ResRef("x2_plc_phylact"). + * May be used by builders to create an object that CAN be destroyed, or has other traits, + * as desired. + * Type: String + * Values: "" [Default] (Blank, or not set: Use default phylactery ResRef for Astral Seed) + * STRING (Entered String will be used as the ResRef of created Astral Seed object) + */ +const string PRC_PSI_ASTRAL_SEED_RESREF = "PRC_PSI_ASTRAL_SEED_RESREF"; + +/** + * By default the Astral Seed power respawns the player, and then makes them immobile for + * 24-game-hours. + * If this switch is set, it will adjust the imobility time period; shortening it, lengthing it, or + * effectively eliminating it. + * Type: Int + * Values: 0 [Default] (Not set: Use default 24 hour duration) + * -1 (Any negative value will result in a fixed duratoion of 2 seconds, which effectively eliminates the wait period) + * 1 (Any potitive value: multiply duration by the value provided and then divide result by 1000. + * Values less than 1000 will shorten the duration, values higher than 1000 will lengthen it.) + */ +const string PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000 = "PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000"; + +/** + * By default, when the Astral Seed power is activated upon death, the user loses one level's + * worth of XP. This may not be compatabile with all death and respawn systems. + * If this flag is set, the XP loss is completely eliminated. The standard PRC event hook script + * of "prc_pw_astralseed" may be used to script any additional effects to occure upon Astral Seed + * respawning, including scripting specific XP loss amount. + * Type: Int + * Values: 0 [Default] (Not set: lose 1 level worth of XP upon Astral Seed respawn) + * 1 (Any potitive value: Remove all XP loss from Astral Seed respawn) + */ +const string PRC_PSI_ASTRAL_SEED_RESPAWN_NO_LEVEL_LOSS = "PRC_PSI_ASTRAL_SEED_RESPAWN_NO_LEVEL_LOSS"; + + +/** + * Setting this switch active makes Psychic Reformation only allow one to + * reselect psionic powers instead of fully rebuilding their character. + * + * Possible values: + * 0 = Off, Psychic Reformation behaves as specified in the power + * description. That is, the target is deleveled by a certain + * amount and then releveled back to where they were. + * Nonzero, not 2 = On, Psychic Reformation only nulls a selected number of + * the target's selected powers and allows reselection. + * 2 = On, and the XP cost is reduced to 25 per level reformed. + */ +const string PRC_PSI_PSYCHIC_REFORMATION_NERF = "PRC_PSI_PSYCHIC_REFORMATION_NERF"; + +/** + * Determines how Rapid Metabolism works. + * When set, heals the feat possessor by their Hit Dice + Constitution modifier + * every 24h. + * Default: Heals the feat possessor by 1 + their Constitution modifier every + * turn (60s). + */ +const string PRC_PNP_RAPID_METABOLISM = "PRC_PNP_RAPID_METABOLISM"; + +/** + * Determines how the epic feat Improved Metapsionics works. + * When set, the total cost of metapsionics applied to power being manifested is + * summed and Improved Metapsionics cost reduction is applied to the sum. + * Default: Improved Metapsionics cost reduction is applied separately to each + * metapsionic used with power being manifested. + */ +const string PRC_PSI_IMP_METAPSIONICS_USE_SUM = "PRC_PSI_IMP_METAPSIONICS_USE_SUM"; + + +/** + * A switch a player can personally toggle. If this is set, their augmentation level + * is considered to be the amount of PP they are willing to pay for augmentation. + * Default: A player's augmentation level is the number of times to augment the power. + */ +const string PRC_PLAYER_SWITCH_AUGMENT_IS_PP = "PRC_PLAYER_SWITCH_AUGMENT_IS_PP"; + +/** + * A switch a player can personally toggle. If set, the metapsionics code attempts + * to avoid exceeding the manifester level cap by skipping application of + * such active metapsionic feats where the cost would cause manifester level cap to + * be exceeded. Quicken Power is exempt from ever being skipped if it is active. + */ +const string PRC_PLAYER_SWITCH_AUTOMETAPSI = "PRC_PLAYER_SWITCH_AUTOMETAPSI"; + +/******************************************************************************\ +* PnP Polymorphing switches * +\******************************************************************************/ + +/** + * These switches are used to limit the targets that can be used with the + * PRC Polymorph / Shifting mechanics. + * + * Remember, mimicing uses the targetting instance, whereas + * shifting into that form again later creats a new instance from + * the resref. Thus if you modify creatures after they have been + * placed from the palette, odd things may happen. + * + * Also if you give any monster the "Archetypal Form" feat, the players + * will not be able to take that monsters shape. + */ + +/** + * If set, the system compares user HD to target CR. + * Default: user HD is compared to target HD + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_USECR = "PNP_SHFT_USECR"; + +/** + * If set, the system does not allow target creatures of size Huge or greater. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_S_HUGE = "PNP_SHFT_S_HUGE"; + +/** + * If set, the system does not allow target creatures of size Large. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_S_LARGE = "PNP_SHFT_S_LARGE"; + +/** + * If set, the system does not allow target creatures of size Medium. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_S_MEDIUM = "PNP_SHFT_S_MEDIUM"; + +/** + * If set, the system does not allow target creatures of size Small. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_S_SMALL = "PNP_SHFT_S_SMALL"; + +/** + * If set, the system does not allow target creatures of size Tiny or smaller. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_S_TINY = "PNP_SHFT_S_TINY"; + +/** + * If set, the system does not allow target creatures of type Outsider. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_OUTSIDER = "PNP_SHFT_F_OUTSIDER"; + +/** + * If set, the system does not allow target creatures of type Elemental. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_ELEMENTAL = "PNP_SHFT_F_ELEMENTAL"; + +/** + * If set, the system does not allow target creatures of type Construct. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_CONSTRUCT = "PNP_SHFT_F_CONSTRUCT"; + +/** + * If set, the system does not allow target creatures of type Undead. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_UNDEAD = "PNP_SHFT_F_UNDEAD"; + +/** + * If set, the system does not allow target creatures of type Dragon. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_DRAGON = "PNP_SHFT_F_DRAGON"; + +/** + * If set, the system does not allow target creatures of type Aberration. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_ABERRATION = "PNP_SHFT_F_ABERRATION"; + +/** + * If set, the system does not allow target creatures of type Ooze. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_OOZE = "PNP_SHFT_F_OOZE"; + +/** + * If set, the system does not allow target creatures of type Magical Beast. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_MAGICALBEAST = "PNP_SHFT_F_MAGICALBEAST"; + +/** + * If set, the system does not allow target creatures of type Giant. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_GIANT = "PNP_SHFT_F_GIANT"; + +/** + * If set, the system does not allow target creatures of type Vermin. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_VERMIN = "PNP_SHFT_F_VERMIN"; + +/** + * If set, the system does not allow target creatures of type Beast. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_BEAST = "PNP_SHFT_F_BEAST"; + +/** + * If set, the system does not allow target creatures of type Animal. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_ANIMAL = "PNP_SHFT_F_ANIMAL"; + +/** + * If set, the system does not allow target creatures of type Monstrous Humanoid. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_MONSTROUSHUMANOID = "PNP_SHFT_F_MONSTROUSHUMANOID"; + +/** + * If set, the system does not allow target creatures of type Humanoid. + * Values: 0 is not set, anything else is set. + */ +const string PNP_SHFT_F_HUMANOID = "PNP_SHFT_F_HUMANOID"; + +/******************************************************************************\ +* Combat System Switches * +\******************************************************************************/ + +/** + * ebonfowl: when TRUE critical hits will not multiply elemental melee damage + * from effects in scripted combat + */ +const string PRC_PNP_ELEMENTAL_DAMAGE = "PRC_PNP_ELEMENTAL_DAMAGE"; + +/** + * TODO: Write description. + */ +const string PRC_SPELL_SNEAK_DISABLE = "PRC_SPELL_SNEAK_DISABLE"; + +/** + * Use 3.5 edition unarmed damage progression instead of 3.0 edition. + * Default: Use 3.0 unarmed damage progression. + */ +const string PRC_3_5e_FIST_DAMAGE = "PRC_3_5e_FIST_DAMAGE"; + +/** + * Use a Brawler character's size as a part of determining their unarmed + * damage. + * Default: A Brawler's size is ignored. + */ +const string PRC_BRAWLER_SIZE = "PRC_BRAWLER_SIZE"; + +/** + * Use appearance size rather than racial-determined size + * This also means it includes bonuses from classes and spells + */ +const string PRC_APPEARANCE_SIZE = "PRC_APPEARANCE_SIZE"; + +/** + * This reenables the Bioware Monk attack progression, with up to 6 monk attacks per round +*/ +const string PRC_BIOWARE_MONK_ATTACKS ="PRC_BIOWARE_MONK_ATTACKS"; + +/** + * This switch (if on) takes care that only light weapons (one size smaller than the creature size) + * are finessable, meaning that small races can only finesse tiny weapons + */ +const string PRC_SMALL_CREATURE_FINESSE ="PRC_SMALL_CREATURE_FINESSE"; + +/** + * turns on combat debugging for scripted combat, + * similar to Bioware's dm_enablecombatdebugging 1 + * will show a lot of info about the attack and damage rolls + */ +const string PRC_COMBAT_DEBUG = "PRC_COMBAT_DEBUG"; + +/** + * switches on Biowares Divine Power version (bonus atacks come at full AB) + */ +const string PRC_BIOWARE_DIVINE_POWER = "PRC_BIOWARE_DIVINE_POWER"; + +/** + * if True, allows us to select a better target in prc combat functions + * by switching from one target to another (closer) target + * only relevant for melee combat (we never switch targets on ranged combat) + */ +const string PRC_ALLOW_SWITCH_OF_TARGET = "PRC_ALLOW_SWITCH_OF_TARGET"; + +/** + * disable coup the grace on first attack in round + */ +const string PRC_DISABLE_COUP_DE_GRACE = "PRC_DISABLE_COUP_DE_GRACE"; + +/** + * limit to the (non-dice) damage of a flame weapon or darkfire spell + * if the switch is not set or zero, non-dice damage of flame weapon or darkfire is limited to 10 + * it is recommended not to set these switches higher than 10 + */ +const string PRC_FLAME_WEAPON_DAMAGE_MAX = "PRC_FLAME_WEAPON_DAMAGE_MAX"; +const string PRC_DARKFIRE_DAMAGE_MAX = "PRC_DARKFIRE_DAMAGE_MAX"; + +/** + * ebonfowl: when this switch is not FALSE, the attack bonus is capped as it is in-game, if FALSE, + * attack bonus is uncapped + */ +const string PRC_CAPPED_ATTACK_BONUS = "PRC_CAPPED_ATTACK_BONUS"; + + +/******************************************************************************\ +* Craft System Switches * +\******************************************************************************/ + +/* + * Completely disable the PRC Crafting System + */ +const string PRC_DISABLE_CRAFT = "PRC_DISABLE_CRAFT"; + +/* + * Disables epic crafting + */ +const string PRC_DISABLE_CRAFT_EPIC = "PRC_DISABLE_CRAFT_EPIC"; + +/* + * Set this on an area to disable crafting within that area + * Best used in conjunction with the time elapsing and no-rest + * This applies to both PRC Crafting and biowares scroll/wand/potions + */ +const string PRC_AREA_DISABLE_CRAFTING = "PRC_AREA_DISABLE_CRAFTING"; + +/* + * Inverts the behavior of previous switch + * Will disable crafting on all areas BUT ones with the + * PRC_AREA_DISABLE_CRAFTING switch set + * Only works for new crafting system + */ +const string PRC_AREA_DISABLE_CRAFTING_INVERT = "PRC_AREA_DISABLE_CRAFTING_INVERT"; + +/* + * Multiply the delay (in seconds) after the creation of an item in which a PC + * can't craft anything. This is divided by 100 to get a float. + * Normally, it's set to the market price of the item. Set + * it to less than 100 to reduce it instead. (default: 0). + * + * This is independant of PRC_PLAYER_TIME + * + */ +const string PRC_CRAFT_TIMER_MULTIPLIER = "PRC_CRAFT_TIMER_MULTIPLIER"; + +/* + * Absolute maximum delay (in seconds) where crafting is disabled for a PC, + * regardless of the item's market price. By default it's 0 (meaning that there's + * no delay at all). + * + * This is independant of PRC_PLAYER_TIME + * + */ +const string PRC_CRAFT_TIMER_MAX = "PRC_CRAFT_TIMER_MAX"; + +/* + * Absolute minimum delay (in seconds) where crafting is disabled for a PC, + * regardless of the item's market price. By default it's 0 (meaning that there's + * no delay at all). + * + * This is independant of PRC_PLAYER_TIME + * + */ +const string PRC_CRAFT_TIMER_MIN = "PRC_CRAFT_TIMER_MIN"; + +/** + * These three switches modify Bioware crafting so that the items produced have the + * casterlevel of the spellcaster who created them. Normally under Bioware, it is possible + * for a level 3 caster to produce level 9 items and for a level 40 caster to only produce + * level 5 items. + * This also allows metamagic to apply to crafting. i.e you produce a wand of maximized fireball + * + * @see PRC_SCRIBE_SCROLL_CASTER_LEVEL + * @see PRC_CRAFT_WAND_CASTER_LEVEL + */ +const string PRC_BREW_POTION_CASTER_LEVEL = "PRC_BREW_POTION_CASTER_LEVEL"; + +/* + * These three switches modify Bioware crafting so that the items produced have the + * casterlevel of the spellcaster who created them. Normally under Bioware, it is possible + * for a level 3 caster to produce level 9 items and for a level 40 caster to only produce + * level 5 items. + * This also allows metamagic to apply to crafting. i.e you produce a wand of maximized fireball + * + * @see PRC_BREW_POTION_CASTER_LEVEL + * @see PRC_CRAFT_WAND_CASTER_LEVEL + */ +const string PRC_SCRIBE_SCROLL_CASTER_LEVEL = "PRC_SCRIBE_SCROLL_CASTER_LEVEL"; + +/* + * These three switches modify Bioware crafting so that the items produced have the + * casterlevel of the spellcaster who created them. Normally under Bioware, it is possible + * for a level 3 caster to produce level 9 items and for a level 40 caster to only produce + * level 5 items. + * This also allows metamagic to apply to crafting. i.e you produce a wand of maximized fireball + * + * @see PRC_BREW_POTION_CASTER_LEVEL + * @see PRC_SCRIBE_SCROLL_CASTER_LEVEL + */ +const string PRC_CRAFT_WAND_CASTER_LEVEL = "PRC_CRAFT_WAND_CASTER_LEVEL"; + +/* + * As above, except it applies to rods + */ +const string PRC_CRAFT_ROD_CASTER_LEVEL = "PRC_CRAFT_ROD_CASTER_LEVEL"; + +/* + * As above, except it applies to staffs + */ +const string PRC_CRAFT_STAFF_CASTER_LEVEL = "PRC_CRAFT_STAFF_CASTER_LEVEL"; + +/* + * Characters with a crafting feat always have the appropriate base item in their inventory + */ +const string PRC_CRAFTING_BASE_ITEMS = "PRC_CRAFTING_BASE_ITEMS"; + +/* + * Max level of spells brewed into potions + * defaults to 3 + */ +const string X2_CI_BREWPOTION_MAXLEVEL = "X2_CI_BREWPOTION_MAXLEVEL"; + +/* + * cost modifier of spells brewed into poitions + * defaults to 50 + */ +const string X2_CI_BREWPOTION_COSTMODIFIER = "X2_CI_BREWPOTION_COSTMODIFIER"; + +/* + * cost modifier of spells scribed into scrolls + * defaults to 25 + */ +const string X2_CI_SCRIBESCROLL_COSTMODIFIER = "X2_CI_SCRIBESCROLL_COSTMODIFIER"; + +/* + * Max level of spells crafted into wands + * defaults to 4 + */ +const string X2_CI_CRAFTWAND_MAXLEVEL = "X2_CI_CRAFTWAND_MAXLEVEL"; + +/* + * cost modifier of spells crafted into wands + * defaults to 750 + */ +const string X2_CI_CRAFTWAND_COSTMODIFIER = "X2_CI_CRAFTWAND_COSTMODIFIER"; + +/* + * cost modifier of spells crafted into rods + * note that adding a second spell costs 75% and 3 or more costs 50% + * defaults to 750 + */ +const string X2_CI_CRAFTROD_COSTMODIFIER = "X2_CI_CRAFTROD_COSTMODIFIER"; + +/* + * cost modifier of spells crafted into staffs + * note that adding a second spell costs 75% and 3 or more costs 50% + * defaults to 750 + */ +const string X2_CI_CRAFTSTAFF_COSTMODIFIER = "X2_CI_CRAFTSTAFF_COSTMODIFIER"; + +/** + * Allows the use of arbitrary itemproperties and uses NWN item costs + * ie. not PnP + */ +const string PRC_CRAFTING_ARBITRARY = "PRC_CRAFTING_ARBITRARY"; + +/** + * Scales the item costs overall for the purposes of crafting + * defaults to 100 + */ +const string PRC_CRAFTING_COST_SCALE = "PRC_CRAFTING_COST_SCALE"; + +/** + * Scales the item costs for mundane item crafting + * defaults to 100 + */ +const string PRC_CRAFTING_MUNDANE_COST_SCALE = "PRC_CRAFTING_MUNDANE_COST_SCALE"; + +/** + * Sets crafting time per 1000gp in market price: + * 1 - off, no time required + * 2 - round + * 3 - turn + * 4 - hour + * 5 - day + * defaults to 1 hour/1000gp + */ +const string PRC_CRAFTING_TIME_SCALE = "PRC_CRAFTING_TIME_SCALE"; + +/** + * TO DISABLE SPECIFIC PROPERTIES: + * + * Set a switch with the name: + * + * PRC_CRAFT_DISABLE__ + * + * where the 2da files are named craft_* (lower case) + * or itempropdef in the case of arbitrary crafting + * + * eg. PRC_CRAFT_DISABLE_itempropdef_15 + * + * disables all castspell properties in arbitrary crafting mode + */ + +/******************************************************************************\ +* Teleport System Switches * +\******************************************************************************/ + +/** + * Defines the maximum number of teleport target locations a PC may store. + * Default: 50 + */ +const string PRC_TELEPORT_MAX_TARGET_LOCATIONS = "PRC_TELEPORT_MAX_TARGET_LOCATIONS"; + +/** + * If this is set, all spells/powers/effects with the [Teleportation] descriptor + * (ie, their scripts use GetCanTeleport()) fail. + * + * Default: Off + */ +const string PRC_DISABLE_TELEPORTATION = "PRC_DISABLE_TELEPORTATION"; + +/** + * If a local integer variable by this name is set on an area, certain + * teleportation destinations are unavailable based on the value of the variable. + * This affects the return value of GetCanTeleport() when the bMovesCreature parameter + * is true. + * + * Possible values are a bitwise combinations of the following: + * PRC_DISABLE_TELEPORTATION_FROM_AREA + * PRC_DISABLE_TELEPORTATION_TO_AREA + * PRC_DISABLE_TELEPORTATION_WITHIN_AREA + */ +const string PRC_DISABLE_TELEPORTATION_IN_AREA = "PRC_DISABLE_TELEPORTATION_IN_AREA"; + +/** + * A value of PRC_DISABLE_TELEPORTATION_IN_AREA. This disables teleporting + * from the area in question to other areas. + */ +const int PRC_DISABLE_TELEPORTATION_FROM_AREA = 0x1; + +/** + * A value of PRC_DISABLE_TELEPORTATION_IN_AREA. This disables teleporting + * from other areas to the area in question. + */ +const int PRC_DISABLE_TELEPORTATION_TO_AREA = 0x2; + +/** + * A value of PRC_DISABLE_TELEPORTATION_IN_AREA. This disables both teleporting + * from area in question to another location in that same area. + */ +const int PRC_DISABLE_TELEPORTATION_WITHIN_AREA = 0x4; + +/** + * Forces spells/powers/effects that use GetTeleportError() to behave in a + * specific way when their destination is in an area on which this local + * variable is set. + * Based on the value of this variable, such a spell/power will always behave in + * a way described by one of the entries of Teleport results table. This happens + * even if the spell/power would normally ignore the table. + * + * Default: Each spell / power behaves by it's normal specification. + * + * Values: + * PRC_FORCE_TELEPORTATION_RESULT_ONTARGET + * PRC_FORCE_TELEPORTATION_RESULT_OFFTARGET + * PRC_FORCE_TELEPORTATION_RESULT_WAYOFFTARGET + * PRC_FORCE_TELEPORTATION_RESULT_MISHAP + */ +const string PRC_FORCE_TELEPORTATION_RESULT = "PRC_FORCE_TELEPORTATION_RESULT"; + +/** + * A value of PRC_FORCE_TELEPORTATION_RESULT. This makes the spells affected by + * the variable always succeed. + */ +const int PRC_FORCE_TELEPORTATION_RESULT_ONTARGET = 1; + +/** + * A value of PRC_FORCE_TELEPORTATION_RESULT. This makes the spells affected by + * the variable always dump the target(s) in a random location in the same area. + */ +const int PRC_FORCE_TELEPORTATION_RESULT_OFFTARGET = 2; + +/** + * A value of PRC_FORCE_TELEPORTATION_RESULT. This makes the spells affected by + * the variable always dump the target(s) in a random location among the users's + * stored teleport choices, or if there are no others, just stay where the user is. + */ +const int PRC_FORCE_TELEPORTATION_RESULT_WAYOFFTARGET = 3; + +/** + * A value of PRC_FORCE_TELEPORTATION_RESULT. This makes the spells affected by + * the variable always do the following: + * // Mishap: + * // You and anyone else teleporting with you have gotten “scrambled.” + * // You each take 1d10 points of damage, and you reroll on the chart to see where you wind up. + * // For these rerolls, roll 1d20+80. Each time “Mishap” comes up, the characters take more damage and must reroll. + */ +const int PRC_FORCE_TELEPORTATION_RESULT_MISHAP = 4; + +/** + * If a variable by this name is non-zero on a creature, that creature cannot + * teleport. If you use this in your own scripts, please do not set it to + * a static value or directly remove it. + * Instead, increase it's value by one when the disabling occurs and decrease + * by one when the disabling turns off. This is required in order to be able to + * handle overlapping sources of forbiddance. + * + * Note: This stops all effects with the [Teleportation] descriptor, by causing + * GetCanTeleport() to always return FALSE. + */ +const string PRC_DISABLE_CREATURE_TELEPORT = "PRC_DISABLE_CREATURE_TELEPORT"; + + +/******************************************************************************\ +* Persistent World switches * +\******************************************************************************/ + +/** + * Persistant time tracking. + * When the first player logs on, the clock is set forward to the last time that + * player logged off. + */ +const string PRC_PW_TIME = "PRC_PW_TIME"; + +/** + * Number of rounds between exporting characters (1 round = 6 seconds). + */ +const string PRC_PW_PC_AUTOEXPORT = "PRC_PW_PC_AUTOEXPORT"; + +/** + * A player's HP is stored via persistant locals every HB and restored on logon. + */ +const string PRC_PW_HP_TRACKING = "PRC_PW_HP_TRACKING"; + +/** + * A player's location is stored via persistant locals every HB and restored + * on logon. + */ +const string PRC_PW_LOCATION_TRACKING = "PRC_PW_LOCATION_TRACKING"; + +/** + * Player places map pins are tracked via persistant locals and restored on logon + */ +const string PRC_PW_MAPPIN_TRACKING = "PRC_PW_MAPPIN_TRACKING"; + +/** + * Being dead is stored via persistant locals and restored on logon. + */ +const string PRC_PW_DEATH_TRACKING = "PRC_PW_DEATH_TRACKING"; + +/** + * Spells cast are tracked via persistant locals and restored on logon + */ +const string PRC_PW_SPELL_TRACKING = "PRC_PW_SPELL_TRACKING"; + +/** + * Players cant logon for this many minutes after a server load + */ +const string PRC_PW_LOGON_DELAY = "PRC_PW_LOGON_DELAY"; + + + +/******************************************************************************\ +* XP system switches * +\******************************************************************************/ + +/** + * This modifies the amount of XP a character recieves based on Level Adjustment + * - Doesn't take racial hit dice into account. + * - Should work with any prior XP system. + * - Use this on pre-exisitng modules. + */ +const string PRC_XP_USE_SIMPLE_LA = "PRC_XP_USE_SIMPLE_LA"; + +/** + * Any new characters entering the module are automatically given racial hit dice + * Unlike PnP, they do not get to select what feats/skills the racial HD grant + * Instead the default bioware package will be used. + * Do not use if the ConvoCCs racial hit dice option is in use. + */ +const string PRC_XP_USE_SIMPLE_RACIAL_HD = "PRC_XP_USE_SIMPLE_RACIAL_HD"; + +/** + * Characters must earn their racial HD through the normal levelup process + * Player must still take all their racial HD before they can take more + * than one level in a non-racial class. + * PRC_XP_USE_SIMPLE_RACIAL_HD must be on, and the convoCC racial hit dice option + * must be off + */ +const string PRC_XP_USE_SIMPLE_RACIAL_HD_NO_FREE_XP = "PRC_XP_USE_SIMPLE_RACIAL_HD_NO_FREE_XP"; + +/** + * Characters are given racial HD via LevelupHenchman so can't select feats etc + * Uses the default packages for each class, which are poor to say the least + * PRC_XP_USE_SIMPLE_RACIAL_HD must be on, and the convoCC racial hit dice option + * must be off + */ +const string PRC_XP_USE_SIMPLE_RACIAL_HD_NO_SELECTION = "PRC_XP_USE_SIMPLE_RACIAL_HD_NO_SELECTION"; + +/** + * Enables PRC XP system. + * This may cause balance issues with pre-exisiting modules, so it is recomended + * that only builders use this and do extensive playtesting and tweaking for + * balance. + * + * Uses the dmgxp.2da file which is a copy of the XP tables in the DMG and ELH + * these are based on the formula of 13.3333 encounters of CR = ECL to advance + * a level. + * Encounters of CR > ECL+8 or CR < ECL-8 dont give XP. + * Tables are setup so that parties' levels will converge over time. + */ +const string PRC_XP_USE_PNP_XP = "PRC_XP_USE_PNP_XP"; + +/** + * PRC XP system will use default bioware xptable.2da instead of dmgxp.2da + */ +const string PRC_XP_USE_BIOWARE_XPTABLE = "PRC_XP_USE_BIOWARE_XPTABLE"; + +/** + * This value is divided by 100 when applied so a value of 100 is equivalent to 1.0 + * slider for PnP XP system, multiplier for final XP amount + * This can also be set on individual PCs for the same result. If it is not set, then + * it defaults to 1.0. + */ +const string PRC_XP_SLIDER_x100 = "PRC_XP_SLIDER_x100"; + +/** + * Player groups will get small xp bonuses. Formula is group size - 1 * switch/100 + * so with a default value of 10 (10%) a party of 4 PCs receives 30% XP bonus + */ +const string PRC_XP_GROUP_BONUS = "PRC_XP_GROUP_BONUS"; + +/** + * Use ECL for NPCs instead of CR. + * Should be close, but I dont know how Bioware CR calculations work with the + * PRC races. + * Also note ECL is a measure of power in a campaign, wheras CR is measure of + * power in a single encounter. Thus ECL weights use/day abilitieis more than + * CR does. + */ +const string PRC_XP_USE_ECL_NOT_CR = "PRC_XP_USE_ECL_NOT_CR"; + +/** + * If this is set, ECL = LA + racial hit dice + * EVEN IF THE CHARACTER DOESNT HAVE ANY RACIAL HIT DICE! + * So it penalizes the power races far more than PnP because they don't get any + * of the other benefits of racial hit dice (BAB, HP, saves, skills, feats, etc) + */ +const string PRC_XP_INCLUDE_RACIAL_HIT_DIE_IN_LA = "PRC_XP_INCLUDE_RACIAL_HIT_DIE_IN_LA"; + +/** + * These values are divided by 100 when applied so a value of 100 is equivalent + * to 1.0. + * This is for purposes of party size for dividing XP awards by. + * By PnP only PCs would count, and possibly henchmen too, but you might want to + * tweak others for balance purposes, for example to hinder a solo wizard with + * dozens of summons. + */ +const string PRC_XP_PC_PARTY_COUNT_x100 = "PRC_XP_PC_PARTY_COUNT_x100"; +const string PRC_XP_HENCHMAN_PARTY_COUNT_x100 = "PRC_XP_HENCHMAN_PARTY_COUNT_x100"; +const string PRC_XP_DOMINATED_PARTY_COUNT_x100 = "PRC_XP_DOMINATED_PARTY_COUNT_x100"; +const string PRC_XP_ANIMALCOMPANION_PARTY_COUNT_x100 = "PRC_XP_ANIMALCOMPANION_PARTY_COUNT_x100"; +const string PRC_XP_FAMILIAR_PARTY_COUNT_x100 = "PRC_XP_FAMILIAR_PARTY_COUNT_x100"; +const string PRC_XP_SUMMONED_PARTY_COUNT_x100 = "PRC_XP_SUMMONED_PARTY_COUNT_x100"; +const string PRC_XP_UNKNOWN_PARTY_COUNT_x100 = "PRC_XP_UNKNOWN_PARTY_COUNT_x100"; + +/** + * Use SetXP rather than GiveXP. Will bypass any possible Bioware interference. + */ +const string PRC_XP_USE_SETXP = "PRC_XP_USE_SETXP"; + +/** + * Give XP to NPCs + */ +const string PRC_XP_GIVE_XP_TO_NPCS = "PRC_XP_GIVE_XP_TO_NPCS"; + +/** + * Setting this switch will turn off the messages about being too far awy to gain XP + */ +const string PRC_XP_DISABLE_SPAM = "PRC_XP_DISABLE_SPAM"; + +/** + * PCs must be in the same area as the CR to gain XP. + * Helps stop powerlevelling by detering low level characters hanging around + * with 1 very strong char. + */ +const string PRC_XP_MUST_BE_IN_AREA = "PRC_XP_MUST_BE_IN_AREA"; + +/** + * Maximum distance that a PC must be to gain XP. + * Helps stop powerlevelling by detering low level characters hanging around + * with 1 very strong char. + */ +const string PRC_XP_MAX_PHYSICAL_DISTANCE = "PRC_XP_MAX_PHYSICAL_DISTANCE"; + +/** + * Maximum level difference in levels between killer and PC being awarded XP. + * Helps stop powerlevelling by detering low level characters hanging around + * with 1 very strong char. + */ +const string PRC_XP_MAX_LEVEL_DIFF = "PRC_XP_MAX_LEVEL_DIFF"; + +/** + * Gives XP to NPCs when no PCs are in their faction + * This might cause lag if large numebrs of NPCs in the same faction. + */ +const string PRC_XP_GIVE_XP_TO_NON_PC_FACTIONS = "PRC_XP_GIVE_XP_TO_NON_PC_FACTIONS"; + + + + +/******************************************************************************\ +* Database and Letoscript switches * +\******************************************************************************/ + +/** + * Set this if you want to use the bioware db for 2da caching + * the value is the number of Hbs between caching runs + * Defaults to 300 (30 mins) if not set + * cache will be flushed automatically when the PRC version changes + * If this is set to -1 or lower, it is never stored for persistance over + * module restarts. + * The bioware database will bloat infinitely on Linux, due to biowares poor + * handling. + */ +const string PRC_USE_BIOWARE_DATABASE = "PRC_USE_BIOWARE_DATABASE"; + +/** + * Set this if you are using NWNX and any sort of database. + */ +const string PRC_USE_DATABASE = "PRC_USE_DATABASE"; + +/** + * Set this if you are using SQLite (the built-in database in NWNX-ODBC2). + * This will use transactions and SQLite specific syntax. + */ +const string PRC_DB_SQLITE = "PRC_DB_SQLITE"; + +/** + * This is the interval of each transaction. By default it is 600 seconds. + * Shorter will mean slower, but less data lost in the event of a server crash. + * Longer is visa versa. + */ +const string PRC_DB_SQLITE_INTERVAL = "PRC_DB_SQLITE_INTERVAL"; + +/** + * Set this if you are using MySQL. + * This will not use transactions and will use MySQL specific syntax + */ +const string PRC_DB_MYSQL = "PRC_DB_MYSQL"; + + +/** + * [DEFUNCT] + * This will precache 2da files into the database. + * The first time a module runs with this set it will lag a lot for a long time + * as the game does 2da reads. + * Afterwards it will be much faster. + * This is a really, really long lag. Like days/weeks type length. + * This is not the "normal" precaching that the spellbooks & psionics does. + */ +const string PRC_DB_PRECACHE = "PRC_DB_PRECACHE"; + +/** + * [DEFUNCT] + * TODO: Write description. + */ +const string PRC_USE_LETOSCRIPT = "PRC_USE_LETOSCRIPT"; + +/** + * [DEFUNCT] + * Set this to 1 if using build 18 + */ +const string PRC_LETOSCRIPT_PHEONIX_SYNTAX = "PRC_LETOSCRIPT_PHEONIX_SYNTAX"; + +/** + * [DEFUNCT] + * Set this to 1 to have Letoscript convert stat boosts on the hide to + * permanent ones. + */ +const string PRC_LETOSCRIPT_FIX_ABILITIES = "PRC_LETOSCRIPT_FIX_ABILITIES"; + +/** + * [DEFUNCT] + * Letoscript needs a string named PRC_LETOSCRIPT_NWN_DIR set to the + * directory of NWN. If it doesnt work, try different slash options: // \\ / \ + */ +const string PRC_LETOSCRIPT_NWN_DIR = "PRC_LETOSCRIPT_NWN_DIR"; + +/** + * [DEFUNCT] + * Switch so that Unicorn will use the SQL database for SCO/RCO + * Must have the zeoslib.dlls installed for this + * + * UNTESTED!!! + */ +const string PRC_LETOSCRIPT_UNICORN_SQL = "PRC_LETOSCRIPT_UNICORN_SQL"; + +/** + * [DEFUNCT] + * This is a string, not integer. + * If the IP is set, Letoscript will use ActivatePortal instead of booting. + * The IP and Password must be correct for your server or bad things will happen. + * - If your IP is non-static make sure this is kept up to date. + * + * See the Lexicon entry on ActivatePortal for more information. + * + * @see PRC_LETOSCRIPT_PORTAL_PASSWORD + */ +const string PRC_LETOSCRIPT_PORTAL_IP = "PRC_LETOSCRIPT_PORTAL_IP"; + +/** + * [DEFUNCT] + * This is a string, not integer. + * If the IP is set, Letoscript will use ActivatePortal instead of booting. + * The IP and Password must be correct for your server or bad things will happen. + * - If your IP is non-static make sure this is kept up to date. + * + * See the Lexicon entry on ActivatePortal for more information. + * + * @see PRC_LETOSCRIPT_PORTAL_IP + */ +const string PRC_LETOSCRIPT_PORTAL_PASSWORD = "PRC_LETOSCRIPT_PORTAL_PASSWORD"; + +/** + * [DEFUNCT] + * If set you must be using Unicorn. + * Will use getnewest bic instead of filename reconstruction (which fails if + * multiple characters have the same name) + */ +const string PRC_LETOSCRIPT_GETNEWESTBIC = "PRC_LETOSCRIPT_GETNEWESTBIC"; + +//This switch is set automatically after prc_onmodload detects NWNX_Funcs plugin +const string PRC_NWNX_FUNCS = "PRC_NWNX_FUNCS"; + + +/******************************************************************************\ +* ConvoCC switches [DEFUNCT] * +\******************************************************************************/ + +/** + * [DEFUNCT] + * Activates the ConvoCC. + * This doesn't turn on the database and letoscript as well, which you must + * do yourself. + * + * @see PRC_USE_DATABASE + * @see PRC_USE_LETOSCRIPT + */ +const string PRC_CONVOCC_ENABLE = "PRC_CONVOCC_ENABLE"; + +/** + * [DEFUNCT] + * Avariel characters have bird wings. + */ +const string PRC_CONVOCC_AVARIEL_WINGS = "PRC_CONVOCC_AVARIEL_WINGS"; + +/** + * [DEFUNCT] + * Fey'ri characters have bat wings. + */ +const string PRC_CONVOCC_FEYRI_WINGS = "PRC_CONVOCC_FEYRI_WINGS"; + +/** + * [DEFUNCT] + * Aasimar characters have the option of angel wings + * Note: Not set by PRC_CONVOCC_ENFORCE_PNP_RACIAL as it isn't part of PnP + */ + +const string PRC_CONVOCC_AASIMAR_WINGS = "PRC_CONVOCC_AASIMAR_WINGS"; + +/** + * [DEFUNCT] + * Fey'ri characters have a demonic tail. + */ +const string PRC_CONVOCC_FEYRI_TAIL = "PRC_CONVOCC_FEYRI_TAIL"; + +/** + * [DEFUNCT] + * Teifling characters have the option of a demonic tail. + */ +const string PRC_CONVOCC_TIEFLING_TAIL = "PRC_CONVOCC_TIEFLING_TAIL"; + +/** + * [DEFUNCT] + * Force Drow characters to be of the correct gender for their race. + */ +const string PRC_CONVOCC_DROW_ENFORCE_GENDER = "PRC_CONVOCC_DROW_ENFORCE_GENDER"; + +/** + *[DEFUNCT] + * Force Genasi clerics to select the relevant elemental domain as one of + * their feats. + */ +const string PRC_CONVOCC_GENASI_ENFORCE_DOMAINS = "PRC_CONVOCC_GENASI_ENFORCE_DOMAINS"; + +/** + *[DEFUNCT] + * Female Rakshasa use the female rakshasa model. Use together with PRC_CONVOCC_USE_RACIAL_APPEARANCES + * @see PRC_CONVOCC_USE_RACIAL_APPEARANCES + */ +const string PRC_CONVOCC_RAKSHASA_FEMALE_APPEARANCE = "PRC_CONVOCC_RAKSHASA_FEMALE_APPEARANCE"; + +/** + *[DEFUNCT] + * A combination switch to turn on all the racial enforcement settings. + * @see PRC_CONVOCC_RAKSHASA_FEMALE_APPEARANCE + * @see PRC_CONVOCC_GENASI_ENFORCE_DOMAINS + * @see PRC_CONVOCC_DROW_ENFORCE_GENDER + * @see PRC_CONVOCC_TIEFLING_TAIL + * @see PRC_CONVOCC_FEYRI_TAIL + * @see PRC_CONVOCC_FEYRI_WINGS + * @see PRC_CONVOCC_AVARIEL_WINGS + */ +const string PRC_CONVOCC_ENFORCE_PNP_RACIAL = "PRC_CONVOCC_ENFORCE_PNP_RACIAL"; + +/** + * [DEFUNCT] + * Note: feat enforcement switches don't do anything (TODO?) + */ + +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_BLOOD_OF_THE_WARLORD= "PRC_CONVOCC_ENFORCE_BLOOD_OF_THE_WARLORD"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_NIMBUSLIGHT = "PRC_CONVOCC_ENFORCE_FEAT_NIMBUSLIGHT"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_HOLYRADIANCE = "PRC_CONVOCC_ENFORCE_FEAT_HOLYRADIANCE"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_SERVHEAVEN = "PRC_CONVOCC_ENFORCE_FEAT_SERVHEAVEN"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_SAC_VOW = "PRC_CONVOCC_ENFORCE_FEAT_SAC_VOW"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_VOW_OBED = "PRC_CONVOCC_ENFORCE_FEAT_VOW_OBED"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_THRALL_TO_DEMON= "PRC_CONVOCC_ENFORCE_FEAT_THRALL_TO_DEMON"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_DISCIPLE_OF_DARKNESS="PRC_CONVOCC_ENFORCE_FEAT_DISCIPLE_OF_DARKNESS"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_LICHLOVED = "PRC_CONVOCC_ENFORCE_FEAT_LICHLOVED"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_EVIL_BRANDS = "PRC_CONVOCC_ENFORCE_FEAT_EVIL_BRANDS"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_VILE_WILL_DEFORM="PRC_CONVOCC_ENFORCE_FEAT_VILE_WILL_DEFORM"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_VILE_DEFORM_OBESE="PRC_CONVOCC_ENFORCE_FEAT_VILE_DEFORM_OBESE"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_VILE_DEFORM_GAUNT="PRC_CONVOCC_ENFORCE_FEAT_VILE_DEFORM_GAUNT"; +/** Separate enforcement of feats with special restrictions. */ +const string PRC_CONVOCC_ENFORCE_FEAT_LOLTHS_MEAT = "PRC_CONVOCC_ENFORCE_FEAT_LOLTHS_MEAT"; + + +/** + *[DEFUNCT] + * A combination switch to turn on all the feat enforcement settings. Doesn't do anything + */ +const string PRC_CONVOCC_ENFORCE_FEATS = "PRC_CONVOCC_ENFORCE_FEATS"; + +/** + * [DEFUNCT] + * Stops players from changing their wings. Turning this on gives players only the "none" choice + * at the wing stage of the convoCC. Use in conjuction with the wing switches. + * @see PRC_CONVOCC_AVARIEL_WINGS + * @see PRC_CONVOCC_FEYRI_WINGS + * @see PRC_CONVOCC_AASIMAR_WINGS + */ +const string PRC_CONVOCC_DISALLOW_CUSTOMISE_WINGS = "PRC_CONVOCC_DISALLOW_CUSTOMISE_WINGS"; + +/** + * [DEFUNCT] + * Stops players from changing their tail. Turning this on gives players only the "none" choice + * at the tail stage of the convoCC. Use in conjuction with the tail switches. + * @see PRC_CONVOCC_FEYRI_TAIL + * @see PRC_CONVOCC_TIEFLING_TAIL + */ +const string PRC_CONVOCC_DISALLOW_CUSTOMISE_TAIL = "PRC_CONVOCC_DISALLOW_CUSTOMISE_TAIL"; + +/** + * [DEFUNCT] + * Stops players from changing their model at all. Doesn't do anything + */ +const string PRC_CONVOCC_DISALLOW_CUSTOMISE_MODEL = "PRC_CONVOCC_DISALLOW_CUSTOMISE_MODEL"; + +/** + * [DEFUNCT] + * Players are only given a choice of appearances that match their race. For most races, this is the + * default appearance defined in racialtypes.2da. + * @see PRC_CONVOCC_RAKSHASA_FEMALE_APPEARANCE + */ +const string PRC_CONVOCC_USE_RACIAL_APPEARANCES = "PRC_CONVOCC_USE_RACIAL_APPEARANCES"; +/** + * [DEFUNCT] + * Player can only choose a portrait that matches their race as in portraits.2da. Because + * Bioware's elf, dwarf etc. subrace portraits are labelled as eg. 'elf' not 'drow' and because + * half elves have no portraits, this is actually done on appearance and not on race for PCs using + * Bioware's PC appearance models. + */ +const string PRC_CONVOCC_USE_RACIAL_PORTRAIT = "PRC_CONVOCC_USE_RACIAL_PORTRAIT"; + +/** + * [DEFUNCT] + * Players can only select from the player voicesets. NPC voicesets are not + * complete, so wont play sounds for many things such as emotes. + */ +const string PRC_CONVOCC_ONLY_PLAYER_VOICESETS = "PRC_CONVOCC_ONLY_PLAYER_VOICESETS"; + +/** + * [DEFUNCT] + * Only allows players to select voiceset of the same gender as their character. + */ +const string PRC_CONVOCC_RESTRICT_VOICESETS_BY_SEX = "PRC_CONVOCC_RESTRICT_VOICESETS_BY_SEX"; + +/** + * [DEFUNCT] + * Allow players to keep their exisiting voiceset. + * The ConvoCC cannot allow players to select custom voiceset, so the only way + * for players to have them is to select them in the Bioware character creator + * and then select to keep them in the ConvoCC. + */ +const string PRC_CONVOCC_ALLOW_TO_KEEP_VOICESET = "PRC_CONVOCC_ALLOW_TO_KEEP_VOICESET"; + +/** + * [DEFUNCT] + * Allow players to keep their exisiting portrait. + * The ConvoCC cannot allow players to select custom portraits, so the only way + * for players to have them is to select them in the Bioware character creator + * and then select to keep them in the ConvoCC. + */ +const string PRC_CONVOCC_ALLOW_TO_KEEP_PORTRAIT = "PRC_CONVOCC_ALLOW_TO_KEEP_PORTRAIT"; + +/** + * [DEFUNCT] + * Only allow players to select portraits of the same gender as their character. + * Most of the NPC portraits do not have a gender so are also removed. + */ +const string PRC_CONVOCC_RESTRICT_PORTRAIT_BY_SEX = "PRC_CONVOCC_RESTRICT_PORTRAIT_BY_SEX"; + +/** + * [DEFUNCT] + * This option give players the ability to start with racial hit dice for some + * of the more powerful races. These are defined in ECL.2da. + * For these races, players do not pick a class in the ConvoCC but instead + * select 1 or more levels in a racial class (such as monsterous humanoid, or + * outsider). + * This is not a complete ECL system, it merely gives players the racial hit + * dice component of their race. It does not make any measure of the Level + * Adjustment component. For example, a pixie has no racial hit dice, but has a + * +4 level adjustment. Doesn't do anything + */ +const string PRC_CONVOCC_ENABLE_RACIAL_HITDICE = "PRC_CONVOCC_ENABLE_RACIAL_HITDICE"; + +/** + * [DEFUNCT] + * This option allows players to keep their skillpoints from one level to + * the next, if they want to. + */ +const string PRC_CONVOCC_ALLOW_SKILL_POINT_ROLLOVER = "PRC_CONVOCC_ALLOW_SKILL_POINT_ROLLOVER"; + +/** + * [DEFUNCT] + * This will identify new characters based on XP as in v1.3 + * This is less secure than using the encrypted key. + * @see PRC_CONVOCC_ENCRYPTION_KEY + */ +const string PRC_CONVOCC_USE_XP_FOR_NEW_CHAR = "PRC_CONVOCC_USE_XP_FOR_NEW_CHAR"; + +/** + * [DEFUNCT] + * This is the key used to encrypt characters' names if USE_XP_FOR_NEW_CHAR + * is false in order to identify returning characters. It should be in the + * range 1-100. + * If USE_XP_FOR_NEW_CHAR is true along with this, then returning characters + * will be encrypted too, so once everone has logged on at least once, + * USE_XP_FOR_NEW_CHAR can be set to false for greater security. + * + * WARNING: Changing this value after some PCs have gone through the convoCC will + * cause them to have to go through it again. The encryption uses the player's + * public CD key, so they won't be able to log into their account from different + * NWN installs as the key won't match using this system + */ +const string PRC_CONVOCC_ENCRYPTION_KEY = "PRC_CONVOCC_ENCRYPTION_KEY"; + +/** + * [DEFUNCT] + * As requested, an option to alter the amount of points available in the stat + * point-buy at character creation. + * Default: 30 + */ +const string PRC_CONVOCC_STAT_POINTS = "PRC_CONVOCC_STAT_POINTS"; + +/** + * [DEFUNCT] + * As requested, if set this will give a number of bonus feats equal to this + * value to each created character, similar to human Quick To Master feat. + */ +const string PRC_CONVOCC_BONUS_FEATS = "PRC_CONVOCC_BONUS_FEATS"; + +/** + * [DEFUNCT] + * As requested, this will cap the maximum a stat can start at, excluding racial + * modifies. + * Default: 18 + */ +const string PRC_CONVOCC_MAX_STAT = "PRC_CONVOCC_MAX_STAT"; + +/** + * [DEFUNCT] + * As requested, this will change the skill point multplier at level 1. + * Default: 4 + */ +const string PRC_CONVOCC_SKILL_MULTIPLIER = "PRC_CONVOCC_SKILL_MULTIPLIER"; + +/** + * [DEFUNCT] + * As requested, this will give a bonus to skill points after multiplication. + */ +const string PRC_CONVOCC_SKILL_BONUS = "PRC_CONVOCC_SKILL_BONUS"; + +/** + * [DEFUNCT] + * When set, the convoCC NO LONGER STARTS AUTOMATICALLY on logging in. It must be called + * by a script in the module. This allows the module builder to start the convoCC from a + * particular area or trigger's On Enter event. The script should call the convoCC with + * ExecuteScript("prc_ccc_main", oPC) where oPC is the PC. It's advisable to check the + * entering object is a PC and not a NPC or DM. + */ + +const string PRC_CONVOCC_CUSTOM_START_LOCATION = "PRC_CONVOCC_CUSTOM_START_LOCATION"; + +/** + * [DEFUNCT] + * When set, this switch causes a custom script to be used to determine whether a PC should go + * through the convoCC or not. + * The script must: + * - be called "ccc_custom_enter" + * - set the local int "CONVOCC_LAST_STATUS" on the PC (OBJECT_SELF) + * - include prc_ccc_const (for the constants the local int can be set to) + * otherwise the PC will always be booted + * + * possible values for CONVOCC_LAST_STATUS: + * CONVOCC_ENTER_BOOT_PC (causes the PC to get kicked) + * CONVOCC_ENTER_NEW_PC (causes the PC to go through the convoCC) + * CONVOCC_ENTER_RETURNING_PC (causes the PC to skip the convoCC) + * + * This switch will completely bypass the convoCC methods for determining whether to run the + * convoCC on an entering PC, so if necessary, your custom marker for 'done' would be set + * in 'ccc_custom_exit' + * @see PRC_CONVOCC_CUSTOM_EXIT_SCRIPT + */ +const string PRC_CONVOCC_CUSTOM_ENTER_SCRIPT = "PRC_CONVOCC_CUSTOM_ENTER_SCRIPT"; + +/** + * [DEFUNCT] + * When set, this switch causes a custom script to be executed at the last stage of the convoCC, + * just before booting the player. The script must be named 'ccc_custom_exit'. + * Possible uses include: giving PCs gold and/or equipment, giving PCs PW items + * (even plot items get removed at the start of the convoCC), setting a new persistant location, + * setting a custom marker for having done the convoCC in conjunction with + * 'ccc_custom_enter' + * @see PRC_CONVOCC_CUSTOM_ENTER_SCRIPT + */ + +const string PRC_CONVOCC_CUSTOM_EXIT_SCRIPT = "PRC_CONVOCC_CUSTOM_EXIT_SCRIPT"; + +/******************************************************************************\ +* Truenaming switches * +\******************************************************************************/ + +/** + * Sets the CR Multiplier for Evolving Mind utterances + * This is divided by 100 to get a float. + * Ex: To multiply by 1.5, set this value to 150 + * + * The formula used is (CR * Multiplier) + 15 + * + * defaults to PnP: (CR * 2) + 15 + */ +const string PRC_TRUENAME_CR_MULTIPLIER = "PRC_TRUENAME_CR_MULTIPLIER"; + +/** + * Gives a bonus based on Truenamer level + * PC Truenamer level is divided by this value + * Ex: To give a bonus equal to 1/2 Truenamer level, set this to 2 + * + * The formula used is (CR * Multiplier) + 15 - Bonus + * + * defaults to PnP: 0/No bonus + */ +const string PRC_TRUENAME_LEVEL_BONUS = "PRC_TRUENAME_LEVEL_BONUS"; + +/** + * Sets the Constant value added to the DC + * Ex: To make the constant 10, simply set this value to 10 + * + * The formula used is (CR * Multiplier) + Constant + * + * defaults to PnP: +15. + */ +const string PRC_TRUENAME_DC_CONSTANT = "PRC_TRUENAME_DC_CONSTANT"; + +/** + * Turns off the Law of Sequence when set to 1 + * + * defaults to PnP: On + */ +const string PRC_LAW_OF_SEQUENCE = "PRC_LAW_OF_SEQUENCE"; + +/** + * Turns off the Law of Resistance when set to 1 + * + * defaults to PnP: On + */ +const string PRC_LAW_OF_RESISTANCE = "PRC_LAW_OF_RESISTANCE"; + +/** + * Sets the Constant value added to the DC + * Ex: To make the constant 10, simply set this value to 10 + * + * The formula used is Constant + (2 * Utterance Level) + * + * defaults to PnP: +25. + */ +const string PRC_PERFECTED_MAP_CONSTANT = "PRC_PERFECTED_MAP_CONSTANT"; + +/** + * Sets the Multiplier value added to the DC + * Ex: To make the multiplier 4, simply set this value to 4 + * + * The formula used is 25 + (Multiplier * Utterance Level) + * + * defaults to PnP: 2. + */ +const string PRC_PERFECTED_MAP_MULTIPLIER = "PRC_PERFECTED_MAP_MULTIPLIER"; + +/******************************************************************************\ +* Binding switches * +\******************************************************************************/ + +/** + * Sets how many seconds it takes to contact a vestige. + * Any number less than 6 is ignored + */ +const string PRC_CONTACT_VESTIGE_TIMER = "PRC_CONTACT_VESTIGE_TIMER"; + +/** + * Sets how many seconds it takes to bind a vestige. + * Any number less than 12 is ignored + */ +const string PRC_BIND_VESTIGE_TIMER = "PRC_BIND_VESTIGE_TIMER"; + +/******************************************************************************\ +* Encounter switches * +\******************************************************************************/ + +/** Encounter areas + * Setting this will disable access to the encounter areas from the PRC Options convo + */ +const string PRC_DISABLE_ENCOUNTERS = "PRC_DISABLE_ENCOUNTERS"; + +/** + * This script prevents hacking to another user account without Master Server Authentication + * + * Stores public cd keys for each account in database. In case the key was registered with + * another account player is booted from the server. + * + * All credits go to FunkySwerve + */ +const string PRC_PW_SECURITY_CD_CHECK = "PRC_PW_SECURITY_CD_CHECK"; + + +/******************************************************************************\ +* Debugging Switches * +\******************************************************************************/ + +/** + * Toggles everything guarded by "if(DEBUG)". Mostly calls to DoDebug(). + */ +const string PRC_DEBUG = "PRC_DEBUG"; + + + + +/////////////////////// +// Function protypes // +/////////////////////// +/* NOTE: if you are looking for a function that used to be in here, it's probably in inc_switch_setup */ + +/** + * Checks the state of a PRC switch. + * NOTE: This will only work with switches that use integer values. You + * must get the value of non-integer-valued switches manually. + * + * @param sSwitch One of the PRC_* constant strings defined in prc_inc_switch + * @return The value of the switch queried + */ +int GetPRCSwitch(string sSwitch); + +/** + * Sets a PRC switch state. + * NOTE: As this will only set switches with integer values, you will need + * to manually set the (few) switches that should have a value other than + * integer. + * + * @param sSwitch One of the PRC_* constant strings defined in prc_inc_switch + * @param nState The integer value to set the switch to + */ +void SetPRCSwitch(string sSwitch, int nState); + +//const string PRC_FILE_END_TOKEN = "prc_fe_tkn"; + +////////////////////////// +// Function definitions // +////////////////////////// + +int GetPRCSwitch(string sSwitch) +{ + return GetLocalInt(GetModule(), sSwitch); +} + +void SetPRCSwitch(string sSwitch, int nState) +{ + SetLocalInt(GetModule(), sSwitch, nState); +} + + +// void main (){} diff --git a/src/include/prc_inc_teleport.nss b/src/include/prc_inc_teleport.nss new file mode 100644 index 0000000..08317d2 --- /dev/null +++ b/src/include/prc_inc_teleport.nss @@ -0,0 +1,901 @@ +//:://///////////////////////////////////////////// +//:: Teleport include +//:: prc_inc_teleport +//:://///////////////////////////////////////////// +/** @file + This include contains operations to maintain + an array of metalocations used as teleport target + locations on a PC. In addition, there is a function + for starting a conversation for the PC to select a + location from their array. + + All the operations work only on PCs, as there is no + AI that could have NPCs take any advantage of the + system. +*/ +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 29.05.2005 +//::////////////////////////////////////////////// + +#include "prc_inc_combat" +#include "inc_dynconv" + +/////////////////////// +/* Public Constants */ +/////////////////////// + +/** + * The name of the array where GetTeleportingObjects() stores the creatures it has + * determined should teleport with the current spell. + */ +const string PRC_TELEPORTING_OBJECTS_ARRAY = "PRC_TeleportingObjectList"; + +/** + * The number of the teleport quickselection slots. Also the index number of the highest slot, + * as they are numbered starting from 1. + */ +const int PRC_NUM_TELEPORT_QUICKSELECTS = 2; + +/** + * A constant for the value of slot parameter used when accessing the active quickselection. + */ +const int PRC_TELEPORT_ACTIVE_QUICKSELECTION = -1; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Starts the conversation for selecting the target location for a teleport. + * Once the PC has made his/her/it's selection, the result is stored on the + * PC and the callbackscript is run. + * + * @param oPC The PC that will make the selection. + * @param sCallbackScript The name of the script to run once the PC has made + * their decision. + * @param sCallbackVar The name of the local variable where the PC's choice + * will be stored. + * @param bMeta If this is TRUE, the result will be stored as a + * metalocation. Otherwise it will be stored as a location. + * @param bForce If this is TRUE, an attempt will be made to make sure + * the PC will make the choice. ClearAllActions will be + * called to prevent other activity from interfering with + * the conversation strating and the PC will not be allowed + * to abort the conversation. + */ +void ChooseTeleportTargetLocation(object oPC, string sCallbackScript, string sCallbackVar, + int bMeta = FALSE, int bForce = FALSE); + +/** + * Returns the first teleport target location in the array and initialises + * the iteration counter for calls to GetNextStoredTeleportTargetLocation(). + * + * @param oPC The PC on whose array to operate. + * @return The first element of the array or the location of oPC if the + * array is empty. + */ +struct metalocation GetFirstStoredTeleportTargetLocation(object oPC); + +/** + * Returns the element at the current value of the iteration counter and + * increments the counter. + * + * @param oPC The PC on whose array to operate. + * @return The next element in the array or null metalocation if the + * iteration has reached the end of the array or if the iteration + * counter hasn't been initialised. + * + * @see GetFirstStoredTeleportTargetLocation + */ +struct metalocation GetNextStoredTeleportTargetLocation(object oPC); + +/** + * Returns the teleport target location stored at the given index in the array. + * This function does not interfere with the iteration counter used by + * GetFirstStoredTeleportTargetLocation and GetNextStoredTeleportTargetLocation. + * + * @param oPC The PC on whose array to operate. + * @param nInd The array index from which to retrieve the location. + * @return The teleport target location stored at the given index, or null + * metalocation if the index was out of array bounds. + */ +struct metalocation GetNthStoredTeleportTargetLocation(object oPC, int nInd); + +/** + * Returns the number of elements in the teleport target locations array on the + * PC. + * + * @param oPC The PC on whose array to operate. + * @return The number of locations stored in the array. + */ +int GetNumberOfStoredTeleportTargetLocations(object oPC); + +/** + * Checks whether the PC has a teleport quickselection active and if so, + * whether it contains a valid metalocation. + * + * @param oPC The PC whose quickselection to check. + * @param nSlot The quickselection slot to check. Valid values are PRC_TELEPORT_ACTIVE_QUICKSELECTION, + * which checks the active quickselection and numbers from 1 to PRC_NUM_TELEPORT_QUICKSELECTS. + * + * @return TRUE if the PC has a quickselection active and it is + * a valid metalocation, FALSE otherwise. + */ +int GetHasTeleportQuickSelection(object oPC, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION); + +/** + * Gets the given creature's active teleport quickselection, if any. + * + * @param oPC The PC whose quickselection to check. + * @param bClear Whether to clear the quickselection after getting it. + * @return The PC's active quickselection, or null metalocation + * if there is none. + */ +struct metalocation GetActiveTeleportQuickSelection(object oPC, int bClear = FALSE); + +/** + * Gets the contents of one of the given creature's quickselect slots. Or the + * active quickselection if the slot parameter is -1. + * + * @param oPC The PC whose quickselection to get. + * @param nSlot The slot to get from. Valid values are PRC_TELEPORT_ACTIVE_QUICKSELECTION, + * which returns the active quickselection and numbers from 1 to PRC_NUM_TELEPORT_QUICKSELECTS. + * + * @return The quickselection in the given slot, or null metalocation if the + * slot was empty. Also returns null metalocation on error. + */ +struct metalocation GetTeleportQuickSelection(object oPC, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION); + +/** + * Sets one of the PC's teleport quickselections to the given value. + * Has no effect on error. + * + * @param oPC The PC whose quickselection to set. + * @param mlocL The metalocation to be stored. + * + * @param nSlot The slot to store the metalocation in. Valid values are PRC_TELEPORT_ACTIVE_QUICKSELECTION, + * which sets the active quickselection and numbers from 1 to PRC_NUM_TELEPORT_QUICKSELECTS. + */ +void SetTeleportQuickSelection(object oPC, struct metalocation mlocL, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION); + +/** + * Deletes the contents of a teleport quickselection slot on the given creature. + * + * @param oPC The PC whose quickselection to delete. + * @param nSlot The quickselection slot to clear. + */ +void RemoveTeleportQuickSelection(object oPC, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION); + +/** + * Removes the teleport target location last returned by GetFirstStoredTeleportTargetLocation + * or GetNextStoredTeleportTargetLocation from the PCs array. + * Resets the iteration counter to prevent possible errors. + * + * @param oPC The PC on whose array to operate. + */ +void RemoveCurrentTeleportTargetLocation(object oPC); + +/** + * Removes the teleport target location at the given index in the PCs array. The + * elements after the removed index will be moved down so there will not be empty + * elements in the middle of the array. + * Resets the iteration counter to prevent possible errors. + * + * @param oPC The PC on whose array to operate. + * @param nInd The index from which to delete. + */ +void RemoveNthTeleportTargetLocation(object oPC, int nInd); + +/** + * Adds a location to the end of the teleport target array of the given PC. + * Implemented by constructing a metalocation out of locToAdd and sName and + * calling AddTeleportTargetLocationAsMeta. + * + * @param oPC The PC to whose teleport target location array the + * location is to be added. + * @param locToAdd The location to store. + * @param sName The name of the teleport target location. + * + * @return TRUE if the addition was successfull, FALSE otherwise. + */ +int AddTeleportTargetLocation(object oPC, location locToAdd, string sName); + +/** + * Adds a metalocation to the end of the teleport target array of the given PC. + * + * @param oPC The PC to whose teleport target location array the + * metalocation is to be added. + * @param mlocToAdd The metalocation to store. + * + * @return TRUE if the addition was successfull, FALSE otherwise. + */ +int AddTeleportTargetLocationAsMeta(object oPC, struct metalocation mlocToAdd); + +/** + * Creates a map pin for each of the given PC's teleport target locations that do not + * have a map pin created for them yet. Is not totally reliable. + * Known problems: + * Cannot detect if a map pin created for a location has been deleted. + * + * @param oPC The PC for whom to create the map pins. + */ +void TeleportLocationsToMapPins(object oPC); + +/** + * This function checks whether the given creature can teleport from + * it's current location. It is intended to be run within teleport + * spellscripts. + * + * @param oCreature A creature casting a teleportation spell. + * @param lTarget The location the creature is going to teleport to. + * @param bInform If this is true, the creature is sent a message if + * it is not allowed to teleport. + * @param bPublic Used as the bBroadcastToFaction parameter of + * FloatingTextStringOnCreature() when informing oCreature + * of error. + * @return TRUE if the creature can teleport, FALSE if it can't. + */ +int GetCanTeleport(object oCreature, location lTarget, int bMovesCreature = FALSE, int bInform = FALSE, int bPublic = FALSE); + +/** + * Common code for teleportation spells that: + * 1) Always teleport the caster. + * 2) Can be used to teleport other willing targets within touch range. + * 2b) The amount of these additional targets is limited to + * 1 / 3 caster|manifester levels. + * + * The results will be stored in a local array on the caster, + * retrievable using functions from prc_inc_array. + * The name of the array is contained within the constant PRC_TELEPORTING_OBJECTS_ARRAY. + * + * @param oCaster The object casting the teleportation spell + * @param nCasterLvl The caster level of oCaster when casting the spell in question. + * @param bSelfOrParty If this is TRUE, willing creatures (party members) + * within 10ft of oCaster are taken along. If FALSE, + * only the caster is teleported. + */ +void GetTeleportingObjects(object oCaster, int nCasterLvl, int bSelfOrParty); + +/** + * Determines whether the given teleportation target location can be used, or whether + * the effect causing the teleportation errors, changing the target location. + * Any inter-area teleportation effects should use this check even if they normally + * always work correctly. + * + * @param lOriginal The original destination of the teleport. + * @param oUser The user of the teleportation causing effect. + * @param bNormallyErroless Whether the effect causing the teleprotation can normally + * error. May be overridden by variables set on the target area. + * @param bRecursing Whether the function was called again due to Mishap being + * rolled or not. This should always be left FALSE. + * + * @return Either lOrignal, or another location based on the error roll. + */ +location GetTeleportError(location lOriginal, object oUser, int bNormallyErroless = FALSE, int bRecursing = FALSE); + +/** + * Increments a marker on the target that will cause it to be unable to be + * teleported. ie. GetCanTeleport() will return FALSE for the target. + * Uses of DisallowTeleport() stack, so for example, if the function has + * been called twice for a particular target, that target needs to have + * AllowTeleport called on it twice before it can teleport again (or once with + * the bClearAll parameter TRUE) + * + * @param oTarget Target object to forbide the teleportation of. + */ +void DisallowTeleport(object oTarget); + +/** + * Reverse of DisallowTeleport(), a call to this function makes the target + * eligible for teleportation again. + * NOTE: multiple forbiddances stack, and by default uses of this function + * only reduces the forbiddace. + * + * @param oTarget Target to allow teleportation for again. + * @param bClearAll If TRUE, fully clears the forbiddance marker, otherwise + * just decrements the value by one. + */ +void AllowTeleport(object oTarget, int bClearAll = FALSE); + +/** + * Performs full attack on eligible target at the end of a teleport sequence + * + * @param oPC The caller + */ +void ShadowPounce(object oPC); + +////////////////////////////////////////////////// +/* Internal Constants - nothing to see here :D */ +////////////////////////////////////////////////// + +/// Internal constant - Name of the array where the teleport target locations are stored. +const string PRC_TELEPORT_ARRAY_NAME = "PRC_TeleportLocation_Array"; +/// Internal constant - Name of personal switch telling whether to create map pins for a particular PC's stored locations. +const string PRC_TELEPORT_CREATE_MAP_PINS = "PRC_Teleport_CreateMapPins"; +/// Internal constant - Name of personal switch telling how long the listener will wait for the player to speak a name when a new location is stored. +const string PRC_TELEPORT_NAMING_TIMER_VARNAME = "PRC_Teleport_NamingListenerDuration"; +/// Internal constant - Name of personal swithc telling whether to automatically store the latest location the character rested at +const string PRC_TELEPORT_ONREST_MARKLOCATION = "PRC_Teleport_OnRest_MarkLocation"; + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +void ChooseTeleportTargetLocation(object oPC, string sCallbackScript, string sCallbackVar, + int bMeta = FALSE, int bForce = TRUE) +{ +/* // The system is only useful to PCs. If it is at some point implemented for NPCs, change this. + if(!GetIsPC(oPC)) return;*/ + + // Make sure the PC has the feats for marking locations + ExecuteScript("prc_tp_mgmt_eval", oPC); + + int nSpellID = GetSpellId(); + + // Handle Word of Recall spell + if(nSpellID == SPELL_WORD_OF_RECALL_SELF + || nSpellID == SPELL_WORD_OF_RECALL_PARTY) + { + struct metalocation mlocL = GetLocalMetalocation(oPC, "PRC_WordOfRecall"); + + if(GetIsMetalocationInModule(mlocL)) + { + // Store the return value under the requested name and as the requested type + if(bMeta) + SetLocalMetalocation(oPC, sCallbackVar, mlocL); + else + SetLocalLocation(oPC, sCallbackVar, MetalocationToLocation(mlocL)); + + // Break the script execution association between this one and the callback script + // by delaying it. Probably unnecessary, but it will clear potential interference + // caused by things done in this execution + DelayCommand(0.2f, ExecuteScript(sCallbackScript, oPC)); + } + else + { + FloatingTextStrRefOnCreature(16789977, oPC, FALSE); + } + } + // Handle possible quickselection + else if(GetHasTeleportQuickSelection(oPC, PRC_TELEPORT_ACTIVE_QUICKSELECTION)) + { + // Get the quickselected metalocation and clear it + struct metalocation mlocL = GetActiveTeleportQuickSelection(oPC, TRUE); + int bTransViaPlants = GetLocalInt(oPC, "PRC_TransportViaPlants"); + DeleteLocalInt(oPC, "PRC_TransportViaPlants"); + object oArea = GetAreaFromMetalocation(mlocL); + + if((bTransViaPlants && GetIsAreaNatural(oArea) && !GetIsAreaInterior(oArea)) + || !bTransViaPlants) + { + // Store the return value under the requested name and as the requested type + if(bMeta) + SetLocalMetalocation(oPC, sCallbackVar, mlocL); + else + SetLocalLocation(oPC, sCallbackVar, MetalocationToLocation(mlocL)); + + // Break the script execution association between this one and the callback script + // by delaying it. Probably unnecessary, but it will clear potential interference + // caused by things done in this execution + DelayCommand(0.2f, ExecuteScript(sCallbackScript, oPC)); + } + else + { + SendMessageToPCByStrRef(oPC, 16789931); + } + } + // We have to go look at the stored array, so make sure it contains at least one entry + else if(!GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME)) + {// "You do not have any locations marked for teleporting to!" + SendMessageToPCByStrRef(oPC, 16825305); + + // Store the PC's location + if(bMeta) + SetLocalMetalocation(oPC, sCallbackVar, LocationToMetalocation(GetLocation(oPC))); + else + SetLocalLocation(oPC, sCallbackVar, GetLocation(oPC)); + + // Break the script execution association between this one and the callback script + // by delaying it. Probably unnecessary, but it will clear potential interference + // caused by things done in this execution + DelayCommand(0.2f, ExecuteScript(sCallbackScript, oPC)); + } + // No quickselection was active and there is at least one location to select, so run the + // conversation to find out where the user wants to go + else + { + SetLocalString(oPC, "PRC_TeleportTargetSelection_CallbackScript", sCallbackScript); + SetLocalString(oPC, "PRC_TeleportTargetSelection_ReturnStoreName", sCallbackVar); + SetLocalInt(oPC, "PRC_TeleportTargetSelection_ReturnAsMetalocation", bMeta); + + StartDynamicConversation("prc_teleprt_conv", oPC, + bForce ? DYNCONV_EXIT_NOT_ALLOWED : DYNCONV_EXIT_ALLOWED_SHOW_CHOICE, + !bForce, bForce, oPC); + } +} + +struct metalocation GetFirstStoredTeleportTargetLocation(object oPC) +{ + // Return null if the array is empty + if(!GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME)) + return LocationToMetalocation(GetLocation(oPC), "Error: No stored locations! Returned current location of " + GetName(oPC)); + + // Set the iterator value for subsequent calls to GetNextStoredTeleportTargetLocation() + SetLocalInt(oPC, "PRC_Teleport_Array_Iterator", 1); + // Clean away the iterator on script execution end + DelayCommand(0.0f, DeleteLocalInt(oPC, "PRC_Teleport_Array_Iterator")); + + return GetPersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_0"); +} + +struct metalocation GetNextStoredTeleportTargetLocation(object oPC) +{ + // Return null if GetFirstStoredTeleportTargetLocation() hasn't been called previously + int nInd = GetLocalInt(oPC, "PRC_Teleport_Array_Iterator"); + if(!nInd) return GetNullMetalocation(); + + // If the iteration has reached the end of the array, delete the iteration counter and return null + if(nInd > GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME) - 1) + { + DeleteLocalInt(oPC, "PRC_Teleport_Array_Iterator"); + return GetNullMetalocation(); + } + + // Increment iterator and return the value + SetLocalInt(oPC, "PRC_Teleport_Array_Iterator", nInd + 1); + return GetPersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_" + IntToString(nInd)); +} + +struct metalocation GetNthStoredTeleportTargetLocation(object oPC, int nInd) +{ + // If out of lower or upper bound, return null + if(nInd < 0 || nInd > GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME) - 1) + return GetNullMetalocation(); + + // Return the nth stored location + return GetPersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_" + IntToString(nInd)); +} + +int GetNumberOfStoredTeleportTargetLocations(object oPC) +{ + return GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME); +} + +int GetHasTeleportQuickSelection(object oPC, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION) +{ + //SendMessageToPC(oPC, "GetLocalInt(oPC, PRC_Teleport_Quickselection): " + IntToString(GetLocalInt(oPC, "PRC_Teleport_Quickselection"))); + //SendMessageToPC(oPC, "GetIsMetalocationValid(GetLocalMetalocation(oPC, PRC_Teleport_Quickselection)): " + IntToString(GetIsMetalocationValid(GetLocalMetalocation(oPC, "PRC_Teleport_Quickselection")))); + if(nSlot < -1 || !nSlot || nSlot > PRC_NUM_TELEPORT_QUICKSELECTS) + return FALSE; + + if(nSlot == PRC_TELEPORT_ACTIVE_QUICKSELECTION) + return GetLocalInt(oPC, "PRC_Teleport_Quickselection");/* && + GetIsMetalocationValid(GetLocalMetalocation(oPC, "PRC_Teleport_Quickselection"));*/ + else + return GetLocalInt(oPC, "PRC_Teleport_QuickSelection_" + IntToString(nSlot)); +} + +struct metalocation GetActiveTeleportQuickSelection(object oPC, int bClear = FALSE) +{ + if(GetHasTeleportQuickSelection(oPC, PRC_TELEPORT_ACTIVE_QUICKSELECTION)) + { + struct metalocation mlocL = GetLocalMetalocation(oPC, "PRC_Teleport_Quickselection"); + if(bClear) + RemoveTeleportQuickSelection(oPC, PRC_TELEPORT_ACTIVE_QUICKSELECTION); + return mlocL; + } + else + return GetNullMetalocation(); +} + +struct metalocation GetTeleportQuickSelection(object oPC, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION) +{ + // Make sure the slot is within allowed range + if(nSlot < -1 || !nSlot || nSlot > PRC_NUM_TELEPORT_QUICKSELECTS) + return GetNullMetalocation(); + + // The active quickselection was asked + if(nSlot == PRC_TELEPORT_ACTIVE_QUICKSELECTION) + return GetActiveTeleportQuickSelection(oPC, FALSE); + // The contents of a slot were asked, an the slot in question is not empty + else if(GetLocalInt(oPC, "PRC_Teleport_QuickSelection_" + IntToString(nSlot))) + return GetLocalMetalocation(oPC, "PRC_Teleport_QuickSelection_" + IntToString(nSlot)); + // The slot is empty + else + return GetNullMetalocation(); +} + +void SetTeleportQuickSelection(object oPC, struct metalocation mlocL, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION) +{ + // Make sure the slot is within allowed range + if(nSlot < -1 || !nSlot || nSlot > PRC_NUM_TELEPORT_QUICKSELECTS) + return; + + // Set either the active selection, or a slot depending on nSlot + if(nSlot == PRC_TELEPORT_ACTIVE_QUICKSELECTION) + { + SetLocalInt(oPC, "PRC_Teleport_Quickselection", TRUE); // Mark quickselection as active + SetLocalMetalocation(oPC, "PRC_Teleport_Quickselection", mlocL); + } + else + { + SetLocalInt(oPC, "PRC_Teleport_QuickSelection_" + IntToString(nSlot), TRUE); // Mark quickselection as existing + SetLocalMetalocation(oPC, "PRC_Teleport_QuickSelection_" + IntToString(nSlot), mlocL); + } +} + +void RemoveTeleportQuickSelection(object oPC, int nSlot = PRC_TELEPORT_ACTIVE_QUICKSELECTION) +{ + DeleteLocalInt(oPC, "PRC_Teleport_Quickselection"); + DeleteLocalMetalocation(oPC, "PRC_Teleport_Quickselection"); +} + +void RemoveCurrentTeleportTargetLocation(object oPC) +{ + // Return if GetFirstStoredTeleportTargetLocation() or GetNextStoredTeleportTargetLocation() hasn't been called previously. + int nInd = GetLocalInt(oPC, "PRC_Teleport_Array_Iterator"); + if(!nInd) return; + + // Remove the location + RemoveNthTeleportTargetLocation(oPC, nInd); + + // Delete the iteration counter to keep potential errors down. + DeleteLocalInt(oPC, "PRC_Teleport_Array_Iterator"); +} + +void RemoveNthTeleportTargetLocation(object oPC, int nInd) +{ + // If out of lower or upper bound, return + if(nInd < 0 || nInd > GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME) - 1) + return; + + // Get the index of the last element in the array and move elements back if needed + int nMax = GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME) - 1; + for(; nInd < nMax; nInd++) + { + SetPersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_" + IntToString(nInd), + GetPersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_" + IntToString(nInd + 1)) + ); + // Move the map pin existence marker if it's present + if(GetLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME + "_HasMapPin_" + IntToString(nInd + 1))) + SetLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME + "_HasMapPin_" + IntToString(nInd), TRUE); + } + + // Remove the last element and mark the size change + DeletePersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_" + IntToString(nMax)); + DeleteLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME + "_HasMapPin_" + IntToString(nMax)); + SetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME, nMax); + + // Delete the iteration counter to keep potential errors down. + DeleteLocalInt(oPC, "PRC_Teleport_Array_Iterator"); +} + +int AddTeleportTargetLocation(object oPC, location locToAdd, string sName) +{ + return AddTeleportTargetLocationAsMeta(oPC, LocationToMetalocation(locToAdd, sName)); +} + +int AddTeleportTargetLocationAsMeta(object oPC, struct metalocation mlocToAdd) +{ + // Make sure the metalocation is valid + if(!GetIsMetalocationValid(mlocToAdd)) + {// "Location could not be marked due to technical limitations - unable to uniquely identify area." + SendMessageToPCByStrRef(oPC, 16825304); + return FALSE; + } + + // Array size check. If no limit is defined via switch, default to 50. + int nInd = GetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME); // Array elements begin at index 0 + int nMax = GetPRCSwitch(PRC_TELEPORT_MAX_TARGET_LOCATIONS) ? + GetPRCSwitch(PRC_TELEPORT_MAX_TARGET_LOCATIONS) : + 50; + if(nInd >= nMax) + {// You have reached the maximum allowed teleport locations ( ).\nYou must remove at least one stored location before you can add new locations. + SendMessageToPC(oPC, GetStringByStrRef(16825294) + IntToString(nMax) + GetStringByStrRef(16825295)); + return FALSE; + } + + // All checks passed, store the location, increment array size and return + SetPersistantLocalMetalocation(oPC, PRC_TELEPORT_ARRAY_NAME + "_" + IntToString(nInd), mlocToAdd); + SetPersistantLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME, nInd + 1); + return TRUE; +} + +void TeleportLocationsToMapPins(object oPC) +{ + // This function is only useful for PCs + if(!GetIsPC(oPC)) return; + + // Iterate over all stored metalocations + int nMax = GetNumberOfStoredTeleportTargetLocations(oPC); + int i; + for(; i < nMax; i++) + { + // Add map pins to those locations that do not already have one + if(!GetLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME + "_HasMapPin_" + IntToString(i))) + { + CreateMapPinFromMetalocation(GetNthStoredTeleportTargetLocation(oPC, i) , oPC); + SetLocalInt(oPC, PRC_TELEPORT_ARRAY_NAME + "_HasMapPin_" + IntToString(i), TRUE); + } + } +} + + +int GetCanTeleport(object oCreature, location lTarget, int bMovesCreature = FALSE, int bInform = FALSE, int bPublic = FALSE) +{ + int bReturn = TRUE; + // First, check global switch to turn off teleporting + if(GetPRCSwitch(PRC_DISABLE_TELEPORTATION)) + bReturn = FALSE; + + // If the creature would be actually moved around, do some extra tests + if(bMovesCreature) + { + // Check area-specific variables + object oSourceArea = GetArea(oCreature); + object oTargetArea = GetAreaFromLocation(lTarget); + // Teleportation is between areas + if(oSourceArea != oTargetArea) + { + // Check forbiddance variable on the current area + if(GetLocalInt(oSourceArea, PRC_DISABLE_TELEPORTATION_IN_AREA) & PRC_DISABLE_TELEPORTATION_FROM_AREA) + bReturn = FALSE; + // Check forbiddance variable on the target area + if(GetLocalInt(oTargetArea, PRC_DISABLE_TELEPORTATION_IN_AREA) & PRC_DISABLE_TELEPORTATION_TO_AREA) + bReturn = FALSE; + } + // Teleportation within an area + else if(GetLocalInt(oSourceArea, PRC_DISABLE_TELEPORTATION_IN_AREA) & PRC_DISABLE_TELEPORTATION_WITHIN_AREA) + bReturn = FALSE; + } + + + // Check forbiddance variable on the creature + if(GetLocalInt(oCreature, PRC_DISABLE_CREATURE_TELEPORT)) + bReturn = FALSE; + + // Tell the creature about failure, if necessary + if(bInform & !bReturn) + { + // "Something prevents your extra-dimensional movement!" + FloatingTextStrRefOnCreature(16825298, oCreature, bPublic); + } + + return bReturn; +} + +void GetTeleportingObjects(object oCaster, int nCasterLvl, int bSelfOrParty) +{ + // Store list of objects to teleport in an array on the caster + // First, null the array + array_delete(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY); + array_create(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY); + + // Init array index variable + int i = 0; + + // Casting Dimension Door always teleports at least the caster + array_set_object(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY, i++, oCaster); + + // If teleporting party, get all faction members fitting in within 10 feet. (Should be dependent on caster's reach, + // but that would mean < Small creatures could not teleport their party at all and even Mediums with their 5 foot + // reach might have trouble considering the position tracking code's shakiness) + if(bSelfOrParty) + { + // Carry amount variables + int nMaxCarry = nCasterLvl / 3, + nCarry = 0, + nIncrease; + + nMaxCarry += GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oCaster); + + location lSelf = GetLocation(oCaster); + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(10.0f), lSelf); + while(GetIsObjectValid(oTarget)) + { + // Check if the target is member of the same faction as the caster. If it is, teleport it along. + if(GetFactionEqual(oCaster, oTarget) && oTarget != oCaster) + { + // Calculate how many carry slots the creature would take + nIncrease = GetCreatureSize(oTarget) == CREATURE_SIZE_HUGE ? 4 : + GetCreatureSize(oTarget) == CREATURE_SIZE_LARGE ? 2 : + 1; + + // Add others if the caster can carry them + if(nCarry + nIncrease <= nMaxCarry) + { + nCarry += nIncrease; + array_set_object(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY, i++, oTarget); + } + // Otherwise inform the caster that they couldn't take the creature along + else // "You do not have anough carrying capacity to teleport X" + SendMessageToPC(oCaster, GetStringByStrRef(16825302) + " " + GetName(oTarget)); + } + + oTarget = GetNextObjectInShape(SHAPE_SPHERE, FeetToMeters(10.0f), lSelf); + } + } + /* + // Targeting one other being in addition to self. If it's hostile, it gets SR and a Will save. + else if(nSpellID = SPELLID_TELEPORT_TARGET) + { + object oTarget = PRCGetSpellTargetObject(); + if(GetIsHostile()) + { + PRCSignalSpellEvent(oTarget, TRUE, nSpellID); // Let the target know it was cast a spell at + + //SR + if(!PRCDoResistSpell(oCaster, oTarget, nCasterLevel + SPGetPenetr())) + { + // Will save + if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, PRCGetSaveDC(oTarget, oCaster), SAVING_THROW_TYPE_SPELL)) + { + array_set_object(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY, i++, oTarget); + } } } + // Not hostile, just add it to the list. + else + { + PRCSignalSpellEvent(oTarget, FALSE, nSpellID); // Let the target know it was cast a spell at + array_set_object(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY, i++, oTarget); + } + } + */ +} + +location GetTeleportError(location lOriginal, object oUser, int bNormallyErroless = FALSE, int bRecursing = FALSE) +{ + if(DEBUG) DoDebug("prc_inc_teleport: GetTeleportError():\n" + + "lOriginal = " + DebugLocation2Str(lOriginal) + "\n" + + "oUser = " + DebugObject2Str(oUser) + "\n" + + "bNormallyErroless = " + DebugBool2String(bNormallyErroless) + "\n" + + "bRecursing = " + DebugBool2String(bRecursing) + "\n" + ); + + int nOverrideValue = GetLocalInt(GetAreaFromLocation(lOriginal), PRC_FORCE_TELEPORTATION_RESULT); + + // If the effect cannot normally error and there is no override active, just return lOriginal + if(bNormallyErroless && !nOverrideValue) + return lOriginal; + + // Roll for the result. If recursing from a mishap, roll d20 + 80, otherwise roll d100 + int nRoll = bRecursing ? d20() + 80 : d100(); + + if (GetLevelByClass(CLASS_TYPE_WAYFARER_GUIDE, oUser) >= 3) + { + // Roll twice, take the better roll + int nRoll2 = bRecursing ? d20() + 80 : d100(); + if (nRoll > nRoll2) nRoll = nRoll2; + } + + // If an override value is specified, force the roll value. Override only applies in the first call, not on subsequent times + if(nOverrideValue && !bRecursing) + { + switch(nOverrideValue) + { + case PRC_FORCE_TELEPORTATION_RESULT_ONTARGET: nRoll = 1; break; + case PRC_FORCE_TELEPORTATION_RESULT_OFFTARGET: nRoll = 91; break; + case PRC_FORCE_TELEPORTATION_RESULT_WAYOFFTARGET: nRoll = 95; break; + case PRC_FORCE_TELEPORTATION_RESULT_MISHAP: nRoll = 99; break; + } + } + + if (GetLocalInt(GetAreaFromLocation(lOriginal), "BlackLabyrinthTeleport")) nRoll = 91; + + int nLabyrinth = GetLocalInt(oUser, "BlackLabyrinthTeleport"); + if(!PRCMySavingThrow(SAVING_THROW_WILL, oUser, nLabyrinth, SAVING_THROW_TYPE_SPELL) && nLabyrinth) nRoll = 99; + else if (nLabyrinth) nRoll = 91; + + if(DEBUG) DoDebug("prc_inc_teleport: GetTeleportError(): Roll is " + IntToString(nRoll) + ", forced = " + DebugBool2String(nOverrideValue)); + + /* On Target Off Target Way Off Target Mishap + * 01–90 91–94 95–98 99–100 + */ + // On Target - Return original location + if(nRoll <= 90) + { + if(DEBUG) DoDebug("prc_inc_teleport: GetTeleportError(): On Target - Returning original location"); + return lOriginal; + } + // Off Target - Get a random location in same area + else if(nRoll <= 94) + { + object oArea = GetAreaFromLocation(lOriginal); + int nAreaW = GetAreaWidth(oArea); + int nAreaH = GetAreaHeight(oArea); + + vector vNew = Vector(Random(nAreaW) * 10.0f + 5.0f, + Random(nAreaH) * 10.0f + 5.0f, + GetPositionFromLocation(lOriginal).z + ); + location lNew = Location(oArea, vNew, 0.0f); + if(DEBUG) DoDebug("prc_inc_teleport: GetTeleportError(): Off Target - Returning " + DebugLocation2Str(lNew)); + return lNew; + } + // Way Off Target - Random location among stored teleport choices, or if there are no others, just stay where the user is + else if(nRoll <= 98) + { + int nLocs = GetNumberOfStoredTeleportTargetLocations(oUser); + int nRand = Random(nLocs); + location lReplacement = MetalocationToLocation(GetNthStoredTeleportTargetLocation(oUser, nRand)); + + if(DEBUG) DoDebug("prc_inc_teleport: GetTeleportError(): Way Off Target - Replacement location rolled: " + DebugLocation2Str(lReplacement) + "\n" + + "Replacement location is useable: " + DebugBool2String(!(nLocs == 0 || lReplacement == lOriginal)) + ); + + if(nLocs == 0 || lReplacement == lOriginal) + return GetLocation(oUser); + else + return lReplacement; + } + // Mishap: + // You and anyone else teleporting with you have gotten “scrambled.” + // You each take 1d10 points of damage, and you reroll on the chart to see where you wind up. + // For these rerolls, roll 1d20+80. Each time “Mishap” comes up, the characters take more damage and must reroll. + else + { + if(DEBUG) DoDebug("prc_inc_teleport: GetTeleportError(): Mishap - damaging people"); + object oTarget; + int i; + for(i = 0; i < array_get_size(oUser, PRC_TELEPORTING_OBJECTS_ARRAY); i++) + { + oTarget = array_get_object(oUser, PRC_TELEPORTING_OBJECTS_ARRAY, i); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d10(), DAMAGE_TYPE_MAGICAL), oTarget); + } + + return GetTeleportError(lOriginal, oUser, bNormallyErroless, TRUE); + } +} + +void DisallowTeleport(object oTarget) +{ + if(DEBUG) DoDebug("DisallowTeleport():\n" + + "oTarget = " + DebugObject2Str(oTarget) + "\n" + + "\n" + + "New blocking variable value: " + IntToString(GetLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT) + 1) + "\n" + ); + SetLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT, + GetLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT) + 1 + ); +} + +void AllowTeleport(object oTarget, int bClearAll = FALSE) +{ + if(DEBUG) DoDebug("AllowTeleport():\n" + + "oTarget = " + DebugObject2Str(oTarget) + "\n" + + "bClearAll = " + DebugBool2String(bClearAll) + "\n" + + "\n" + + "Old blocking variable value: " + IntToString(GetLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT)) + ); + int nValue = GetLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT) - 1; + if((nValue > 0) && !bClearAll) + SetLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT, nValue); + else + DeleteLocalInt(oTarget, PRC_DISABLE_CREATURE_TELEPORT); + + if(DEBUG) DoDebug("New blocking variable value: " + (((nValue > 0) && !bClearAll) ? IntToString(nValue) : "Deleted")); +} + +void ShadowPounce(object oPC) +{ + if (GetHasFeat(FEAT_SHADOW_POUNCE, oPC)) + { + location lTarget = GetLocation(oPC); + // Use the function to get the closest creature as a target + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget)) + { + if(oAreaTarget != oPC && // Not you + GetIsInMeleeRange(oPC, oAreaTarget) && // They must be in melee range + GetIsEnemy(oAreaTarget, oPC)) // Only enemies + { + PerformAttackRound(oAreaTarget, oPC, EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY), 0.0, 0, 0, 0, TRUE, "Shadow Pounce Hit", "Shadow Pounce Miss", FALSE, FALSE, TRUE); + break; //End loop + } + //Select the next target within the spell shape. + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + } +} \ No newline at end of file diff --git a/src/include/prc_inc_template.nss b/src/include/prc_inc_template.nss new file mode 100644 index 0000000..e5550ef --- /dev/null +++ b/src/include/prc_inc_template.nss @@ -0,0 +1,318 @@ +//:://///////////////////////////////////////////// +//:: Name Template and Weapons of Legacy Include +//:: FileName prc_inc_template +//::////////////////////////////////////////////// +/* + This is the main include file for the template system + Deals with applying templates and interacting with the 2da + + This does the above, but for the benefits of Weapons of + Legacy as well, since they function similarly to templates. +*/ +//::////////////////////////////////////////////// +//:: Created By: Primogenitor +//:: Created On: 18/4/06 +//::////////////////////////////////////////////// + +//void main (){} + +//Checks if the target has the template or not. +//returns 1 if it does, 0 if it doesnt or if its an invalid target +int GetHasTemplate(int nTemplate, object oPC = OBJECT_SELF); + +//Get total template ECL +int GetTemplateLA(object oPC); + +//add a template to the creature +//will not work if it already has the template +//will not work on non-creatures +//if bApply is false, this can test if the template is applicable or not +int ApplyTemplateToObject(int nTemplate, object oPC = OBJECT_SELF, int bApply = TRUE); + +int RemoveTemplateFromObject(int nTemplate, object oPC = OBJECT_SELF); + +/** + * Determines whether the PC is a legal target for the weapon of legacy + * If so, spawns and equips the item, as well as charging them gold for it + * + * @param oPC The new owner of the Weapon of Legacy + */ +int ApplyWoLToObject(int nWoL, object oPC = OBJECT_SELF, int bApply = TRUE); + +/** + * Determines whether the PC has too many hit points and removes them + * This is a Hit Point Loss penalty applied by weapons of legacy + * + * @param oPC The owner of the Weapon of Legacy + */ +void WoLHealthPenaltyHB(object oPC); + +/** + * Burns one spell per day of the chosen spell level + * This is a Spell Slot penalty applied by weapons of legacy + * + * @param oPC The owner of the Weapon of Legacy + * @param nSlot The level of spell to expend + */ +void WoLSpellSlotPenalty(object oPC, int nSlot); + +/** + * Applies the Weapon of Legacy Rituals + * + * @param oPC The owner of the Weapon of Legacy + */ +void UpgradeLegacy(object oPC); + +/** + * Gets uses per day of Legacy abilities + * + * @param oPC The owner of the Weapon of Legacy + * @param nSLA The SLA to check + */ +int GetLegacyUses(object oPC, int nSLA); + +/** + * Sets uses per day of Legacy abilities + * + * @param oPC The owner of the Weapon of Legacy + * @param nSLA The SLA to set + */ +void SetLegacyUses(object oPC, int nSLA); + +/** + * Clears all uses per day on rest + * + * @param oPC The owner of the Weapon of Legacy + */ +void ClearLegacyUses(object oPC); + +#include "prc_inc_function" +#include "prc_template_con" +#include "inc_persist_loca" +#include "prc_inc_burn" + +int GetHasTemplate(int nTemplate, object oPC = OBJECT_SELF) +{ + int bHasTemplate = GetPersistantLocalInt(oPC, "template_"+IntToString(nTemplate)); + if(bHasTemplate && DEBUG) + DoDebug("GetHasTemplate("+IntToString(nTemplate)+", "+GetName(oPC)+") is true"); + return bHasTemplate; +} + +int GetTemplateLA(object oPC) +{ + return GetPersistantLocalInt(oPC, "template_LA"); + /* + //Loop could TMI avoid it + int nLA; + //loop over all templates and see if the player has them + int i; + for(i=0;i<200;i++) + { + if(GetHasTemplate(i, oPC)) + nLA += StringToInt(Get2DACache("templates", "LA", i)); + } + return nLA;*/ +} + +int RemoveTemplateFromObject(int nTemplate, object oPC = OBJECT_SELF) +{ + //:: Sanity check + if(!GetHasTemplate(nTemplate, oPC)) + return FALSE; + + //:: Remove the template from the array + if(persistant_array_exists(oPC, "templates")) + { + persistant_array_shrink(oPC, "templates", persistant_array_get_size(oPC, "templates")-nTemplate); + persistant_array_delete(oPC, "templates"); + } + + //:: Delete template's markers + DeletePersistantLocalInt(oPC, "template_"+IntToString(nTemplate)); + DeletePersistantLocalInt(oPC, "template_LA"); + + DelayCommand(0.01, EvalPRCFeats(oPC)); + return TRUE; +} + +int ApplyTemplateToObject(int nTemplate, object oPC = OBJECT_SELF, int bApply = TRUE) +{ + //templates never stack, so dont let them + if(GetHasTemplate(nTemplate, oPC)) + return FALSE; + + //sanity checks + if(GetObjectType(oPC) != OBJECT_TYPE_CREATURE) + return FALSE; + if(nTemplate < 0 || nTemplate > 250) + return FALSE; + + //test if it can be applied + string sScript = Get2DACache("templates", "TestScript", nTemplate); + if(sScript != "" + && ExecuteScriptAndReturnInt(sScript, oPC)) + return FALSE; + + //if not applying it, abort at this point + if(!bApply) + return TRUE; + + //run the application script + sScript = Get2DACache("templates", "SetupScript", nTemplate); + if(sScript != "") + ExecuteScript(sScript, oPC); + + //mark the PC as possessing the template + SetPersistantLocalInt(oPC, "template_"+IntToString(nTemplate), TRUE); + //adjust the LA marker accordingly + if (!(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC) >= 20 && nTemplate == TEMPLATE_LICH)) + { + if(DEBUG) DoDebug("ApplyTemplateToObject(): Adding Template LA"); + SetPersistantLocalInt(oPC, "template_LA", + GetPersistantLocalInt(oPC, "template_LA")+StringToInt(Get2DACache("templates", "LA", nTemplate))); + } + //add the template to the array + if(!persistant_array_exists(oPC, "templates")) + persistant_array_create(oPC, "templates"); + persistant_array_set_int(oPC, "templates", persistant_array_get_size(oPC, "templates"), nTemplate); + + + //run the main PRC feat system so we trigger any other feats we've borrowed + DelayCommand(0.01, EvalPRCFeats(oPC)); + //ExecuteScript("prc_feat", oPC); + //ran, evaluated, done + return TRUE; +} + +int ApplyWoLToObject(int nWoL, object oPC = OBJECT_SELF, int bApply = TRUE) +{ + //Weapons of Legacy never stack, so dont let them + if(GetPersistantLocalInt(oPC, "LegacyOwner")) + return FALSE; + + //sanity checks + if(GetObjectType(oPC) != OBJECT_TYPE_CREATURE) + return FALSE; + if(nWoL < 0 || nWoL > 200) + return FALSE; + + //test if it can be applied + string sScript = Get2DACache("wol_items", "TestScript", nWoL); + if(sScript != "" && ExecuteScriptAndReturnInt(sScript, oPC)) + return FALSE; + + // Need to be able to pay for it + int nGold = StringToInt(Get2DACache("wol_items", "Cost", nWoL)); + if (nGold > GetGold(oPC)) + return FALSE; + + //if not applying the weapon of legacy, abort at this point + if(!bApply) + return TRUE; + + //mark the PC as possessing a Weapon of Legacy. You only get one, so we store the number + SetPersistantLocalInt(oPC, "LegacyOwner", nWoL); + // Pay for the item, create it and equip it + TakeGoldFromCreature(nGold, oPC, TRUE); + object oWoL = CreateItemOnObject(Get2DACache("wol_items", "ResRef", nWoL), oPC); + SetIdentified(oWoL, TRUE); + SetDroppableFlag(oWoL, FALSE); + SetItemCursedFlag(oWoL, TRUE); + AssignCommand(oWoL, SetIsDestroyable(FALSE, FALSE, FALSE)); + SetName(oWoL, GetStringByStrRef(StringToInt(Get2DACache("wol_items", "Name", nWoL)))); + DelayCommand(0.25, AssignCommand(oPC, ActionEquipItem(oWoL, StringToInt(Get2DACache("wol_items", "Slot", nWoL))))); + + // Make the twin + if (GetTag(oWoL) == "WOL_Devious") + { + oWoL = CreateItemOnObject("prc_wol_vicious", oPC); + SetIdentified(oWoL, TRUE); + SetDroppableFlag(oWoL, FALSE); + SetItemCursedFlag(oWoL, TRUE); + AssignCommand(oWoL, SetIsDestroyable(FALSE, FALSE, FALSE)); + DelayCommand(0.25, AssignCommand(oPC, ActionEquipItem(oWoL, INVENTORY_SLOT_LEFTHAND))); + } + + //run the main PRC feat system so we trigger any other feats we've borrowed + DelayCommand(0.01, EvalPRCFeats(oPC)); + //ran, evaluated, done + return TRUE; +} + +void WoLHealthPenaltyHB(object oPC) +{ + int nCurHP = GetCurrentHitPoints(oPC); + int nMaxHP = GetMaxHitPoints(oPC); + int nPen = GetLocalInt(oPC, "WoLHealthPenalty"); + + // Does the PC have too many hit points? + if (nCurHP > (nMaxHP - nPen)) + { + int nDam = nCurHP - (nMaxHP - nPen); + //if (!GetIsResting(oPC))ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, DAMAGE_TYPE_POSITIVE), oPC); + if (!GetIsResting(oPC)) SetCurrentHitPoints(oPC, nMaxHP - nPen); + } + DelayCommand(0.25, WoLHealthPenaltyHB(oPC)); +} + +void WoLSpellSlotPenalty(object oPC, int nSlot) +{ + if (DEBUG) DoDebug("WoLSpellSlotPenalty nSlot "+IntToString(nSlot)); + if (GetLocalInt(oPC, "WOLSlotBurned")) return; // Already burned for the day + + if (nSlot > 0) + { + SetLocalInt(oPC, "BurnSpellLevel", nSlot); + BurnSpell(oPC); + DeleteLocalInt(oPC, "BurnSpellLevel"); + SetLocalInt(oPC, "WOLSlotBurned", TRUE); + } +} + +void UpgradeLegacy(object oPC) +{ + int nLegacy = GetPersistantLocalInt(oPC, "LegacyRitual"); + SetPersistantLocalInt(oPC, "LegacyRitual", nLegacy+1); + // Apply the feat and the updated abilities + ExecuteScript("prc_templates", oPC); +} + +int GetLegacyUses(object oPC, int nSLA) +{ + // This makes sure everything is stored using the proper number + return GetLocalInt(oPC, "PRC_WoLUses" + IntToString(nSLA)); +} + +void SetLegacyUses(object oPC, int nSLA) +{ + // Uses are stored for each SLA by SpellId + int nNum = GetLocalInt(oPC, "PRC_WoLUses" + IntToString(nSLA)); + // Store the number of times per day its been cast succesfully + SetLocalInt(oPC, "PRC_WoLUses" + IntToString(nSLA), (nNum + 1)); +} + +void ClearLegacyUses(object oPC) +{ + // Uses are stored by SpellId + // So we loop em all and blow em away + // i is the SpellId + int i; + for(i = 16390; i < 16600; i++) + { + DeleteLocalInt(oPC, "PRC_WoLUses" + IntToString(i)); + } + + // Clear markers on rest + DeleteLocalInt(oPC, "WOLSlotBurned"); + DeleteLocalInt(oPC, "WoLPsiPointsPenalty"); + DeleteLocalInt(oPC, "AradrosTHP"); + DeleteLocalInt(oPC, "WarriorsSurgeUse"); +} + +void JumpToEncounterArea(object oPC, int nWoL) +{ + SetLocalLocation(oPC, "EA_Return", GetLocation(oPC)); + CreateArea(Get2DACache("wol_items", "Area", nWoL)); + DelayCommand(1.5, AssignCommand(oPC, JumpToLocation(GetLocation(GetWaypointByTag(Get2DACache("wol_items", "Waypoint", nWoL)))))); +} \ No newline at end of file diff --git a/src/include/prc_inc_turning.nss b/src/include/prc_inc_turning.nss new file mode 100644 index 0000000..5504819 --- /dev/null +++ b/src/include/prc_inc_turning.nss @@ -0,0 +1,650 @@ +//:://///////////////////////////////////////////// +//:: Turn undead include +//:: prc_inc_turning +//:://///////////////////////////////////////////// +/** @file + Defines functions that seem to have something + to do with Turn Undead (and various other + stuff). +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + + + + + + + + + + + + +//gets the number of class levels that count for turning +int GetTurningClassLevel(object oCaster = OBJECT_SELF, int nTurnType = SPELL_TURN_UNDEAD); + +//Returns the turning charisma modifier of OBJECT_SELF +int GetTurningCharismaMod(int nTurnType); + +//this adjusts the highest HD of undead turned +int GetTurningCheckResult(int nLevel, int nTurningCheck); + +//gets the equivalent HD total for turning purposes +//includes turn resistance and SR for outsiders +int GetHitDiceForTurning(object oTarget, int nTurnType, int nTargetRace); + +int GetCommandedTotalHD(int nTurnType = SPELL_TURN_UNDEAD, int bUndeadMastery = FALSE); + +//various sub-turning effect funcions +void DoTurn(object oTarget); +void DoDestroy(object oTarget); +void DoRebuke(object oTarget); +void DoCommand(object oTarget); + +// This uses the turn type to determine whether the target should be turned or rebuked +// Return values: +// 0 - target can not be turned/rebuked +// 1 - target can be rebuked +// 2 - target can be turned +int GetIsTurnOrRebuke(object oTarget, int nTurnType, int nTargetRace); + +// What this does is check to see if there is an discrete word +// That matches the input, and if there is returns TRUE +// (please use sName string in lowercase) +int CheckTargetName(object oTarget, string sName); + +int GetIsAirCreature(object oCreature, int nAppearance); +int GetIsEarthCreature(object oCreature, int nAppearance); +int GetIsFireCreature(object oCreature, int nAppearance); +int GetIsWaterCreature(object oCreature, int nAppearance); +int GetIsColdCreature(object oCreature, int nAppearance); +int GetIsReptile(object oCreature, int nAppearance); +int GetIsSpider(object oCreature, int nAppearance); + +// Does the cold damage for this feat +void FaithInTheFrost(object oPC, object oTarget); + +// Does the fire damage for this feat +void LightOfAurifar(object oPC, object oTarget); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_racial" +#include "inc_utility" +#include "bnd_inc_bndfunc" +#include "prc_inc_template" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +const int ACTION_REBUKE = 1; +const int ACTION_TURN = 2; + +//private function +int GetIsTurnOrRebuke(object oTarget, int nTurnType, int nTargetRace) +{ + //is not an enemy + if(GetIsFriend(oTarget)/* || GetFactionEqual(oTarget)*/) + return FALSE; + + //already turned + //NOTE: At the moment this breaks down in "turning conflicts" where clerics try to + //turn each others undead. Fix later. + //if(GetHasSpellEffect(nTurnType, oTarget)) + // return FALSE; + if(PRCGetHasEffect(EFFECT_TYPE_TURNED, oTarget)) + return FALSE; + + int nTargetAppearance = GetAppearanceType(oTarget); + int nReturn = FALSE; + + switch(nTurnType) + { + case SPELL_TURN_UNDEAD: + { + if(nTargetRace == RACIAL_TYPE_UNDEAD) + { + // Evil clerics rebuke undead, otherwise turn + // Dread Necro, True Necro and Master of Shrouds always rebuke or command + if(GetAlignmentGoodEvil(OBJECT_SELF) == ALIGNMENT_EVIL + || GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER) + || GetLevelByClass(CLASS_TYPE_TRUENECRO) + || GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS) + || GetLevelByClass(CLASS_TYPE_NIGHTSTALKER) > 3) + nReturn = ACTION_REBUKE; + else + nReturn = ACTION_TURN; + } + else if(nTargetRace == RACIAL_TYPE_OUTSIDER && GetHasFeat(FEAT_EPIC_PLANAR_TURNING)) + { + // Evil clerics turn non-evil outsiders, and rebuke evil outsiders + if(GetAlignmentGoodEvil(OBJECT_SELF) == ALIGNMENT_EVIL) + { + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_EVIL) + nReturn = ACTION_TURN; + else + nReturn = ACTION_REBUKE; + } + // Good clerics turn non-good outsiders, and rebuke good outsiders + else + { + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD) + nReturn = ACTION_TURN; + else + nReturn = ACTION_REBUKE; + } + } + break; + } + case SPELL_TURN_OUTSIDER: + { + if(nTargetRace == RACIAL_TYPE_OUTSIDER) + { + // Evil clerics turn non-evil outsiders, and rebuke evil outsiders + if(GetAlignmentGoodEvil(OBJECT_SELF) == ALIGNMENT_EVIL) + { + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_EVIL) + nReturn = ACTION_TURN; + else + nReturn = ACTION_REBUKE; + } + // Good clerics turn non-good outsiders, and rebuke good outsiders + else + { + if(GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD) + nReturn = ACTION_TURN; + else + nReturn = ACTION_REBUKE; + } + } + break; + } + case SPELL_TURN_BLIGHTSPAWNED: + { + // Rebuke/Command blightspawned, either by tag from Blight touch + // Or from having a Blightlord master + if(GetTag(oTarget) == "prc_blightspawn" || GetLevelByClass(CLASS_TYPE_BLIGHTLORD, GetMaster(oTarget))) + { + nReturn = ACTION_REBUKE; + } + // Rebuke/Command evil animals and plants + else if(nTargetRace == RACIAL_TYPE_ANIMAL || nTargetRace == RACIAL_TYPE_PLANT) + { + if(GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL) + nReturn = ACTION_REBUKE; + } + break; + } + case SPELL_TURN_OOZE: + { + // Slime domain rebukes or commands oozes + if(nTargetRace == RACIAL_TYPE_OOZE) + nReturn = ACTION_REBUKE; + + break; + } + case SPELL_TURN_PLANT: + { + // Plant domain rebukes or commands plants + if(GetPRCSwitch(PRC_BIOWARE_PLANT_DOMAIN_POWER)) + { + if(nTargetRace == RACIAL_TYPE_VERMIN) + nReturn = ACTION_TURN; + } + else + { + if(nTargetRace == RACIAL_TYPE_PLANT) + nReturn = ACTION_REBUKE; + } + break; + } + case SPELL_TURN_AIR: + { + if(GetIsAirCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + else if(GetIsEarthCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_TURN; + + break; + } + case SPELL_TURN_EARTH: + { + if(GetIsAirCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_TURN; + + else if(GetIsEarthCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + break; + } + case SPELL_TURN_FIRE: + { + if(GetIsFireCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + else if(GetIsWaterCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_TURN; + + break; + } + case SPELL_TURN_WATER: + { + if(GetIsFireCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_TURN; + + else if(GetIsWaterCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + break; + } + case SPELL_TURN_REPTILE: + { + if(GetIsReptile(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + break; + } + case SPELL_TURN_SPIDER: + { + if(GetIsSpider(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + break; + } + case SPELL_TURN_COLD: + { + if(GetIsFireCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_TURN; + + else if(GetIsColdCreature(oTarget, nTargetAppearance)) + nReturn = ACTION_REBUKE; + + break; + } + } + + //always turn baelnorns & archliches + if(GetLevelByClass(CLASS_TYPE_BAELNORN, oTarget) || GetHasFeat(FEAT_TEMPLATE_ARCHLICH_MARKER, oTarget)) //:: Archlich + nReturn = ACTION_TURN; + + //Immunity check + if(nReturn == ACTION_TURN && GetHasFeat(FEAT_TURNING_IMMUNITY, oTarget)) + nReturn = FALSE; + + if(nReturn == ACTION_REBUKE && GetHasFeat(FEAT_IMMUNITY_TO_REBUKING, oTarget)) + nReturn = FALSE; + + return nReturn; +} +//ok +void DoTurn(object oTarget) +{ + //create the effect + effect eTurn = EffectLinkEffects(EffectTurned(), EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR)); + eTurn = EffectLinkEffects(eTurn, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE)); + eTurn = SupernaturalEffect(eTurn); + //apply the effect for 60 seconds + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eTurn, oTarget, 60.0); + FaithInTheFrost(OBJECT_SELF, oTarget); + LightOfAurifar(OBJECT_SELF, oTarget); +} +//ok +void DoDestroy(object oTarget) +{ + //supernatural so it penetrates immunity to death + ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDeath()), oTarget);//EffectDeath(TRUE)? +} + +void DoRebuke(object oTarget) +{ + //rebuke effect + //The character is frozen in fear and can take no actions. + //A cowering character takes a -2 penalty to Armor Class and loses + //her Dexterity bonus (if any). + //create the effect + effect eRebuke = EffectEntangle(); //this removes dex bonus + eRebuke = EffectLinkEffects(eRebuke, EffectACDecrease(2)); + eRebuke = EffectLinkEffects(eRebuke, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR)); + eRebuke = EffectLinkEffects(eRebuke, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE)); + //apply the effect for 60 seconds + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eRebuke, oTarget, 60.0); + //handle unable to take actions + AssignCommand(oTarget, ClearAllActions()); + AssignCommand(oTarget, DelayCommand(60.0, SetCommandable(TRUE))); + AssignCommand(oTarget, SetCommandable(FALSE)); + FaithInTheFrost(OBJECT_SELF, oTarget); + LightOfAurifar(OBJECT_SELF, oTarget); +} +//ok +void DoCommand(object oTarget) +{ + //create the effect + //supernatural will last over resting + ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectCutsceneDominated()), oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DOMINATE_S), oTarget); +} +//ok +int GetTurningCheckResult(int nLevel, int nTurningCheck) +{ + switch(nTurningCheck) + { + case 0: + return nLevel-4; + case 1: + case 2: + case 3: + return nLevel-3; + case 4: + case 5: + case 6: + return nLevel-2; + case 7: + case 8: + case 9: + return nLevel-1; + case 10: + case 11: + case 12: + return nLevel; + case 13: + case 14: + case 15: + return nLevel+1; + case 16: + case 17: + case 18: + return nLevel+2; + case 19: + case 20: + case 21: + return nLevel+3; + default: + if(nTurningCheck < 0) + return nLevel-4; + else + return nLevel+4; + } + //somethings gone wrong here + return 0; +} + +int GetTurningClassLevel(object oCaster = OBJECT_SELF, int nTurnType = SPELL_TURN_UNDEAD) +{ + int nLevel, nTemp; + + if (nTurnType == SPELL_OPPORTUNISTIC_PIETY_TURN) + return GetLevelByClass(CLASS_TYPE_FACTOTUM, oCaster); + + //Baelnorn & Archlich adds all class levels. + if(GetLevelByClass(CLASS_TYPE_BAELNORN, oCaster) || GetHasFeat(FEAT_TEMPLATE_ARCHLICH_MARKER, oCaster)) //:: Archlich + nLevel = GetHitDice(oCaster); + else + { + //full classes + nLevel += GetLevelByClass(CLASS_TYPE_CLERIC, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_TRUENECRO); + nLevel += GetLevelByClass(CLASS_TYPE_SOLDIER_OF_LIGHT, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHROUDS, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_MORNINGLORD, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_SACREDPURIFIER, oCaster); + nLevel += GetLevelByClass(CLASS_TYPE_UR_PRIEST, oCaster); + if (!GetIsVestigeExploited(oCaster, VESTIGE_TENEBROUS_TURN_UNDEAD) && GetHasSpellEffect(VESTIGE_TENEBROUS, oCaster)) nLevel += GetBinderLevel(oCaster, VESTIGE_TENEBROUS); + + //Mystics with sun domain can turn undead + if(GetHasFeat(FEAT_BONUS_DOMAIN_SUN, oCaster)) + nLevel += GetLevelByClass(CLASS_TYPE_MYSTIC, oCaster); + + //offset classes + nTemp = GetLevelByClass(CLASS_TYPE_PALADIN, oCaster)-3; + //Seek Eternal Rest + if (GetHasSpellEffect(SPELL_SEEK_ETERNAL_REST, oCaster)) nTemp = GetLevelByClass(CLASS_TYPE_PALADIN, oCaster); + if(nTemp > 0)nLevel += nTemp; + nTemp = GetLevelByClass(CLASS_TYPE_SHAMAN, oCaster)-2; + if(nTemp > 0) nLevel += nTemp; + nTemp = GetLevelByClass(CLASS_TYPE_TEMPLAR, oCaster)-3; + if(nTemp > 0) nLevel += nTemp; + nTemp = GetLevelByClass(CLASS_TYPE_BLACKGUARD, oCaster)-2; + if(nTemp > 0) nLevel += nTemp; + nTemp = GetLevelByClass(CLASS_TYPE_HOSPITALER, oCaster)-2; + if(nTemp > 0) nLevel += nTemp; + nTemp = GetLevelByClass(CLASS_TYPE_NIGHTSTALKER, oCaster)-3; + if(nTemp > 0) nLevel += nTemp; + + //not undead turning classes + if(nTurnType == SPELL_TURN_SPIDER) + nLevel += GetLevelByClass(CLASS_TYPE_JUDICATOR, oCaster); + else if(nTurnType == SPELL_TURN_BLIGHTSPAWNED) + nLevel += GetLevelByClass(CLASS_TYPE_BLIGHTLORD, oCaster); + else if(nTurnType == SPELL_TURN_OUTSIDER) + { + nTemp = GetLevelByClass(CLASS_TYPE_ANTI_PALADIN, oCaster)-3; + if(nTemp > 0) nLevel += nTemp; + } + } + + //Improved turning feat + nLevel += GetHasFeat(FEAT_IMPROVED_TURNING, oCaster); + + //Phylactery of Undead Turning + if(GetTag(GetItemInSlot(INVENTORY_SLOT_NECK, oCaster)) == "prc_turnphyl") + nLevel += 4; + //Weapon of Legacy bonuses + nLevel += GetLocalInt(oCaster, "WOLTurning"); + + return nLevel; +} +//ok +int GetTurningCharismaMod(int nTurnType) +{ + int nChaMod = GetAbilityModifier(ABILITY_CHARISMA); + + //Heartwarder adds two to cha checks + if(GetHasFeat(FEAT_HEART_PASSION)) + nChaMod += 2; + + if(nTurnType == SPELL_TURN_UNDEAD) + { + //Hierophants Mastery of Energy + if(GetHasFeat(FEAT_MASTER_OF_ENERGY)) + nChaMod += 4; + + //Consecrate spell + if(GetHasSpellEffect(SPELL_CONSECRATE)) + nChaMod += 3; + + //Desecrate spell + if(GetHasSpellEffect(SPELL_DES_20) || GetHasSpellEffect(SPELL_DES_100) || GetHasSpellEffect(SPELL_DESECRATE)) + nChaMod -= 3; + } + + return nChaMod; +} +//ok +int GetHitDiceForTurning(object oTarget, int nTurnType, int nTargetRace) +{ + //Hit Dice + Turn Resistance + int nHD = GetHitDice(oTarget) + GetTurnResistanceHD(oTarget); + + if(GetHasFeat(FEAT_TURN_SUBMISSION, oTarget)) + nHD -= 4; + + //Outsiders get SR (halved if turner has planar turning) + if(nTargetRace == RACIAL_TYPE_OUTSIDER) + { + int nSR = PRCGetSpellResistance(oTarget, OBJECT_SELF); + if(nTurnType == SPELL_TURN_OUTSIDER || GetHasFeat(FEAT_EPIC_PLANAR_TURNING)) + nSR /= 2; + nHD += nSR; + } + + return nHD; +} +//ok +int GetCommandedTotalHD(int nTurnType = SPELL_TURN_UNDEAD, int bUndeadMastery = FALSE) +{ + int nCommandedTotalHD; + object oTest = GetFirstFactionMember(OBJECT_SELF, FALSE); + while(GetIsObjectValid(oTest)) + { + if(GetMaster(oTest) == OBJECT_SELF && GetAssociateType(oTest) == ASSOCIATE_TYPE_DOMINATED) + { + int nRace = MyPRCGetRacialType(oTest); + int nTestHD = GetHitDiceForTurning(oTest, nTurnType, nRace); + if(nRace != RACIAL_TYPE_UNDEAD && bUndeadMastery) + nTestHD *= 10; + nCommandedTotalHD += nTestHD; + } + oTest = GetNextFactionMember(OBJECT_SELF, FALSE); + } + return nCommandedTotalHD; +} + +int CheckTargetName(object oTarget, string sName) +{ + return FindSubString(GetStringLowerCase(GetName(oTarget)), sName) > -1; +} +//ok - elemental savant/bonded summoner? +int GetIsAirCreature(object oCreature, int nAppearance) +{ + return nAppearance == APPEARANCE_TYPE_ELEMENTAL_AIR + || nAppearance == APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER + || nAppearance == APPEARANCE_TYPE_INVISIBLE_STALKER + || nAppearance == APPEARANCE_TYPE_MEPHIT_AIR + || nAppearance == APPEARANCE_TYPE_MEPHIT_DUST + || nAppearance == APPEARANCE_TYPE_MEPHIT_ICE + || nAppearance == APPEARANCE_TYPE_WILL_O_WISP + || nAppearance == APPEARANCE_TYPE_DRAGON_GREEN + || nAppearance == APPEARANCE_TYPE_WYRMLING_GREEN; + //|| CheckTargetName(oCreature, "air")) // This last one is to catch anything named with Air that doesnt use the appearances +} +//ok +int GetIsEarthCreature(object oCreature, int nAppearance) +{ + return nAppearance == APPEARANCE_TYPE_ELEMENTAL_EARTH + || nAppearance == APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER + || nAppearance == APPEARANCE_TYPE_GARGOYLE + || nAppearance == APPEARANCE_TYPE_MEPHIT_EARTH + || nAppearance == APPEARANCE_TYPE_MEPHIT_SALT + || nAppearance == APPEARANCE_TYPE_DRAGON_BLUE + || nAppearance == APPEARANCE_TYPE_DRAGON_COPPER + || nAppearance == APPEARANCE_TYPE_WYRMLING_BLUE + || nAppearance == APPEARANCE_TYPE_WYRMLING_COPPER; + //|| CheckTargetName(oCreature, "earth")) // As above +} +//ok +int GetIsFireCreature(object oCreature, int nAppearance) +{ + return nAppearance == APPEARANCE_TYPE_ELEMENTAL_FIRE + || nAppearance == APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER + || nAppearance == APPEARANCE_TYPE_AZER_MALE + || nAppearance == APPEARANCE_TYPE_AZER_FEMALE + || nAppearance == APPEARANCE_TYPE_GIANT_FIRE + || nAppearance == APPEARANCE_TYPE_GIANT_FIRE_FEMALE + || nAppearance == APPEARANCE_TYPE_MEPHIT_FIRE + || nAppearance == APPEARANCE_TYPE_MEPHIT_MAGMA + || nAppearance == APPEARANCE_TYPE_MEPHIT_STEAM + || nAppearance == APPEARANCE_TYPE_DRAGON_BRASS + || nAppearance == APPEARANCE_TYPE_DRAGON_GOLD + || nAppearance == APPEARANCE_TYPE_DRAGON_RED + || nAppearance == APPEARANCE_TYPE_WYRMLING_BRASS + || nAppearance == APPEARANCE_TYPE_WYRMLING_GOLD + || nAppearance == APPEARANCE_TYPE_WYRMLING_RED + || nAppearance == APPEARANCE_TYPE_DOG_HELL_HOUND; + //|| CheckTargetName(oCreature, "fire")) // This last one is to catch anything named with Fire that doesnt use the appearances +} +//ok +int GetIsWaterCreature(object oCreature, int nAppearance) +{ + return nAppearance == APPEARANCE_TYPE_ELEMENTAL_WATER + || nAppearance == APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER + || nAppearance == APPEARANCE_TYPE_MEPHIT_WATER + || nAppearance == APPEARANCE_TYPE_MEPHIT_OOZE + || nAppearance == APPEARANCE_TYPE_DRAGON_BLACK + || nAppearance == APPEARANCE_TYPE_DRAGON_BRONZE + || nAppearance == APPEARANCE_TYPE_WYRMLING_BLACK + || nAppearance == APPEARANCE_TYPE_WYRMLING_BRONZE; + //|| CheckTargetName(oCreature, "water")) // As above +} +//only animals in PnP +int GetIsReptile(object oCreature, int nAppearance) +{ + if(nAppearance == APPEARANCE_TYPE_KOBOLD_A + || nAppearance == APPEARANCE_TYPE_KOBOLD_B + || nAppearance == APPEARANCE_TYPE_KOBOLD_CHIEF_A + || nAppearance == APPEARANCE_TYPE_KOBOLD_CHIEF_B + || nAppearance == APPEARANCE_TYPE_KOBOLD_SHAMAN_A + || nAppearance == APPEARANCE_TYPE_KOBOLD_SHAMAN_B + || nAppearance == APPEARANCE_TYPE_LIZARDFOLK_A + || nAppearance == APPEARANCE_TYPE_LIZARDFOLK_B + || nAppearance == APPEARANCE_TYPE_LIZARDFOLK_WARRIOR_A + || nAppearance == APPEARANCE_TYPE_LIZARDFOLK_WARRIOR_B + || nAppearance == APPEARANCE_TYPE_LIZARDFOLK_SHAMAN_A + || nAppearance == APPEARANCE_TYPE_LIZARDFOLK_SHAMAN_B + || nAppearance == 451 //Trog + || nAppearance == 452 //Trog Warrior + || nAppearance == 453 //Trog Cleric + || nAppearance == 178 //Black Cobra + || nAppearance == 183 //Cobra + || nAppearance == 194) //Gold Cobra + //|| CheckTargetName(oCreature, "scale") + //|| CheckTargetName(oCreature, "snake") + //|| CheckTargetName(oCreature, "reptile")) // Reptilian names + { + return TRUE; + } + return FALSE; +} + +int GetIsSpider(object oCreature, int nAppearance) +{ + return GetLevelByClass(CLASS_TYPE_VERMIN, oCreature) + && (nAppearance == APPEARANCE_TYPE_SPIDER_DEMON + || nAppearance == APPEARANCE_TYPE_SPIDER_DIRE + || nAppearance == APPEARANCE_TYPE_SPIDER_GIANT + || nAppearance == APPEARANCE_TYPE_SPIDER_SWORD + || nAppearance == APPEARANCE_TYPE_SPIDER_PHASE + || nAppearance == APPEARANCE_TYPE_SPIDER_WRAITH + || nAppearance == 463); //Maggris + //|| CheckTargetName(oCreature, "spider"); // Duh +} + +int GetIsColdCreature(object oCreature, int nAppearance) +{ + return nAppearance == APPEARANCE_TYPE_DOG_WINTER_WOLF + || nAppearance == APPEARANCE_TYPE_DRAGON_WHITE + || nAppearance == APPEARANCE_TYPE_GIANT_FROST + || nAppearance == APPEARANCE_TYPE_GIANT_FROST_FEMALE + || nAppearance == APPEARANCE_TYPE_MEPHIT_ICE + || nAppearance == APPEARANCE_TYPE_WYRMLING_WHITE + || nAppearance == APPEARANCE_TYPE_DRAGON_SILVER + || nAppearance == APPEARANCE_TYPE_WYRMLING_SILVER; + //|| CheckTargetName(oCreature, "water")) // As above +} + +void FaithInTheFrost(object oPC, object oTarget) +{ + if (GetHasFeat(FEAT_FAITH_IN_THE_FROST, oPC)) + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(GetAbilityModifier(ABILITY_CHARISMA, oPC), DAMAGE_TYPE_COLD), oTarget); +} + +void LightOfAurifar(object oPC, object oTarget) +{ + if (GetHasFeat(FEAT_LIGHT_OF_AURIFAR, oPC)) + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(2), DAMAGE_TYPE_FIRE), oTarget); +} + +// Test main +//void main(){} + + diff --git a/src/include/prc_inc_unarmed.nss b/src/include/prc_inc_unarmed.nss new file mode 100644 index 0000000..a08f912 --- /dev/null +++ b/src/include/prc_inc_unarmed.nss @@ -0,0 +1,596 @@ +//::////////////////////////////////////////////// +//:: Unarmed evaluation include +//:: prc_inc_unarmed +//::////////////////////////////////////////////// +/* + Handles attack bonus, damage and itemproperties + for creature weapons created based on class + and race abilities. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + + +////////////////////////////////////////////////// +/* Constant declarations */ +////////////////////////////////////////////////// + +const int ITEM_PROPERTY_WOUNDING = 69; + +const string CALL_UNARMED_FEATS = "CALL_UNARMED_FEATS"; +const string CALL_UNARMED_FISTS = "CALL_UNARMED_FISTS"; +const string UNARMED_CALLBACK = "UNARMED_CALLBACK"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + + +// Determines the amount of unarmed damage a character can do +// ========================================================== +// oCreature a creature whose unarmed damage dice are +// being evaluated +// +// Returns one of the IP_CONST_MONSTERDAMAGE_* constants +int FindUnarmedDamage(object oCreature); + +// Adds appropriate unarmed feats to the skin. Goes with UnarmedFists() +// ==================================================================== +// oCreature a creature whose unarmed combat feats to handle +// +// Do not call this directly from your evaluation script. Instead, set +// the local variable CALL_UNARMED_FEATS on the creature to TRUE. +// This is done to avoid bugs from redundant calls to these functions. +void UnarmedFeats(object oCreature); + +// Creates/strips a creature weapon and applies bonuses. Goes with UnarmedFeats() +// ============================================================================== +// oCreature a creature whose creature weapon to handle +// +// Do not call this directly from your evaluation script. Instead, set +// the local variable CALL_UNARMED_FISTS on the creature to TRUE. +// This is done to avoid bugs from redundant calls to these functions. +// +// If you are going to add properties to the creature weapons, hook +// your script for callback after this is evaluated by calling +// AddEventScript(oPC, CALLBACKHOOK_UNARMED, "your_script", FALSE, FALSE); +// When the callback is running, a local int UNARMED_CALLBACK will be +// set on OBJECT_SELF +void UnarmedFists(object oCreature); + +/** + * Determines whether the given object is one of the PRC creature weapons based + * on it's resref and tag. Resref is tested first, then tag. + * + * @param oTest Object to test + * @return TRUE if the object is a PRC creature weapon, FALSE otherwise + */ +int GetIsPRCCreatureWeapon(object oTest); + +/** + * Determines the average damage of a IP_CONST_MONSTERDAMAGE_*** constant. + * Used to compare different unarmed damages. + * + * @param iDamage IP_CONST_MONSTERDAMAGE_*** constant + * @return average damage of that constant + */ +float DamageAvg(int iDamage); + +//#include "prc_alterations" +//#include "pnp_shft_poly" +//#include "prc_feat_const" +//#include "prc_ipfeat_const" +//#include "prc_class_const" +//#include "prc_racial_const" +//#include "prc_spell_const" +//#include "inc_utility" +#include "prc_inc_natweap" + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +// Clean up any extras in the inventory. +void CleanExtraFists(object oCreature) +{ + int nItemType; + object oItem = GetFirstItemInInventory(oCreature); + object oCWPB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCreature); + object oCWPL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature); + object oCWPR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCreature); + object oCSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); + + while(GetIsObjectValid(oItem)) + { + nItemType = GetBaseItemType(oItem); + + if(nItemType == BASE_ITEM_CBLUDGWEAPON || + nItemType == BASE_ITEM_CPIERCWEAPON || + nItemType == BASE_ITEM_CREATUREITEM || + nItemType == BASE_ITEM_CSLASHWEAPON || + nItemType == BASE_ITEM_CSLSHPRCWEAP + ) + { + if(oItem != oCWPB && + oItem != oCWPL && + oItem != oCWPR && + oItem != oCSkin + ) + MyDestroyObject(oItem); + } + oItem = GetNextItemInInventory(oCreature); + } +} + +int GetIsPRCCreatureWeapon(object oTest) +{ + string sTest = GetStringUpperCase(GetResRef(oTest)); + + return // First, test ResRef + sTest == "PRC_UNARMED_B" || + sTest == "PRC_UNARMED_S" || + sTest == "PRC_UNARMED_P" || + sTest == "PRC_UNARMED_SP" || + sTest == "NW_IT_CREWPB010" || // Legacy item, should not be used anymore + // If resref doesn't match, try tag + (sTest = GetStringUpperCase(GetTag(oTest))) == "PRC_UNARMED_B" || + sTest == "PRC_UNARMED_S" || + sTest == "PRC_UNARMED_P" || + sTest == "PRC_UNARMED_SP" || + sTest == "NW_IT_CREWPB010" + ; +} + +// Remove the unarmed penalty effect +void RemoveUnarmedAttackEffects(object oCreature) +{ + effect e = GetFirstEffect(oCreature); + + while (GetIsEffectValid(e)) + { + if (GetEffectSpellId(e) == SPELL_UNARMED_ATTACK_PEN) + RemoveEffect(oCreature, e); + + e = GetNextEffect(oCreature); + } +} + +// Add the unarmed penalty effect -- the DR piercing property gives an unwanted +// attack bonus. This clears it up. +void ApplyUnarmedAttackEffects(object oCreature) +{ + object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(OBJECT_SELF)); + + AssignCommand(oCastingObject, ActionCastSpellAtObject(SPELL_UNARMED_ATTACK_PEN, oCreature, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + + DestroyObject(oCastingObject, 6.0); +} + +// Determines the amount of damage a character can do. +// IoDM: +1 dice at level 4, +2 dice at level 8 +// Sacred Fist: Levels add to monk levels, or stand alone as monk levels. +// Shou: 1d6 at level 1, 1d8 at level 2, 1d10 at level 3, 2d6 at level 5 +// Monk: 1d6 at level 1, 1d8 at level 4, 1d10 at level 8, 2d6 at level 12, 2d8 at level 16, 2d10 at level 20 +// Frostrager: 1d6 at level 1, 1d8 at level 4 +int FindUnarmedDamage(object oCreature) +{ + int iDamage = 0; + int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk"); + int iShou = GetLevelByClass(CLASS_TYPE_SHOU, oCreature); + int iBrawler = GetLevelByClass(CLASS_TYPE_BRAWLER, oCreature); + int iSacredFist = GetLevelByClass(CLASS_TYPE_SACREDFIST, oCreature); + int iEnlightenedFist = GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCreature); + int iHenshin = GetLevelByClass(CLASS_TYPE_HENSHIN_MYSTIC, oCreature); + int iZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature); + int iShadowSunNinja = GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oCreature); + int iFrost = GetLevelByClass(CLASS_TYPE_FROSTRAGER, oCreature); + int iAscetic = GetLevelByClass(CLASS_TYPE_NINJA, oCreature); + int iRonove; + int iMonkDamage = 1; + int iShouDamage = 1; + int iBrawlerDamage = 1; + int iFrostDamage = 1; + int iSUSDamage = 1; + int iDieIncrease = 0; + int iSize; + + if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) iRonove = GetLocalInt(oCreature, "RonovesFists"); + + // if the creature is shifted, use model size + // otherwise, we want to stick to what the feats say they "should" be. + // No making pixies with Dragon Appearance for "huge" fist damage. + if( GetIsPolyMorphedOrShifted(oCreature) + || GetPRCSwitch(PRC_APPEARANCE_SIZE)) + { + iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; // medium is size 5 for us + } + else + { + // Determine creature size by feats. + iSize = 5; // medium is size 5 for us + if (GetHasFeat(FEAT_TINY, oCreature)) iSize = 3; + if (GetHasFeat(FEAT_SMALL, oCreature)) iSize = 4; + if (GetHasFeat(FEAT_LARGE, oCreature)) iSize = 6; + if (GetHasFeat(FEAT_HUGE, oCreature)) iSize = 7; + // include size changes + iSize += PRCGetCreatureSize(oCreature) - PRCGetCreatureSize(oCreature, PRC_SIZEMASK_NONE); + // cap if needed + if (iSize < 1) iSize = 1; + if (iSize > 9) iSize = 9; + } + + // Sacred Fist cannot add their levels if they've broken their code. + if (GetHasFeat(FEAT_SF_CODE, oCreature)) iSacredFist = 0; + + // several classes add their levels to the monk class, + // or use monk progression if the character has no monk levels + iMonk += iSacredFist + iHenshin + iEnlightenedFist + iShou + iZuoken + iShadowSunNinja; + + // Superior Unarmed Strike + if (GetHasFeat(FEAT_SUPERIOR_UNARMED_STRIKE, oCreature)) + { + iMonk += 4; + int nHD = GetHitDice(oCreature); + if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; + else if (nHD >= 12) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d10; + else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; + else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; + else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; + } + + // Ascetic Stalker + if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) + iMonk += iAscetic; + + // In 3.0e, Monk progression stops after level 16: + if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE) ) iMonk = 16; + // in 3.5e, monk progression stops at 20. + else if(iMonk > 20) iMonk = 20; + + // Ronove is in place of monk, does not stack + if (iRonove > iMonk) iMonk = iRonove; + + // monks damage progesses every four levels, starts at 1d6 + if (iMonk > 0) + iMonkDamage = iMonk / 4 + 3; + + // For medium monks in 3.0e skip 2d8 and go to 1d20 + if(iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE) ) iMonkDamage = 8; + + // Shou Disciple either adds its level to existing class or does its own damage, depending + // on which is better. Here we will determine how much damage the Shou Disciple does + // without stacking. + if (iShou > 0) iShouDamage = iShou + 2; // Lv. 1: 1d6, Lv. 2: 1d8, Lv. 3: 1d10 + if (iShou > 3) iShouDamage--; // Lv. 4: 1d10, Lv. 5: 2d6 + iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); + + // Frostrager does not stack with other damage types + if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; // Lv. 1: 1d6 + if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; // Lv. 3: 1d8 + + // Brawler follows monk progression except for the last one (3d8) + if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; // 1d6, 1d8, 1d10, 2d6, 2d8, 2d10 + if (iBrawler >= 36) iBrawlerDamage += 2; // 3d8 + + // Monks and monk-like classes deal no additional damage when wearing any armor, at + // least in NWN. This is to reflect that. No shields too. + if (iMonkDamage > 1) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + int bShieldEq = GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; + + if (GetBaseAC(oArmor) > 0 || bShieldEq) + { + iMonkDamage = 1; + } + } + +// Shou Disciples can wear light armor + if (iShouDamage > 1) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + int bShieldEq = GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; + + if (GetBaseAC(oArmor) > 3 || bShieldEq) + { + iShouDamage = 1; + } + } + + // For Initiate of Draconic Mysteries + if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease = 2; + else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease = 1; + + //:: Expansion / Compression powers + int nExpansion = GetLocalInt(oCreature, "PRC_Power_Expansion_SizeIncrease"); + int nCompression = GetLocalInt(oCreature, "PRC_Power_Compression_SizeReduction"); + + if (nExpansion) + { + iSize += nExpansion; + } + + if (nCompression) + { + iSize -= nCompression; + } + + iMonkDamage += iDieIncrease; + iShouDamage += iDieIncrease; + iBrawlerDamage += iDieIncrease; + iFrostDamage += iDieIncrease; + iSUSDamage += iDieIncrease; + + //FloatingTextStringOnCreature("prc_inc_unarmed: Size is: "+IntToString(iSize)+".", oCreature); + //FloatingTextStringOnCreature("prc_inc_unarmed: Pre 2DA Lookup >> iMonkDamage = "+IntToString(iMonkDamage)+".", oCreature); + + // now, read the damage from the table in unarmed_dmg.2da + iMonkDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iMonkDamage)); + iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); + + //FloatingTextStringOnCreature("prc_inc_unarmed: Post 2DA Lookup >> iMonkDamage = "+IntToString(iMonkDamage)+".", oCreature); + + // Medium+ monks have some special values on the table in 3.0: + if (iSize >= 5 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + { + if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d6) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d12; + if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d10) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d20; + } + + iDamage = iMonkDamage; + // Future unarmed classes: if you do your own damage, add in "comparisons" below here. + iDamage = (DamageAvg(iShouDamage ) > DamageAvg(iDamage)) ? iShouDamage : iDamage; + iDamage = (DamageAvg(iFrostDamage ) > DamageAvg(iDamage)) ? iFrostDamage : iDamage; + iDamage = (DamageAvg(iSUSDamage ) > DamageAvg(iDamage)) ? iSUSDamage : iDamage; + + if (DEBUG) DoDebug("prc_inc_unarmed: iDamage "+IntToString(iDamage)); + + return iDamage; +} + +// Adds appropriate feats to the skin. Stolen from SoulTaker + expanded with overwhelming/devastating critical. +void UnarmedFeats(object oCreature) +{ + // If we are polymorphed/shifted, do not mess with the creature weapon. + if (GetIsPolyMorphedOrShifted(oCreature)) return; + + object oSkin = GetPCSkin(oCreature); + + if (!GetHasFeat(FEAT_WEAPON_PROFICIENCY_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_CREATURE),oSkin); + + //only roll unarmed feats into creature feats when not using natural weapons + if(!GetIsUsingPrimaryNaturalWeapons(oCreature)) + { + if (GetHasFeat(FEAT_WEAPON_FOCUS_UNARMED_STRIKE, oCreature) && !GetHasFeat(FEAT_WEAPON_FOCUS_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapFocCreature),oSkin); + + if (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE, oCreature) && !GetHasFeat(FEAT_WEAPON_SPECIALIZATION_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapSpecCreature),oSkin); + + if (GetHasFeat(FEAT_IMPROVED_CRITICAL_UNARMED_STRIKE, oCreature) && !GetHasFeat(FEAT_IMPROVED_CRITICAL_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_ImpCritCreature),oSkin); + + if (GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapEpicFocCreature),oSkin); + + if (GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_WeapEpicSpecCreature),oSkin); + + if (GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_OVERCRITICAL_CREATURE),oSkin); + + if (GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_UNARMED, oCreature) && !GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_CREATURE, oCreature)) + AddItemProperty(DURATION_TYPE_PERMANENT,PRCItemPropertyBonusFeat(IP_CONST_FEAT_DEVCRITICAL_CREATURE),oSkin); + } +} + +// Creates/strips a creature weapon and applies bonuses. Large chunks stolen from SoulTaker. +void UnarmedFists(object oCreature) +{ + // If we are polymorphed/shifted, do not mess with the creature weapon. + if (GetIsPolyMorphedOrShifted(oCreature)) return; + + RemoveUnarmedAttackEffects(oCreature); + + object oRighthand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + object oLefthand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + object oWeapL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature); + + + // Clean up the mess of extra fists made on taking first level. + DelayCommand(6.0f, CleanExtraFists(oCreature)); + + // Determine the character's capacity to pierce DR. + // only applies when not using natural weapons + if(!GetIsUsingPrimaryNaturalWeapons(oCreature)) + { + + int iRace = GetRacialType(oCreature); + int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk"); + int iShou = GetLevelByClass(CLASS_TYPE_SHOU, oCreature); + int iSacFist = GetLevelByClass(CLASS_TYPE_SACREDFIST, oCreature); + int iHenshin = GetLevelByClass(CLASS_TYPE_HENSHIN_MYSTIC, oCreature); + int iIoDM = GetLevelByClass(CLASS_TYPE_INITIATE_DRACONIC, oCreature); + int iBrawler = GetLevelByClass(CLASS_TYPE_BRAWLER, oCreature); + int iZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature); + int iShadowSunNinja = GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oCreature); + int iAscetic = GetLevelByClass(CLASS_TYPE_NINJA, oCreature); + + // Sacred Fists who break their code get no benefits. + if (GetHasFeat(FEAT_SF_CODE,oCreature)) iSacFist = 0; + + // The monk adds all these classes. + int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja; + + // Ascetic Stalker + if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) + iMonkEq += iAscetic; + + // Determine the type of damage the character should do. + string sWeapType; + if (GetHasFeat(FEAT_CLAWDRAGON, oCreature)) + sWeapType = "PRC_UNARMED_S"; + else + sWeapType = "PRC_UNARMED_B"; + + + // Equip the creature weapon. + if (!GetIsObjectValid(oWeapL) || GetTag(oWeapL) != sWeapType) + { + if (GetHasItem(oCreature, sWeapType)) + { + oWeapL = GetItemPossessedBy(oCreature, sWeapType); + SetIdentified(oWeapL, TRUE); + AssignCommand(oCreature, ActionEquipItem(oWeapL, INVENTORY_SLOT_CWEAPON_L)); + } + else + { + oWeapL = CreateItemOnObject(sWeapType, oCreature); + SetIdentified(oWeapL, TRUE); + AssignCommand(oCreature,ActionEquipItem(oWeapL, INVENTORY_SLOT_CWEAPON_L)); + } + } + + int iKi = (iMonkEq > 9) ? 1 : 0; + iKi = (iMonkEq > 12) ? 2 : iKi; + iKi = (iMonkEq > 15) ? 3 : iKi; + + int iDragClaw = GetHasFeat(FEAT_CLAWDRAGON,oCreature) ? 1: 0; + iDragClaw = GetHasFeat(FEAT_CLAWENH2,oCreature) ? 2: iDragClaw; + iDragClaw = GetHasFeat(FEAT_CLAWENH3,oCreature) ? 3: iDragClaw; + + int iBrawlEnh = iBrawler / 6; + + int iEpicKi = GetHasFeat(FEAT_EPIC_IMPROVED_KI_STRIKE_4,oCreature) ? 1 : 0 ; + iEpicKi = GetHasFeat(FEAT_EPIC_IMPROVED_KI_STRIKE_5,oCreature) ? 2 : iEpicKi ; + + // The total enhancement to the fist is the sum of all the enhancements above + int iEnh = iKi + iDragClaw + iBrawlEnh + iEpicKi; + + // Strip the Fist. + itemproperty ip = GetFirstItemProperty(oWeapL); + while (GetIsItemPropertyValid(ip)) + { + RemoveItemProperty(oWeapL, ip); + ip = GetNextItemProperty(oWeapL); + } + + // Leave the fist blank if weapons are equipped. The only way a weapon will + // be equipped on the left hand is if there is a weapon in the right hand. + if (GetIsObjectValid(oRighthand)) return; + + // Add glove bonuses. + object oItem = GetItemInSlot(INVENTORY_SLOT_ARMS,oCreature); + int iGloveEnh = 0; + if (GetIsObjectValid(oItem)) + { + int iType = GetBaseItemType(oItem); + if (iType == BASE_ITEM_GLOVES) + { + ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + iType = GetItemPropertyType(ip); + switch (iType) + { + case ITEM_PROPERTY_DAMAGE_BONUS: + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT: + case ITEM_PROPERTY_ON_HIT_PROPERTIES: + case ITEM_PROPERTY_ONHITCASTSPELL: + case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: + case ITEM_PROPERTY_KEEN: + case ITEM_PROPERTY_MASSIVE_CRITICALS: + case ITEM_PROPERTY_POISON: + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + case ITEM_PROPERTY_WOUNDING: + case ITEM_PROPERTY_DECREASED_DAMAGE: + case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: + DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT,ip,oWeapL)); + break; + case ITEM_PROPERTY_ATTACK_BONUS: + int iCost = GetItemPropertyCostTableValue(ip); + iGloveEnh = (iCost>iGloveEnh) ? iCost:iGloveEnh; + iEnh = (iCost>iEnh) ? iCost:iEnh; + break; + } + ip = GetNextItemProperty(oItem); + } + // handles these seperately so as not to create "attack penalties vs. xxxx" + ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + iType = GetItemPropertyType(ip); + switch (iType) + { + case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: + if (GetItemPropertyCostTableValue(ip) > iEnh) + DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT,ip,oWeapL)); + break; + } + ip = GetNextItemProperty(oItem); + } + } + } + + // Add damage resistance penetration. + DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyAttackBonus(iEnh), oWeapL)); + + // Cool VFX when striking unarmed + if (iMonkEq > 9) + //DelayCommand(0.1, AddItemProperty(DURATION_TYPE_PERMANENT, PRCItemPropertyBonusFeat(IP_CONST_FEAT_KI_STRIKE), oWeapL)); + DelayCommand(0.1, IPSafeAddItemProperty(oWeapL, PRCItemPropertyBonusFeat(IP_CONST_FEAT_KI_STRIKE), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE)); + + // This adds creature weapon finesse and a penalty to offset the DR penetration attack bonus. + SetLocalInt(oCreature, "UnarmedEnhancement", iEnh); + SetLocalInt(oCreature, "UnarmedEnhancementGlove", iGloveEnh); + } + + // Weapon finesse or intuitive attack? + SetLocalInt(oCreature, "UsingCreature", TRUE); + ExecuteScript("prc_intuiatk", oCreature); + DelayCommand(1.0f, DeleteLocalInt(oCreature, "UsingCreature")); + ApplyUnarmedAttackEffects(oCreature); + + // Add the appropriate damage to the fist. + int iMonsterDamage = FindUnarmedDamage(oCreature); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyMonsterDamage(iMonsterDamage),oWeapL); + + // Add OnHitCast: Unique if necessary. Frostrager level 5 grants Rend too + if(GetHasFeat(FEAT_REND, oCreature) || GetLevelByClass(CLASS_TYPE_FROSTRAGER, oCreature) > 4) + IPSafeAddItemProperty(oWeapL, + ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1)); + + // Friendly message to remind players that certain things won't appear correct. + if (GetLocalInt(oCreature, "UnarmedSubSystemMessage") != TRUE + && GetHasSpellEffect(SPELL_UNARMED_ATTACK_PEN, oCreature)) + { + SetLocalInt(oCreature, "UnarmedSubSystemMessage", TRUE); + DelayCommand(3.001f, SendMessageToPC(oCreature, "This character uses the PRC's unarmed system. This system has been created to")); + DelayCommand(3.002f, SendMessageToPC(oCreature, "work around many Aurora engine bugs and limitations. Your attack roll may appear to be")); + DelayCommand(3.003f, SendMessageToPC(oCreature, "incorrect on the character's stats. However, the attack rolls should be correct in")); + DelayCommand(3.004f, SendMessageToPC(oCreature, "combat. Disregard any attack effects that seem extra: they are part of the workaround.")); + DelayCommand(600.0f, DeleteLocalInt(oCreature, "UnarmedSubSystemMessage")); + } +} + +float DamageAvg(int iDamage) +{ + int iDie = StringToInt(Get2DACache("iprp_monstcost", "Die", iDamage)); + int iNum = StringToInt(Get2DACache("iprp_monstcost", "NumDice", iDamage)); + + return IntToFloat(iNum * (iDie+1)) / 2; +} + +//:: void main (){} diff --git a/src/include/prc_inc_util.nss b/src/include/prc_inc_util.nss new file mode 100644 index 0000000..0d4ac47 --- /dev/null +++ b/src/include/prc_inc_util.nss @@ -0,0 +1,228 @@ +/////////////////////////////////////////////////////////////////////////////// +// VARIABLE DECLARATIONS +/////////////////////////////////////////////////////////////////////////////// + +const string PRC_Rest_Generation = "PRC_Rest_Generation"; +const string PRC_Rest_Generation_Check = "PRC_Rest_Generation_Check"; +const string PRC_ForcedRestDetector_Generation = "PRC_ForcedRestDetector_Generation"; + +/////////////////////////////////////////////////////////////////////////////// +// INCLUDES +/////////////////////////////////////////////////////////////////////////////// + +#include "prc_alterations" + +/////////////////////////////////////////////////////////////////////////////// +// FUNCTION DECLARATIONS +/////////////////////////////////////////////////////////////////////////////// + +// Returns the number of henchmen a player has. +int GetNumHenchmen(object oPC); + +// returns the float time in seconds to close the given distance +float GetTimeToCloseDistance(float fMeters, object oPC, int bIsRunning = FALSE); + +/* PRC ForceRest wrapper + * + * ForceRest does not trigger the module's OnRest event, nor will the targeted player show up + * when GetLastPCRested is used. This wrapper can be used to ForceRest a target, while still + * running the PRC's OnRest script. + */ +void PRCForceRest(object oPC); +void PRCForceRested(object oPC); + +/////////////////////////////////////////////////////////////////////////////// +// FUNCTION DEFINITIONS +/////////////////////////////////////////////////////////////////////////////// + +int GetNumHenchmen(object oPC) +{ + if (!GetIsPC(oPC)) return -1; + + int nLoop, nCount; + for (nLoop = 1; nLoop <= GetMaxHenchmen(); nLoop++) + { + if (GetIsObjectValid(GetHenchman(oPC, nLoop))) + nCount++; + } + + return nCount; +} + +float GetTimeToCloseDistance(float fMeters, object oPC, int bIsRunning = FALSE) +{ + float fTime = 0.0; + float fSpeed = 0.0; + + int iMoveRate = GetMovementRate(oPC); + + switch(iMoveRate) + { + case 0: + fSpeed = 2.0; + break; + case 1: + fSpeed = 0.0; + break; + case 2: + fSpeed = 0.75; + break; + case 3: + fSpeed = 1.25; + break; + case 4: + fSpeed = 1.75; + break; + case 5: + fSpeed = 2.25; + break; + case 6: + fSpeed = 2.75; + break; + case 7: + fSpeed = 2.0; // could change to creature default in the appearance.2da. + break; + case 8: + fSpeed = 5.50; + break; + } + + // movement speed doubled if running + if(bIsRunning) fSpeed *= 2.0; + + // other effects that can change movement speed + if( PRCGetHasEffect(EFFECT_TYPE_HASTE, oPC) ) fSpeed *= 2.0; + if( PRCGetHasEffect(EFFECT_TYPE_MOVEMENT_SPEED_INCREASE, oPC) ) fSpeed *= 2.0; + + if( PRCGetHasEffect(EFFECT_TYPE_SLOW, oPC) ) fSpeed /= 2.0; + if( PRCGetHasEffect(EFFECT_TYPE_MOVEMENT_SPEED_DECREASE, oPC) ) fSpeed /= 2.0; + + if( GetHasFeat(FEAT_BARBARIAN_ENDURANCE, oPC) ) fSpeed *= 1.1; // 10% gain + if( GetHasFeat(FEAT_MONK_ENDURANCE, oPC) ) + { + float fBonus = 0.1 * (GetLevelByClass(CLASS_TYPE_MONK, oPC) / 3 ); + if (fBonus > 0.90) fBonus = 0.9; + + fBonus += 1.0; + fSpeed *= fBonus; + } + + // final calculation + fTime = fMeters / fSpeed; + + return fTime; +} + +int PRC_NextGeneration(int nCurrentGeneration) +{ + nCurrentGeneration++; + if (nCurrentGeneration > 30000) + nCurrentGeneration = 1; + return nCurrentGeneration; +} + +//TODO: TRIED THIS CLEANER VERSION AND IT DIDN'T WORK; FIX IT AND USE IT LATER +// effect _prc_inc_ForcedRestDetectorEffect() +// { +// //Create an extraordinary effect, which is removed only by resting. +// //Therefore, if it disappears, we know that resting has happened. +// return ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_CESSATE_NEUTRAL)); +// //TODO: this could match effects that are not ours. Make it more unique somehow. +// } +// +// void _prc_inc_ApplyForcedRestDetectorEffect(object oPC) +// { +// ApplyEffectToObject(DURATION_TYPE_PERMANENT, _prc_inc_ForcedRestDetectorEffect(), oPC); +// } +// +// int _prc_inc_TestForcedRestDetectorEffect(effect eEffect) +// { +// effect eTestEffect = _prc_inc_ForcedRestDetectorEffect(); +// return GetEffectType(eEffect) == GetEffectType(eTestEffect) && +// GetEffectSubType(eEffect) == GetEffectSubType(eTestEffect) && +// GetEffectDurationType(eEffect) == GetEffectDurationType(eTestEffect) && +// GetEffectSpellId(eEffect) == -1; +// } + +void _prc_inc_ApplyForcedRestDetectorEffect(object oPC) +{ + //Apply an extraordinary effect, which is removed only by resting + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_CESSATE_NEUTRAL)), oPC); + //TODO: this could match effects that are not ours. Make it more unique somehow. +} + +int _prc_inc_TestForcedRestDetectorEffect(effect eEffect) +{ + return GetEffectType(eEffect) == EFFECT_TYPE_VISUALEFFECT && GetEffectSubType(eEffect) == SUBTYPE_EXTRAORDINARY && GetEffectDurationType(eEffect) == DURATION_TYPE_PERMANENT && GetEffectSpellId(eEffect) == -1; + //TODO: this could match effects that are not ours. Make it more unique somehow. +} + +void _prc_inc_ForcedRestDetector(object oPC, int nExpectedGeneration) +{ + int nGeneration = GetLocalInt(oPC, PRC_ForcedRestDetector_Generation); + if (nGeneration != nExpectedGeneration) + { + //There's another forced rest detector running, so stop ourselves and let that one continue + DoDebug("STOPPING DUPLICATE FORCED REST DETECTOR"); + return; + } + nGeneration = PRC_NextGeneration(nGeneration); + SetLocalInt(oPC, PRC_ForcedRestDetector_Generation, nGeneration); + + int nRestGeneration = GetLocalInt(oPC, PRC_Rest_Generation); + int nRestGenerationCheck = GetLocalInt(oPC, PRC_Rest_Generation_Check); + if (nRestGeneration != nRestGenerationCheck) + { + //A normal rest happened, and the delayed commands it schedules may still + //be executing or waiting to execute, so do nothing and check back later. + SetLocalInt(oPC, PRC_Rest_Generation_Check, nRestGeneration); + DelayCommand(10.0f, _prc_inc_ApplyForcedRestDetectorEffect(oPC)); + DelayCommand(10.1f, _prc_inc_ForcedRestDetector(oPC, nGeneration)); + } + else + { + int bFound = FALSE; + effect eEffect = GetFirstEffect(oPC); + while (GetIsEffectValid(eEffect)) + { + if (_prc_inc_TestForcedRestDetectorEffect(eEffect)) + { + bFound = TRUE; + break; + } + eEffect = GetNextEffect(oPC); + } + + if (!bFound) + { + DoDebug("FORCED REST DETECTED"); + PRCForceRested(oPC); //This executes the normal resting code, which will be detected above the next time this function executes + } + + //Schedule next check + DelayCommand(1.0f, _prc_inc_ForcedRestDetector(oPC, nGeneration)); + } +} + +void StartForcedRestDetector(object oPC) +{ + //TODO: make a way for the detector to detect that forced rest has already happened, or else do it here now (but how?) + DoDebug("STARTING FORCED REST DETECTOR"); + _prc_inc_ApplyForcedRestDetectorEffect(oPC); + SetLocalInt(oPC, PRC_Rest_Generation_Check, GetLocalInt(oPC, PRC_Rest_Generation)); + int nGeneration = GetLocalInt(oPC, PRC_ForcedRestDetector_Generation); + DelayCommand(1.0f, _prc_inc_ForcedRestDetector(oPC, nGeneration)); +} + +void PRCForceRest(object oPC) +{ + ForceRest(oPC); + PRCForceRested(oPC); +} + +void PRCForceRested(object oPC) +{ + //The PC has been forced rested--fix the problems this causes. + SetLocalInt(oPC, "PRC_ForceRested", 1); + ExecuteScript("prc_rest", oPC); +} \ No newline at end of file diff --git a/src/include/prc_inc_wpnrest.nss b/src/include/prc_inc_wpnrest.nss new file mode 100644 index 0000000..401b323 --- /dev/null +++ b/src/include/prc_inc_wpnrest.nss @@ -0,0 +1,1083 @@ +//:://///////////////////////////////////////////// +//:: Weapon Restriction System Include +//:: prc_inc_restwpn.nss +//:://///////////////////////////////////////////// +/* + Functions to support PnP Weapon Proficiency and + weapon feat chain simulation +*/ +//::////////////////////////////////////////////// +//:: Created By: Fox +//:: Created On: Feb 2, 2008 +//::////////////////////////////////////////////// + +#include "prc_inc_fork" +#include "inc_item_props" +#include "prc_x2_itemprop" + +/** + * All of the following functions use the following parameters: + * + * @param oPC The character weilding the weapon + * @param oItem The item in question. + * @param nHand The hand the weapon is wielded in. In the form of + * ATTACK_BONUS_ONHAND or ATTACK_BONUS_OFFHAND. + */ +void DoRacialEquip(object oPC, int nBaseType); + + //return if PC has proficiency in an item +int IsProficient(object oPC, int nBaseItem) +{ + switch(nBaseItem) + { + //special case: counts as simple for chitine + case BASE_ITEM_SHORTSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_MINDBLADE, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) && GetRacialType(oPC) == RACIAL_TYPE_CHITINE) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SHORTSWORD, oPC); + + case BASE_ITEM_CLUB: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_CLUB, oPC); + + case BASE_ITEM_DAGGER: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DAGGER, oPC); + + case BASE_ITEM_LONGSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_MINDBLADE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_LONGSWORD, oPC); + + case BASE_ITEM_BATTLEAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) && GetRacialType(oPC) == RACIAL_TYPE_GNOLL) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_BATTLEAXE, oPC); + + case BASE_ITEM_BASTARDSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || GetHasFeat(FEAT_MINDBLADE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD, oPC); + + case BASE_ITEM_LIGHTFLAIL: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL, oPC); + + case BASE_ITEM_WARHAMMER: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_WARHAMMER, oPC); + + case BASE_ITEM_LONGBOW: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_LONGBOW, oPC); + + case BASE_ITEM_LIGHTMACE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_LIGHT_MACE, oPC); + + case BASE_ITEM_HALBERD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_HALBERD, oPC); + + case BASE_ITEM_SHORTBOW: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) && GetRacialType(oPC) == RACIAL_TYPE_GNOLL) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SHORTBOW, oPC); + + case BASE_ITEM_TWOBLADEDSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD, oPC); + + case BASE_ITEM_GREATSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_GREATSWORD, oPC); + + case BASE_ITEM_GREATAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_GREATAXE, oPC); + + case BASE_ITEM_DART: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DART, oPC); + + case BASE_ITEM_DIREMACE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_DIRE_MACE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_DOUBLEAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_HEAVYFLAIL: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_LIGHTHAMMER: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_HANDAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_HANDAXE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oPC); + + case BASE_ITEM_KAMA: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_KAMA, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_KATANA: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_KATANA, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_KUKRI: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_KUKRI, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_MORNINGSTAR: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MORNINGSTAR, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC); + + case BASE_ITEM_QUARTERSTAFF: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_WIZARD, oPC); + + case BASE_ITEM_RAPIER: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_RAPIER, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oPC); + + case BASE_ITEM_SCIMITAR: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SCIMITAR, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC); + + case BASE_ITEM_SCYTHE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SCYTHE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_SHORTSPEAR: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SHORTSPEAR, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC); + + case BASE_ITEM_SHURIKEN: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SHURIKEN, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oPC); + + case BASE_ITEM_SICKLE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SICKLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC); + + case BASE_ITEM_SLING: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SLING, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oPC); + + case BASE_ITEM_THROWINGAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_THROWING_AXE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) + || GetHasFeat(FEAT_MINDBLADE, oPC); + + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_CREATURE, oPC); + + case BASE_ITEM_TRIDENT: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_TRIDENT, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_DOUBLE_SCIMITAR: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_FALCHION: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_FALCHION, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_GOAD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_GOAD, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC); + + case BASE_ITEM_HEAVY_MACE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_HEAVY_MACE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC); + + case BASE_ITEM_HEAVY_PICK: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_HEAVY_PICK, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_LIGHT_PICK: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_LIGHT_PICK, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_KATAR: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_KATAR, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oPC); + + case BASE_ITEM_MAUL: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MAUL, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + case BASE_ITEM_NUNCHAKU: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_NUNCHAKU, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_SAI: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SAI, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_SAP: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SAP, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC); + + //special case: counts as martial for dwarves + case BASE_ITEM_DWARVENWARAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) && GetHasFeat(FEAT_DWARVEN, oPC)) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_WHIP: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_WHIP, oPC) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC); + + case BASE_ITEM_ELVEN_LIGHTBLADE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) && MyPRCGetRacialType(oPC) == RACIAL_TYPE_ELF) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE, oPC); + + case BASE_ITEM_ELVEN_THINBLADE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) && MyPRCGetRacialType(oPC) == RACIAL_TYPE_ELF) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE, oPC); + + case BASE_ITEM_ELVEN_COURTBLADE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) && MyPRCGetRacialType(oPC) == RACIAL_TYPE_ELF) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE, oPC); + + //special case: counts as martial for asherati + case BASE_ITEM_EAGLE_CLAW: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oPC) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oPC) && GetRacialType(oPC) == RACIAL_TYPE_ASHERATI) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW, oPC); + } + + return TRUE; +} + +//gets the main weapon proficiency feat needed for a given weapon - mostly for Favored Soul +int GetWeaponProfFeatByType(int nBaseType) +{ + switch(nBaseType) + { + case BASE_ITEM_SHORTSWORD: + return FEAT_WEAPON_PROFICIENCY_SHORTSWORD; + + case BASE_ITEM_LONGSWORD: + return FEAT_WEAPON_PROFICIENCY_LONGSWORD; + + case BASE_ITEM_BATTLEAXE: + return FEAT_WEAPON_PROFICIENCY_BATTLEAXE; + + case BASE_ITEM_BASTARDSWORD: + return FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD; + + case BASE_ITEM_LIGHTFLAIL: + return FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL; + + case BASE_ITEM_WARHAMMER: + return FEAT_WEAPON_PROFICIENCY_WARHAMMER; + + case BASE_ITEM_LONGBOW: + return FEAT_WEAPON_PROFICIENCY_LONGBOW; + + case BASE_ITEM_LIGHTMACE: + return FEAT_WEAPON_PROFICIENCY_LIGHT_MACE; + + case BASE_ITEM_HALBERD: + return FEAT_WEAPON_PROFICIENCY_HALBERD; + + case BASE_ITEM_SHORTBOW: + return FEAT_WEAPON_PROFICIENCY_SHORTBOW; + + case BASE_ITEM_TWOBLADEDSWORD: + return FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD; + + case BASE_ITEM_GREATSWORD: + return FEAT_WEAPON_PROFICIENCY_GREATSWORD; + + case BASE_ITEM_GREATAXE: + return FEAT_WEAPON_PROFICIENCY_GREATAXE; + + case BASE_ITEM_DART: + return FEAT_WEAPON_PROFICIENCY_DART; + + case BASE_ITEM_DIREMACE: + return FEAT_WEAPON_PROFICIENCY_DIRE_MACE; + + case BASE_ITEM_DOUBLEAXE: + return FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE; + + case BASE_ITEM_HEAVYFLAIL: + return FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL; + + case BASE_ITEM_LIGHTHAMMER: + return FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER; + + case BASE_ITEM_HANDAXE: + return FEAT_WEAPON_PROFICIENCY_HANDAXE; + + case BASE_ITEM_KAMA: + return FEAT_WEAPON_PROFICIENCY_KAMA; + + case BASE_ITEM_KATANA: + return FEAT_WEAPON_PROFICIENCY_KATANA; + + case BASE_ITEM_KUKRI: + return FEAT_WEAPON_PROFICIENCY_KUKRI; + + case BASE_ITEM_MORNINGSTAR: + return FEAT_WEAPON_PROFICIENCY_MORNINGSTAR; + + case BASE_ITEM_RAPIER: + return FEAT_WEAPON_PROFICIENCY_RAPIER; + + case BASE_ITEM_SCIMITAR: + return FEAT_WEAPON_PROFICIENCY_SCIMITAR; + + case BASE_ITEM_SCYTHE: + return FEAT_WEAPON_PROFICIENCY_SCYTHE; + + case BASE_ITEM_SHORTSPEAR: + return FEAT_WEAPON_PROFICIENCY_SHORTSPEAR; + + case BASE_ITEM_SHURIKEN: + return FEAT_WEAPON_PROFICIENCY_SHURIKEN; + + case BASE_ITEM_SICKLE: + return FEAT_WEAPON_PROFICIENCY_SICKLE; + + case BASE_ITEM_SLING: + return FEAT_WEAPON_PROFICIENCY_SLING; + + case BASE_ITEM_THROWINGAXE: + return FEAT_WEAPON_PROFICIENCY_THROWING_AXE; + + case BASE_ITEM_CSLASHWEAPON: + return FEAT_WEAPON_PROFICIENCY_CREATURE; + + case BASE_ITEM_CPIERCWEAPON: + return FEAT_WEAPON_PROFICIENCY_CREATURE; + + case BASE_ITEM_CBLUDGWEAPON: + return FEAT_WEAPON_PROFICIENCY_CREATURE; + + case BASE_ITEM_CSLSHPRCWEAP: + return FEAT_WEAPON_PROFICIENCY_CREATURE; + + case BASE_ITEM_TRIDENT: + return FEAT_WEAPON_PROFICIENCY_TRIDENT; + + case BASE_ITEM_DWARVENWARAXE: + return FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE; + + case BASE_ITEM_WHIP: + return FEAT_WEAPON_PROFICIENCY_WHIP; + + case BASE_ITEM_ELVEN_LIGHTBLADE: + return FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE; + + case BASE_ITEM_ELVEN_THINBLADE: + return FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE; + + case BASE_ITEM_ELVEN_COURTBLADE: + return FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE; + + case BASE_ITEM_HEAVY_PICK: + return FEAT_WEAPON_PROFICIENCY_HEAVY_PICK; + + case BASE_ITEM_LIGHT_PICK: + return FEAT_WEAPON_PROFICIENCY_LIGHT_PICK; + + case BASE_ITEM_SAI: + return FEAT_WEAPON_PROFICIENCY_SAI; + + case BASE_ITEM_NUNCHAKU: + return FEAT_WEAPON_PROFICIENCY_NUNCHAKU; + + case BASE_ITEM_FALCHION: + return FEAT_WEAPON_PROFICIENCY_FALCHION; + + case BASE_ITEM_SAP: + return FEAT_WEAPON_PROFICIENCY_SAP; + + case BASE_ITEM_KATAR: + return FEAT_WEAPON_PROFICIENCY_KATAR; + + case BASE_ITEM_HEAVY_MACE: + return FEAT_WEAPON_PROFICIENCY_HEAVY_MACE; + + case BASE_ITEM_MAUL: + return FEAT_WEAPON_PROFICIENCY_MAUL; + + case BASE_ITEM_DOUBLE_SCIMITAR: + return FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR; + + case BASE_ITEM_GOAD: + return FEAT_WEAPON_PROFICIENCY_GOAD; + + case BASE_ITEM_EAGLE_CLAW: + return FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW; + + default: + return FEAT_WEAPON_PROFICIENCY_SIMPLE; + } + + return 0; +} + +//resolves Weapon Prof feats to their ItemProp counterparts +int GetWeaponProfIPFeat(int nWeaponProfFeat) +{ + return nWeaponProfFeat - 3300; +} + +//:: Handles the feat emulation chain for Elven Lightblades +void DoEquipLightblade(object oPC, object oItem) +{ + effect eLightblade; + int bHasEffect; + + //:: Weapon Focus + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORT_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER, oPC)) + { + eLightblade = EffectBonusFeat(FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE); + bHasEffect = TRUE; + + //:: Epic Weapon Focus + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SHORTSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_RAPIER, oPC)) + { + eLightblade = EffectLinkEffects(eLightblade, EffectBonusFeat(FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE)); + } + //:: Weapon Specialization + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_SPECIALIZATION_RAPIER, oPC)) + { + eLightblade = EffectLinkEffects(eLightblade, EffectBonusFeat(FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE)); + + //:: Epic Weapon Specialization + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER, oPC)) + { + eLightblade = EffectLinkEffects(eLightblade, EffectBonusFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE)); + } + } + } + //:: Improved Critical + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORT_SWORD, oPC) || GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER, oPC)) + { + eLightblade = EffectLinkEffects(eLightblade, EffectBonusFeat(FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE)); + bHasEffect = TRUE; + } + + if (bHasEffect) + { + eLightblade = TagEffect(eLightblade, "LIGHTBLADE_FEAT_EMULATATION"); + eLightblade = SupernaturalEffect(eLightblade); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLightblade, oPC); + } +} + +/* //handles the feat chain for Elven Lightblades +void DoEquipLightblade(object oPC, object oItem) +{ + if(DEBUG) DoDebug("Checking Lightblade feats"); // optimised as some feats are prereq for others + if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORT_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER, oPC)) + { + SetCompositeAttackBonus(oPC, "LightbladeWF" + IntToString(nHand), 1, nHand); + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SHORTSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_RAPIER, oPC)) + SetCompositeAttackBonus(oPC, "LightbladeEpicWF" + IntToString(nHand), 2, nHand); + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_SPECIALIZATION_RAPIER, oPC)) + { + SetCompositeDamageBonusT(oItem, "LightbladeWS", 2); + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER, oPC)) + SetCompositeDamageBonusT(oItem, "LightbladeEpicWS", 4); + } + } + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORT_SWORD, oPC) || GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER, oPC)) + IPSafeAddItemProperty(oItem, ItemPropertyKeen(), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); +} + */ + +//:: Handles the feat emulation chain for Elven Thinblades +void DoEquipThinblade(object oPC, object oItem) +{ + effect eThinblade; + int bHasEffect; + + //:: Weapon Focus + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONG_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER, oPC)) + { + eThinblade = EffectBonusFeat(FEAT_WEAPON_FOCUS_ELVEN_THINBLADE); + bHasEffect = TRUE; + + //:: Epic Weapon Focus + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LONGSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_RAPIER, oPC)) + { + eThinblade = EffectLinkEffects(eThinblade, EffectBonusFeat(FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE)); + } + //:: Weapon Specialization + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONG_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_SPECIALIZATION_RAPIER, oPC)) + { + eThinblade = EffectLinkEffects(eThinblade, EffectBonusFeat(FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE)); + + //:: Epic Weapon Specialization + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LONGSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER, oPC)) + { + eThinblade = EffectLinkEffects(eThinblade, EffectBonusFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE)); + } + } + } + //:: Improved Critical + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LONG_SWORD, oPC) || GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER, oPC)) + { + eThinblade = EffectLinkEffects(eThinblade, EffectBonusFeat(FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE)); + bHasEffect = TRUE; + } + + if (bHasEffect) + { + eThinblade = TagEffect(eThinblade, "THINBLADE_FEAT_EMULATATION"); + eThinblade = SupernaturalEffect(eThinblade); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eThinblade, oPC); + } +} + +/* //handles the feat chain for Elven Thinblades +void DoEquipThinblade(object oPC, object oItem, int nHand) +{ + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONG_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER, oPC)) + { + SetCompositeAttackBonus(oPC, "ThinbladeWF" + IntToString(nHand), 1, nHand); + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_LONGSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_RAPIER, oPC)) + SetCompositeAttackBonus(oPC, "ThinbladeEpicWF" + IntToString(nHand), 2, nHand); + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONG_SWORD, oPC) || GetHasFeat(FEAT_WEAPON_SPECIALIZATION_RAPIER, oPC)) + { + SetCompositeDamageBonusT(oItem, "ThinbladeWS", 2); + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_LONGSWORD, oPC) || GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER, oPC)) + SetCompositeDamageBonusT(oItem, "ThinbladeEpicWS", 4); + } + } + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LONG_SWORD, oPC) || GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER, oPC)) + IPSafeAddItemProperty(oItem, ItemPropertyKeen(), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); +} */ + +//:: Handles the feat emulation chain for Elven Courtblades +void DoEquipCourtblade(object oPC, object oItem) +{ + effect eCourtblade; + int bHasEffect; + + //:: Weapon Focus + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_SWORD, oPC)) + { + eCourtblade = EffectBonusFeat(FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE); + bHasEffect = TRUE; + + //:: Epic Weapon Focus + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_GREATSWORD, oPC)) + { + eCourtblade = EffectLinkEffects(eCourtblade, EffectBonusFeat(FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE)); + } + //:: Weapon Specialization + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD, oPC)) + { + eCourtblade = EffectLinkEffects(eCourtblade, EffectBonusFeat(FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE)); + + //:: Epic Weapon Specialization + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_GREATSWORD, oPC)) + { + eCourtblade = EffectLinkEffects(eCourtblade, EffectBonusFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE)); + } + } + } + //:: Improved Critical + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_SWORD, oPC)) + { + eCourtblade = EffectLinkEffects(eCourtblade, EffectBonusFeat(FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE)); + bHasEffect = TRUE; + } + + if (bHasEffect) + { + eCourtblade = TagEffect(eCourtblade, "COURTBLADE_FEAT_EMULATATION"); + eCourtblade = SupernaturalEffect(eCourtblade); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eCourtblade, oPC); + } +} + +/* void DoEquipCourtblade(object oPC, object oItem, int nHand) +{ + if(GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_SWORD, oPC)) + { + SetCompositeAttackBonus(oPC, "CourtbladeWF" + IntToString(nHand), 1, nHand); + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_GREATSWORD, oPC)) + SetCompositeAttackBonus(oPC, "CourtbladeEpicWF" + IntToString(nHand), 2, nHand); + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD, oPC)) + { + SetCompositeDamageBonusT(oItem, "CourtbladeWS", 2); + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_GREATSWORD, oPC)) + SetCompositeDamageBonusT(oItem, "CourtbladeEpicWS", 4); + } + } + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_SWORD, oPC)) + IPSafeAddItemProperty(oItem, ItemPropertyKeen(), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); +} */ + +//clears any bonuses used to simulate feat chains on unequip +//:: None of this should be needed now -Jaysyn +void DoWeaponFeatUnequip(object oPC, object oItem, int nHand) +{ + // fluffyamoeba - going to assume redundant local var clearing isn't worth tradeoff + int nBaseType = GetBaseItemType(oItem); + if(nBaseType == BASE_ITEM_ELVEN_LIGHTBLADE) + { + if(DEBUG) DoDebug("Clearing Lightblade variables."); + SetCompositeAttackBonus(oPC, "LightbladeWF" + IntToString(nHand), 0, nHand); + SetCompositeAttackBonus(oPC, "LightbladeEpicWF" + IntToString(nHand), 0, nHand); + SetCompositeDamageBonusT(oItem, "LightbladeWS", 0); + SetCompositeDamageBonusT(oItem, "LightbladeEpicWS", 0); + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORT_SWORD, oPC) || GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER, oPC)) + RemoveSpecificProperty(oItem, ITEM_PROPERTY_KEEN, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY); + } + else if(nBaseType == BASE_ITEM_ELVEN_THINBLADE) + { + SetCompositeAttackBonus(oPC, "ThinbladeWF" + IntToString(nHand), 0, nHand); + SetCompositeAttackBonus(oPC, "ThinbladeEpicWF" + IntToString(nHand), 0, nHand); + SetCompositeDamageBonusT(oItem, "ThinbladeWS", 0); + SetCompositeDamageBonusT(oItem, "ThinbladeEpicWS", 0); + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_LONG_SWORD, oPC) || GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER, oPC)) + RemoveSpecificProperty(oItem, ITEM_PROPERTY_KEEN, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY); + } + else if(nBaseType == BASE_ITEM_ELVEN_COURTBLADE) + { + SetCompositeAttackBonus(oPC, "CourtbladeWF" + IntToString(nHand), 0, nHand); + SetCompositeAttackBonus(oPC, "CourtbladeEpicWF" + IntToString(nHand), 0, nHand); + SetCompositeDamageBonusT(oItem, "CourtbladeWS", 0); + SetCompositeDamageBonusT(oItem, "CourtbladeEpicWS", 0); + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_SWORD, oPC)) + RemoveSpecificProperty(oItem, ITEM_PROPERTY_KEEN, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY); + } +} + +int IsMeleeWeapon(int nBaseItemType) +{ + // Reject invalid base item values. + if (nBaseItemType == BASE_ITEM_INVALID) + { + return FALSE; + } + + // Only want melee weapons, exclude all others. + switch (nBaseItemType) + { + case BASE_ITEM_ALCHEMY: + case BASE_ITEM_AMULET: + case BASE_ITEM_ARMOR: + case BASE_ITEM_ARROW: + case BASE_ITEM_BELT: + case BASE_ITEM_BLANK_POTION: + case BASE_ITEM_BLANK_SCROLL: + case BASE_ITEM_BLANK_WAND: + case BASE_ITEM_BOLT: + case BASE_ITEM_BOOK: + case BASE_ITEM_BOOTS: + case BASE_ITEM_BRACER: + case BASE_ITEM_BULLET: + case BASE_ITEM_CLOAK: + case BASE_ITEM_CRAFTED_ROD: + case BASE_ITEM_CRAFTED_STAFF: + case BASE_ITEM_CRAFTMATERIALMED: + case BASE_ITEM_CRAFTMATERIALSML: + case BASE_ITEM_CREATUREITEM: + case BASE_ITEM_ENCHANTED_POTION: + case BASE_ITEM_ENCHANTED_SCROLL: + case BASE_ITEM_ENCHANTED_WAND: + case BASE_ITEM_GEM: + case BASE_ITEM_GLOVES: + case BASE_ITEM_GOLD: + case BASE_ITEM_GOLEM: + case BASE_ITEM_GRENADE: + case BASE_ITEM_HEALERSKIT: + case BASE_ITEM_HEAVYCROSSBOW: + case BASE_ITEM_HELMET: + case BASE_ITEM_KEY: + case BASE_ITEM_LARGEBOX: + case BASE_ITEM_LARGESHIELD: + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_LONGBOW: + case BASE_ITEM_MAGICROD: + case BASE_ITEM_MAGICSTAFF: + case BASE_ITEM_MAGICWAND: + case BASE_ITEM_MISCLARGE: + case BASE_ITEM_MISCMEDIUM: + case BASE_ITEM_MISCSMALL: + case BASE_ITEM_MISCTALL: + case BASE_ITEM_MISCTHIN: + case BASE_ITEM_MISCWIDE: + case BASE_ITEM_POISON: + case BASE_ITEM_POTIONS: + case BASE_ITEM_RING: + case BASE_ITEM_SCROLL: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_SHURIKEN: + case BASE_ITEM_SLING: + case BASE_ITEM_SMALLSHIELD: + case BASE_ITEM_SPELLSCROLL: + case BASE_ITEM_THIEVESTOOLS: + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_TORCH: + case BASE_ITEM_TOWERSHIELD: + case BASE_ITEM_TRAPKIT: + return FALSE; + } + + // Everything else assumed to be melee weapon. + return TRUE; +} + +int IsWeaponMartial(int nBaseItemType, object oPC) +{ + switch(nBaseItemType) + { + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_BATTLEAXE: + case BASE_ITEM_LIGHTFLAIL: + case BASE_ITEM_WARHAMMER: + case BASE_ITEM_LONGBOW: + case BASE_ITEM_HALBERD: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_HANDAXE: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SCIMITAR: + case BASE_ITEM_TRIDENT: + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_SAP: + case BASE_ITEM_MAUL: + case BASE_ITEM_FALCHION: + case BASE_ITEM_HEAVY_PICK: + case BASE_ITEM_LIGHT_PICK: + case BASE_ITEM_LIGHT_LANCE: + return TRUE; + + //special case: counts as martial for dwarves + case BASE_ITEM_DWARVENWARAXE: + if(GetHasFeat(FEAT_DWARVEN, oPC)) + return TRUE; + + //special case: counts as martial for asherati + case BASE_ITEM_EAGLE_CLAW: + if(GetRacialType(oPC) == RACIAL_TYPE_ASHERATI) + return TRUE; + + + default: + return FALSE; + } + return FALSE; +} + +int IsWeaponExotic(int nBaseItemType) +{ + switch(nBaseItemType) + { + case BASE_ITEM_BASTARDSWORD: + case BASE_ITEM_TWOBLADEDSWORD: + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_KAMA: + case BASE_ITEM_KATANA: + case BASE_ITEM_KUKRI: + case BASE_ITEM_SHURIKEN: + case BASE_ITEM_DWARVENWARAXE: + case BASE_ITEM_WHIP: + case BASE_ITEM_ELVEN_LIGHTBLADE: + case BASE_ITEM_ELVEN_COURTBLADE: + case BASE_ITEM_ELVEN_THINBLADE: + case BASE_ITEM_SAI: + case BASE_ITEM_NUNCHAKU: + case BASE_ITEM_DOUBLE_SCIMITAR: + case BASE_ITEM_EAGLE_CLAW: + return TRUE; + } + + return FALSE; +} + +//checks to see if the PC can wield the weapon. If not, applies a -4 penalty. +void DoProficiencyCheck(object oPC, object oItem, int nHand) +{ + int bProficient = FALSE; + int nBase = GetBaseItemType(oItem); + + bProficient = IsProficient(oPC, nBase); + if (!bProficient) + { + if (nHand == ATTACK_BONUS_ONHAND) + { + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_ONHAND), -4, ATTACK_BONUS_ONHAND); + } + if (nHand == ATTACK_BONUS_OFFHAND) + { + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_OFFHAND), -4, ATTACK_BONUS_OFFHAND); + } + + // Handle specific double-sided weapon logic + if (nBase == BASE_ITEM_DOUBLEAXE || nBase == BASE_ITEM_TWOBLADEDSWORD || nBase == BASE_ITEM_DIREMACE || nBase == BASE_ITEM_DOUBLE_SCIMITAR) + { // This should only affect offhand if the main hand is these types + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_OFFHAND), -4, ATTACK_BONUS_OFFHAND); + } + } + else + { + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_ONHAND), 0, ATTACK_BONUS_ONHAND); + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_OFFHAND), 0, ATTACK_BONUS_OFFHAND); + } + +} + +//checks to see if the PC can wield the weapon. If not, applies a -4 penalty. +/* void DoProficiencyCheck(object oPC, object oItem, int nHand) +{ + int bProficient = FALSE; + + int nBase = GetBaseItemType(oItem); + bProficient = IsProficient(oPC, nBase); + + // Warlock special code + if (GetTag(oItem) == "prc_eldrtch_glv") + bProficient = TRUE; + // Incarnate Weapon + if (GetTag(oItem) == "moi_incarnatewpn") + bProficient = TRUE; + // Skarn Spine + if (GetTag(oItem) == "skarn_spine") + bProficient = TRUE; + + if(!bProficient) + { + if (DEBUG) DoDebug(GetName(oPC)+" is non-proficient with "+GetName(oItem)); + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(nHand), -4, ATTACK_BONUS_ONHAND); + if(nBase == BASE_ITEM_DOUBLEAXE || nBase == BASE_ITEM_TWOBLADEDSWORD || nBase == BASE_ITEM_DIREMACE || nBase == BASE_ITEM_DOUBLE_SCIMITAR) + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_OFFHAND), -4, ATTACK_BONUS_OFFHAND); + } + else + { + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(nHand), 0, ATTACK_BONUS_ONHAND); + if(nBase == BASE_ITEM_DOUBLEAXE || nBase == BASE_ITEM_TWOBLADEDSWORD || nBase == BASE_ITEM_DIREMACE || nBase == BASE_ITEM_DOUBLE_SCIMITAR) + SetCompositeAttackBonus(oPC, "Unproficient" + IntToString(ATTACK_BONUS_OFFHAND), 0, ATTACK_BONUS_OFFHAND); + } +} + */ + +void DoWeaponEquip(object oPC, object oItem, int nHand) +{ + if(GetIsDM(oPC) || !GetIsWeapon(oItem)) return; + + if(GetTag(oItem) == "prc_eldrtch_glv") return; + if(GetTag(oItem) == "PRC_PYRO_LASH_WHIP") return; + + //initialize variables + int nRealSize = PRCGetCreatureSize(oPC); //size for Finesse/TWF + int nSize = nRealSize; //size for equipment restrictions + int nWeaponSize = GetWeaponSize(oItem); + int nStrMod = GetAbilityModifier(ABILITY_STRENGTH, oPC); + int nElfFinesse = GetAbilityModifier(ABILITY_DEXTERITY, oPC) - nStrMod; + int nTHFDmgBonus = nStrMod / 2; + int nBaseType = GetBaseItemType(oItem); + + //Powerful Build bonus + if(GetHasFeat(FEAT_RACE_POWERFUL_BUILD, oPC)) + nSize++; + //Monkey Grip + if(GetHasFeat(FEAT_MONKEY_GRIP, oPC)) + { + nSize++; + // If you try and use the big weapons + if (nWeaponSize > nRealSize) + { + SetCompositeAttackBonus(oPC, "MonkeyGripL", -2, ATTACK_BONUS_OFFHAND); + SetCompositeAttackBonus(oPC, "MonkeyGripR", -2, ATTACK_BONUS_ONHAND); + } + else + { + SetCompositeAttackBonus(oPC, "MonkeyGripL", 0, ATTACK_BONUS_OFFHAND); + SetCompositeAttackBonus(oPC, "MonkeyGripR", 0, ATTACK_BONUS_ONHAND); + } + + } + + if(DEBUG) DoDebug("prc_inc_wpnrest - Weapon size: " + IntToString(nWeaponSize)); + if(DEBUG) DoDebug("prc_inc_wpnrest - Character Size: " + IntToString(nSize)); + + //check to make sure it's not too large, or that you're not trying to TWF with 2-handers + if((nWeaponSize > 1 + nSize && nHand == ATTACK_BONUS_ONHAND) + || ((nWeaponSize > nSize || GetWeaponSize(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)) > nSize) && nHand == ATTACK_BONUS_OFFHAND)) + { + if(DEBUG) DoDebug("prc_inc_wpnrest: Forcing unequip due to size"); + if(nHand == ATTACK_BONUS_OFFHAND) + nHand = INVENTORY_SLOT_LEFTHAND; + else + nHand = INVENTORY_SLOT_RIGHTHAND; + // Force unequip + ForceUnequip(oPC, oItem, nHand); + } + +//:: Oversized TWF +//:: Check if the player is a Ranger, wearing medium/heavy armor, and does not have Two-Weapon Fighting feat + int bIsRestricted = FALSE; + + // Check if the player has levels in the Ranger class + if (GetLevelByClass(CLASS_TYPE_RANGER, oPC) > 0) + { + // Check if the player is wearing medium or heavy armor + int nArmorType = GetArmorType(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC)); + if (nArmorType == ARMOR_TYPE_MEDIUM || nArmorType == ARMOR_TYPE_HEAVY) + { + // Check if the player does not have the Two-Weapon Fighting feat + if (!GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oPC)) + { + // Set the restricted flag to TRUE if all conditions are met + bIsRestricted = TRUE; + } + } + } + //:: Proceed with OSTWF bonuses if the restrictions are not met + if (!bIsRestricted) + { + if (GetHasFeat(FEAT_OTWF, oPC)) + { + // When wielding a one-handed weapon in your off hand, you take penalties for fighting with two weapons as if you were wielding a light weapon in your off hand + if (nWeaponSize == nRealSize && nHand == ATTACK_BONUS_OFFHAND) + { + SetCompositeAttackBonus(oPC, "OTWFL", 2, ATTACK_BONUS_OFFHAND); + SetCompositeAttackBonus(oPC, "OTWFR", 2, ATTACK_BONUS_ONHAND); + } + else + { + SetCompositeAttackBonus(oPC, "OTWFL", 0, ATTACK_BONUS_OFFHAND); + SetCompositeAttackBonus(oPC, "OTWFR", 0, ATTACK_BONUS_ONHAND); + } + + } + } + + //check for proficiency + DoProficiencyCheck(oPC, oItem, nHand); + +//:: This is no longer needed with NWN:EE - Jaysyn +/* //simulate Weapon Finesse for Elven *blades + if((nBaseType == BASE_ITEM_ELVEN_LIGHTBLADE || nBaseType == BASE_ITEM_ELVEN_THINBLADE + || nBaseType == BASE_ITEM_ELVEN_COURTBLADE) && GetHasFeat(FEAT_WEAPON_FINESSE, oPC) && nElfFinesse > 0) + { + if(nHand == ATTACK_BONUS_ONHAND) + SetCompositeAttackBonus(oPC, "ElfFinesseRH", nElfFinesse, nHand); + else if(nHand == ATTACK_BONUS_OFFHAND) + SetCompositeAttackBonus(oPC, "ElfFinesseLH", nElfFinesse, nHand); + } */ + //Two-hand damage bonus + if(!GetWeaponRanged(oItem) && PRCLargeWeaponCheck(nBaseType, nWeaponSize) + && (nWeaponSize == nSize + 1 || (nWeaponSize == nRealSize + 1 && GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC) == OBJECT_INVALID) && nRealSize > CREATURE_SIZE_SMALL)) + { + if(DEBUG) DoDebug("prc_inc_wpnrest - Two-hand Damage Bonus (Before Enhancement): " + IntToString(nTHFDmgBonus)); + nTHFDmgBonus += IPGetWeaponEnhancementBonus(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS, FALSE);//include temp effects here + if(DEBUG) DoDebug("prc_inc_wpnrest - Two-hand Damage Bonus: " + IntToString(nTHFDmgBonus)); + SetCompositeDamageBonusT(oItem, "THFBonus", nTHFDmgBonus); + } + + //if a 2-hander, then unequip shield/offhand weapon + if(nWeaponSize == 1 + nSize && nHand == ATTACK_BONUS_ONHAND) + // Force unequip + ForceUnequip(oPC, GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC), INVENTORY_SLOT_LEFTHAND); + + //apply TWF penalty if a one-handed, not light weapon in offhand - -4/-4 etc isntead of -2/-2 + //Does not apply to small races due to weapon size-up. Stupid size equip hardcoded restrictions. + if(nWeaponSize == nRealSize && nHand == ATTACK_BONUS_OFFHAND && nRealSize > CREATURE_SIZE_MEDIUM) + { + // Assign penalty + if(DEBUG) DoDebug("prc_inc_wpnrest - OTWFPenalty: " + IntToString(-2)); + SetCompositeAttackBonus(oPC, "OTWFPenalty", -2); + } + else + { + SetCompositeAttackBonus(oPC, "OTWFPenalty", 0); + } + + + //:: Handle feat emulation Elven Blades + if(nBaseType == BASE_ITEM_ELVEN_LIGHTBLADE) + DoEquipLightblade(oPC, oItem); + else if(nBaseType == BASE_ITEM_ELVEN_THINBLADE) + DoEquipThinblade(oPC, oItem); + else if(nBaseType == BASE_ITEM_ELVEN_COURTBLADE) + DoEquipCourtblade(oPC, oItem); + + DoRacialEquip(oPC, nBaseType); +} + +void DoWeaponsEquip(object oPC) +{ + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + DelayCommand(0.2, DoWeaponEquip(oPC, oWeapon, ATTACK_BONUS_ONHAND)); + oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + DelayCommand(0.2, DoWeaponEquip(oPC, oWeapon, ATTACK_BONUS_OFFHAND)); +} + +void DoRacialEquip(object oPC, int nBaseType) +{ + if(GetRacialType(oPC) == RACIAL_TYPE_NEANDERTHAL) + { + if (nBaseType == BASE_ITEM_CLUB || + nBaseType == BASE_ITEM_SHORTSPEAR || + nBaseType == BASE_ITEM_QUARTERSTAFF || + nBaseType == BASE_ITEM_SHORTBOW || + nBaseType == BASE_ITEM_SLING || + nBaseType == BASE_ITEM_THROWINGAXE) + { + SetCompositeAttackBonus(oPC, "PrimitiveWeapon", 1); + } + else + SetCompositeAttackBonus(oPC, "PrimitiveWeapon", 0); + } +} + +//:: void main (){} \ No newline at end of file diff --git a/src/include/prc_ip_srcost.nss b/src/include/prc_ip_srcost.nss new file mode 100644 index 0000000..f166843 --- /dev/null +++ b/src/include/prc_ip_srcost.nss @@ -0,0 +1,49 @@ +const int IP_CONST_SPELLRESISTANCEBONUS_34 = 12; +const int IP_CONST_SPELLRESISTANCEBONUS_36 = 13; +const int IP_CONST_SPELLRESISTANCEBONUS_38 = 14; +const int IP_CONST_SPELLRESISTANCEBONUS_40 = 15; +const int IP_CONST_SPELLRESISTANCEBONUS_42 = 16; +const int IP_CONST_SPELLRESISTANCEBONUS_44 = 17; +const int IP_CONST_SPELLRESISTANCEBONUS_46 = 18; +const int IP_CONST_SPELLRESISTANCEBONUS_48 = 19; +const int IP_CONST_SPELLRESISTANCEBONUS_50 = 20; +const int IP_CONST_SPELLRESISTANCEBONUS_52 = 21; +const int IP_CONST_SPELLRESISTANCEBONUS_54 = 22; +const int IP_CONST_SPELLRESISTANCEBONUS_56 = 23; +const int IP_CONST_SPELLRESISTANCEBONUS_58 = 24; +const int IP_CONST_SPELLRESISTANCEBONUS_60 = 25; +const int IP_CONST_SPELLRESISTANCEBONUS_11 = 26; +const int IP_CONST_SPELLRESISTANCEBONUS_13 = 27; +const int IP_CONST_SPELLRESISTANCEBONUS_15 = 28; +const int IP_CONST_SPELLRESISTANCEBONUS_17 = 29; +const int IP_CONST_SPELLRESISTANCEBONUS_19 = 30; +const int IP_CONST_SPELLRESISTANCEBONUS_21 = 31; +const int IP_CONST_SPELLRESISTANCEBONUS_23 = 32; +const int IP_CONST_SPELLRESISTANCEBONUS_25 = 33; +const int IP_CONST_SPELLRESISTANCEBONUS_27 = 34; +const int IP_CONST_SPELLRESISTANCEBONUS_29 = 35; +const int IP_CONST_SPELLRESISTANCEBONUS_31 = 36; +const int IP_CONST_SPELLRESISTANCEBONUS_33 = 37; +const int IP_CONST_SPELLRESISTANCEBONUS_35 = 38; +const int IP_CONST_SPELLRESISTANCEBONUS_37 = 39; +const int IP_CONST_SPELLRESISTANCEBONUS_39 = 40; +const int IP_CONST_SPELLRESISTANCEBONUS_41 = 41; +const int IP_CONST_SPELLRESISTANCEBONUS_43 = 42; +const int IP_CONST_SPELLRESISTANCEBONUS_45 = 43; +const int IP_CONST_SPELLRESISTANCEBONUS_47 = 44; +const int IP_CONST_SPELLRESISTANCEBONUS_49 = 45; +const int IP_CONST_SPELLRESISTANCEBONUS_51 = 46; +const int IP_CONST_SPELLRESISTANCEBONUS_53 = 47; +const int IP_CONST_SPELLRESISTANCEBONUS_55 = 48; +const int IP_CONST_SPELLRESISTANCEBONUS_57 = 49; +const int IP_CONST_SPELLRESISTANCEBONUS_59 = 50; +const int IP_CONST_SPELLRESISTANCEBONUS_61 = 51; +const int IP_CONST_SPELLRESISTANCEBONUS_1 = 52; +const int IP_CONST_SPELLRESISTANCEBONUS_2 = 53; +const int IP_CONST_SPELLRESISTANCEBONUS_3 = 54; +const int IP_CONST_SPELLRESISTANCEBONUS_4 = 55; +const int IP_CONST_SPELLRESISTANCEBONUS_5 = 56; +const int IP_CONST_SPELLRESISTANCEBONUS_6 = 57; +const int IP_CONST_SPELLRESISTANCEBONUS_7 = 58; +const int IP_CONST_SPELLRESISTANCEBONUS_8 = 59; +const int IP_CONST_SPELLRESISTANCEBONUS_9 = 60; diff --git a/src/include/prc_ipfeat_const.nss b/src/include/prc_ipfeat_const.nss new file mode 100644 index 0000000..a1829be --- /dev/null +++ b/src/include/prc_ipfeat_const.nss @@ -0,0 +1,1600 @@ + +//const int FEAT_WEAPON_FOCUS_WHIP = 993; +//const int FEAT_IMPROVED_CRITICAL_WHIP = 995; + +//::: iprp_feats + +const int IP_CONST_FEAT_IMPROVED_CRITICAL_UNARMED = 20; +const int IP_CONST_FEAT_WEAPON_FINESSE = 95; +const int IP_CONST_FEAT_TRACK = 94; +const int IP_CONST_FEAT_IMPROVED_UNARMED_STRIKE = 93; +const int IP_CONST_FEAT_TRAPFINDING = 392; +const int IP_CONST_FEAT_DEFLECT_ARROWS = 92; +const int IP_CONST_FEAT_AWESOME_BLOW = 571; +const int IP_CONST_FEAT_PRC_ATTACK = 572; +const int IP_CONST_FEAT_BANE_MAGIC_DRAGON = 91; +const int IP_CONST_FEAT_BONES_EARTH = 90; +const int IP_CONST_FEAT_BREATHLESS = 574; +const int IP_CONST_FEAT_IMPROVED_BULL_RUSH = 89; +const int IP_CONST_FEAT_WATER_BREATHING = 575; +const int IP_CONST_FEAT_TURN_UNDEAD = 88; +const int IP_CONST_FEAT_THUG = 576; +const int IP_CONST_FEAT_MOUNTED_COMBAT = 577; +const int IP_CONST_FEAT_MOUNTED_ARCHERY = 578; +const int IP_CONST_FEAT_PRC_DEATH_ATTACK = 579; +const int IP_CONST_FEAT_RAGE = 87; +const int IP_CONST_FEAT_BLINDSIGHT_60_FT = 4670; +const int IP_CONST_FEAT_DAYLIGHTADAPT = 581; + +//:: Crafting feats +const int IP_FEAT_FEAT_SCRIBE_SCROLL = 4340; +const int IP_FEAT_FEAT_BREW_POTION = 4339; +const int IP_FEAT_FEAT_CRAFT_WONDROUS = 4342; +const int IP_FEAT_FEAT_CRAFT_ARMS_ARMOR = 4343; +const int IP_FEAT_FEAT_CRAFT_WAND = 4341; +const int IP_FEAT_FEAT_CRAFT_ROD = 4344; +const int IP_FEAT_FEAT_CRAFT_STAFF = 4345; +const int IP_FEAT_FEAT_FORGE_RING = 4346; + +//:: Sneak Attacks +const int IP_CONST_FEAT_ROGUE_SA_1D6 = 32; +const int IP_CONST_FEAT_ROGUE_SA_2D6 = 33; +const int IP_CONST_FEAT_ROGUE_SA_3D6 = 34; +const int IP_CONST_FEAT_ROGUE_SA_4D6 = 301; +const int IP_CONST_FEAT_ROGUE_SA_5D6 = 39; +const int IP_CONST_FEAT_ROGUE_SA_6D6 = 302; +const int IP_CONST_FEAT_ROGUE_SA_7D6 = 303; +const int IP_CONST_FEAT_ROGUE_SA_8D6 = 304; +const int IP_CONST_FEAT_ROGUE_SA_9D6 = 305; +const int IP_CONST_FEAT_ROGUE_SA_10D6 = 306; +const int IP_CONST_FEAT_ROGUE_SA_11D6 = 307; +const int IP_CONST_FEAT_ROGUE_SA_12D6 = 308; +const int IP_CONST_FEAT_ROGUE_SA_13D6 = 309; +const int IP_CONST_FEAT_ROGUE_SA_14D6 = 310; +const int IP_CONST_FEAT_ROGUE_SA_15D6 = 311; +const int IP_CONST_FEAT_ROGUE_SA_16D6 = 312; +const int IP_CONST_FEAT_ROGUE_SA_17D6 = 313; +const int IP_CONST_FEAT_ROGUE_SA_18D6 = 314; +const int IP_CONST_FEAT_ROGUE_SA_19D6 = 315; +const int IP_CONST_FEAT_ROGUE_SA_20D6 = 316; + +const int IP_CONST_FEAT_BG_SA_1D6 = 276; +const int IP_CONST_FEAT_BG_SA_2D6 = 277; +const int IP_CONST_FEAT_BG_SA_3D6 = 278; +const int IP_CONST_FEAT_BG_SA_4D6 = 279; +const int IP_CONST_FEAT_BG_SA_5D6 = 280; +const int IP_CONST_FEAT_BG_SA_6D6 = 281; +const int IP_CONST_FEAT_BG_SA_7D6 = 282; +const int IP_CONST_FEAT_BG_SA_8D6 = 283; +const int IP_CONST_FEAT_BG_SA_9D6 = 284; +const int IP_CONST_FEAT_BG_SA_10D6 = 285; +const int IP_CONST_FEAT_BG_SA_11D6 = 286; +const int IP_CONST_FEAT_BG_SA_12D6 = 287; +const int IP_CONST_FEAT_BG_SA_13D6 = 288; +const int IP_CONST_FEAT_BG_SA_14D6 = 289; +const int IP_CONST_FEAT_BG_SA_15D6 = 290; + +const int IP_CONST_FEAT_EPIC_SA_1D6 = 291; +const int IP_CONST_FEAT_EPIC_SA_2D6 = 292; +const int IP_CONST_FEAT_EPIC_SA_3D6 = 293; +const int IP_CONST_FEAT_EPIC_SA_4D6 = 294; +const int IP_CONST_FEAT_EPIC_SA_5D6 = 295; +const int IP_CONST_FEAT_EPIC_SA_6D6 = 296; +const int IP_CONST_FEAT_EPIC_SA_7D6 = 297; +const int IP_CONST_FEAT_EPIC_SA_8D6 = 298; +const int IP_CONST_FEAT_EPIC_SA_9D6 = 299; +const int IP_CONST_FEAT_EPIC_SA_10D6 = 300; + +const int IP_CONST_FEAT_DEATHATTACK = 317; +const int IP_CONST_FEAT_DEATHATTACK2 = 318; +const int IP_CONST_FEAT_DEATHATTACK3 = 319; +const int IP_CONST_FEAT_DEATHATTACK4 = 320; +const int IP_CONST_FEAT_DEATHATTACK5 = 321; +const int IP_CONST_FEAT_DEATHATTACK6 = 322; +const int IP_CONST_FEAT_DEATHATTACK7 = 323; +const int IP_CONST_FEAT_DEATHATTACK8 = 324; +const int IP_CONST_FEAT_DEATHATTACK9 = 325; +const int IP_CONST_FEAT_DEATHATTACK10 = 326; +const int IP_CONST_FEAT_DEATHATTACK11 = 327; +const int IP_CONST_FEAT_DEATHATTACK12 = 328; +const int IP_CONST_FEAT_DEATHATTACK13 = 329; +const int IP_CONST_FEAT_DEATHATTACK14 = 330; +const int IP_CONST_FEAT_DEATHATTACK15 = 331; +const int IP_CONST_FEAT_DEATHATTACK16 = 332; +const int IP_CONST_FEAT_DEATHATTACK17 = 333; +const int IP_CONST_FEAT_DEATHATTACK18 = 334; +const int IP_CONST_FEAT_DEATHATTACK19 = 335; +const int IP_CONST_FEAT_DEATHATTACK20 = 336; + +const int IP_CONST_FEAT_BarbEndurance = 337; +const int IP_CONST_FEAT_WeapFocCreature = 338; +const int IP_CONST_FEAT_ImpCritCreature = 339; +const int IP_CONST_FEAT_WeapSpecCreature = 340; +const int IP_CONST_FEAT_WeapEpicFocCreature = 341; +const int IP_CONST_FEAT_WeapEpicSpecCreature = 342; + +const int IP_CONST_FEAT_IMPROVED_CRITICAL_DAGGER = 343; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_DART = 344; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW = 345; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW = 346; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_MACE = 347; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_MORNING_STAR = 348; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_STAFF = 349; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SPEAR = 350; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SICKLE = 351; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SLING = 352; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LONGBOW = 353; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SHORTBOW = 354; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SHORT_SWORD = 355; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_RAPIER = 356; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SCIMITAR = 357; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LONG_SWORD = 358; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_GREAT_SWORD = 359; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_HAND_AXE = 360; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_THROWING_AXE = 361; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_BATTLE_AXE = 362; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_GREAT_AXE = 363; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_HALBERD = 364; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER = 365; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL = 366; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_WAR_HAMMER = 367; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL = 368; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_KAMA = 369; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_KUKRI = 370; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SHURIKEN = 371; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SCYTHE = 372; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_KATANA = 373; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_BASTARD_SWORD = 374; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_DIRE_MACE = 375; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_DOUBLE_AXE = 376; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD = 377; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_CLUB = 378; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_DWAXE = 379; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_WHIP = 380; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_MINDBLADE = 388; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_LANCE = 15927; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_PICK = 15928; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_PICK = 15929; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SAI = 15930; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_NUNCHAKU = 15931; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_FALCHION = 15932; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_SAP = 15933; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_KATAR = 15934; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_HEAVY_MACE = 15935; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_MAUL = 15936; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_DBL_SCIMITAR = 15937; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_GOAD = 15938; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE = 15979; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE = 15987; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE = 15995; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_EAGLE_CLAW = 15876; +const int IP_CONST_FEAT_IMPROVED_CRITICAL_TRIDENT = 15877; + + +const int IP_CONST_FEAT_SF_CODE = 381; +const int IP_CONST_FEAT_UNCANNY_DODGE1 = 382; +const int IP_CONST_FEAT_LOWLIGHT_VISION = 383; +const int IP_CONST_FEAT_NEW_PWATK = 384; +const int IP_CONST_FEAT_NEW_IPWATK = 385; +const int IP_CONST_FEAT_DARKVISION = 391; + +const int IP_CONST_FEAT_EPIC_AUTO_STILL_I = 204; +const int IP_CONST_FEAT_EPIC_AUTO_STILL_II = 205; +const int IP_CONST_FEAT_EPIC_AUTO_STILL_III = 206; +const int IP_CONST_FEAT_EPIC_AUTO_QUICKEN_I = 207; +const int IP_CONST_FEAT_EPIC_AUTO_QUICKEN_II = 208; +const int IP_CONST_FEAT_EPIC_AUTO_QUICKEN_III = 209; +const int IP_CONST_FEAT_EPIC_AUTO_SILENT_I = 210; +const int IP_CONST_FEAT_EPIC_AUTO_SILENT_II = 211; +const int IP_CONST_FEAT_EPIC_AUTO_SILENT_III = 212; + + +const int IP_CONST_FEAT_KI_STRIKE = 254; + +const int IP_CONST_FEAT_REND = 255; +const int IP_CONST_FEAT_SPELL10 = 256; +//const int IP_CONST_FEAT_MOBILITY = 27; // Provided by Bioware in 1.66 +//const int IP_CONST_FEAT_WHIRLWIND = 29; // ------------||------------- +const int IP_CONST_FEAT_BLINDFIGHT = 257; +const int IP_CONST_FEAT_SPRINGATTACK = 258; +const int IP_CONST_FEAT_EVASION = 386; +const int IP_CONST_FEAT_IMPEVASION = 387; +const int IP_CONST_FEAT_GREAT_CLEAVE = 260; +const int IP_CONST_FEAT_IMPROVED_INIT = 261; +const int IP_CONST_FEAT_BLOODED = 270; + +const int IP_CONST_FEAT_POWER_ATTACK_SINGLE_RADIAL = 252; +const int IP_CONST_FEAT_POWER_ATTACK_FIVES_RADIAL = 253; +const int IP_CONST_FEAT_PRC_POWER_ATTACK_QUICKS_RADIAL = 262; + +const int IP_CONST_FEAT_TELEPORT_MANAGEMENT_RADIAL = 263; + +const int IP_CONST_FEAT_EPIC_REST = 399; + +//:: Psionic System Feats +const int IP_CONST_FEAT_PSIONIC_FOCUS = 259; +const int IP_CONST_FEAT_AUGMENT_PSIONICS_QUICKSELECTS = 584; +const int IP_CONST_FEAT_AUGMENT_PSIONICS_DIGITS_0_4 = 585; +const int IP_CONST_FEAT_AUGMENT_PSIONICS_DIGITS_5_9 = 586; +const int IP_CONST_FEAT_AUGMENT_PSIONICS_TENS = 587; +const int IP_CONST_FEAT_AUGMENT_QUICKSELECTS_2 = 586; + + +//:: PnP Weapon Feats +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTSWORD = 4601; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LONGSWORD = 4602; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_BATTLEAXE = 4603; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD = 4604; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL = 4605; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_WARHAMMER = 4606; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LONGBOW = 4607; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_MACE = 4608; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HALBERD = 4609; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTBOW = 4610; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD = 4611; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_GREATSWORD = 4612; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_GREATAXE = 4613; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_DART = 4614; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_DIRE_MACE = 4615; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE = 4616; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL = 4617; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER = 4618; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HANDAXE = 4619; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_KAMA = 4620; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_KATANA = 4621; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_KUKRI = 4622; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_MORNINGSTAR = 4623; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_RAPIER = 4624; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SCIMITAR = 4625; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SCYTHE = 4626; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTSPEAR = 4627; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SHURIKEN = 4628; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SICKLE = 4629; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SLING = 4630; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_THROWING_AXE = 4631; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_TRIDENT = 4632; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE = 4633; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_WHIP = 4634; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE = 4635; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE = 4636; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE = 4637; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE = 4638; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_PICK = 4639; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_PICK = 4640; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SAI = 4641; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHUKU = 4642; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_FALCHION = 4643; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SAP = 4644; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_KATAR = 4645; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_MACE = 4646; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_MAUL = 4647; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR = 4648; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_GOAD = 4649; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW = 4650; + +const int IP_CONST_FEAT_WEAPON_FOCUS_CLUB = 100; +const int IP_CONST_FEAT_WEAPON_FOCUS_DAGGER = 101; +const int IP_CONST_FEAT_WEAPON_FOCUS_DART = 102; +const int IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW = 103; +const int IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW = 104; +const int IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_MACE = 105; +const int IP_CONST_FEAT_WEAPON_FOCUS_MORNING_STAR = 106; +const int IP_CONST_FEAT_WEAPON_FOCUS_STAFF = 107; +const int IP_CONST_FEAT_WEAPON_FOCUS_SPEAR = 108; +const int IP_CONST_FEAT_WEAPON_FOCUS_SICKLE = 109; +const int IP_CONST_FEAT_WEAPON_FOCUS_SLING = 110; +const int IP_CONST_FEAT_WEAPON_FOCUS_UNARMED_STRIKE = 111; +const int IP_CONST_FEAT_WEAPON_FOCUS_LONGBOW = 112; +const int IP_CONST_FEAT_WEAPON_FOCUS_SHORTBOW = 113; +const int IP_CONST_FEAT_WEAPON_FOCUS_SHORT_SWORD = 114; +const int IP_CONST_FEAT_WEAPON_FOCUS_RAPIER = 115; +const int IP_CONST_FEAT_WEAPON_FOCUS_SCIMITAR = 116; +const int IP_CONST_FEAT_WEAPON_FOCUS_LONG_SWORD = 117; +const int IP_CONST_FEAT_WEAPON_FOCUS_GREAT_SWORD = 118; +const int IP_CONST_FEAT_WEAPON_FOCUS_HAND_AXE = 119; +const int IP_CONST_FEAT_WEAPON_FOCUS_THROWING_AXE = 120; +const int IP_CONST_FEAT_WEAPON_FOCUS_BATTLE_AXE = 121; +const int IP_CONST_FEAT_WEAPON_FOCUS_GREAT_AXE = 122; +const int IP_CONST_FEAT_WEAPON_FOCUS_HALBERD = 123; +const int IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_HAMMER = 124; +const int IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_FLAIL = 125; +const int IP_CONST_FEAT_WEAPON_FOCUS_WAR_HAMMER = 126; +const int IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_FLAIL = 127; +const int IP_CONST_FEAT_WEAPON_FOCUS_KAMA = 128; +const int IP_CONST_FEAT_WEAPON_FOCUS_KUKRI = 129; +const int IP_CONST_FEAT_WEAPON_FOCUS_SHURIKEN = 130; +const int IP_CONST_FEAT_WEAPON_FOCUS_SCYTHE = 131; +const int IP_CONST_FEAT_WEAPON_FOCUS_KATANA = 132; +const int IP_CONST_FEAT_WEAPON_FOCUS_BASTARD_SWORD = 133; +const int IP_CONST_FEAT_WEAPON_FOCUS_DIRE_MACE = 134; +const int IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_AXE = 135; +const int IP_CONST_FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD = 136; +const int IP_CONST_FEAT_WEAPON_FOCUS_DWAXE = 137; +const int IP_CONST_FEAT_WEAPON_FOCUS_WHIP = 138; +const int IP_CONST_FEAT_WEAPON_FOCUS_MINDBLADE = 139; +const int IP_CONST_FEAT_WEAPON_FOCUS_RAY = 140; +const int IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT = 1072; +const int IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_LANCE = 15879; +const int IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_PICK = 15880; +const int IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_PICK = 15881; +const int IP_CONST_FEAT_WEAPON_FOCUS_SAI = 15882; +const int IP_CONST_FEAT_WEAPON_FOCUS_NUNCHAKU = 15883; +const int IP_CONST_FEAT_WEAPON_FOCUS_FALCHION = 15884; +const int IP_CONST_FEAT_WEAPON_FOCUS_SAP = 15885; +const int IP_CONST_FEAT_WEAPON_FOCUS_KATAR = 15886; +const int IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_MACE = 15887; +const int IP_CONST_FEAT_WEAPON_FOCUS_MAUL = 15888; +const int IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR = 15889; +const int IP_CONST_FEAT_WEAPON_FOCUS_GOAD = 15890; +const int IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE = 15975; +const int IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW = 15878; +const int IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE = 15983; +const int IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE = 15991; + +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_CLUB = 16000; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_DAGGER = 16001; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_DART = 16002; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW = 16003; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW = 16004; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE = 16005; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_MORNING_STAR = 16006; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_STAFF = 16007; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SPEAR = 16008; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SICKLE = 16009; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SLING = 16010; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE = 16011; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LONGBOW = 16012; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHORTBOW = 16013; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD = 16014; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_RAPIER = 16015; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SCIMITAR = 16016; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LONG_SWORD = 16017; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD = 16018; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_HAND_AXE = 16019; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_THROWING_AXE = 16020; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE = 16021; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_GREAT_AXE = 16022; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_HALBERD = 16023; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER = 16024; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL = 16025; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER = 16026; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL = 16027; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_KAMA = 16028; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_KUKRI = 16029; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHURIKEN = 16030; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SCYTHE = 16031; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_KATANA = 16032; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD = 16033; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_DIRE_MACE = 16034; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE = 16035; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD = 16036; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_DWAXE = 16037; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_WHIP = 16038; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_MINDBLADE = 16039; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW = 594; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_TRIDENT = 582; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_LANCE = 15903; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_PICK = 15904; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_PICK = 15905; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SAI = 15906; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_NUNCHAKU = 15907; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_FALCHION = 15908; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_SAP = 15909; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_KATAR = 15910; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_MACE = 15911; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_MAUL = 15912; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_DBL_SCIMITAR = 15913; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_GOAD = 15914; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE = 15977; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE = 15985; +const int IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE = 15993; + +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_CLUB = 16050; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_DAGGER = 16051; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_DART = 16052; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYCROSSBOW = 16053; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTCROSSBOW = 16054; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTMACE = 16055; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_MORNINGSTAR = 16056; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_QUARTERSTAFF = 16057; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSPEAR = 16058; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SICKLE = 16059; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SLING = 16060; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_UNARMED = 16061; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LONGBOW = 16062; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SHORTBOW = 16063; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SHORTSWORD = 16064; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_RAPIER = 16065; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SCIMITAR = 16066; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LONGSWORD = 16067; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_GREATSWORD = 16068; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_HANDAXE = 16069; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_THROWINGAXE = 16070; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_BATTLEAXE = 16071; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_GREATAXE = 16072; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_HALBERD = 16073; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTHAMMER = 16074; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHTFLAIL = 16075; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_WARHAMMER = 16076; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_HEAVYFLAIL = 16077; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_KAMA = 16078; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_KUKRI = 16079; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SHURIKEN = 16080; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SCYTHE = 16081; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_KATANA = 16082; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_BASTARDSWORD = 16083; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_DIREMACE = 16084; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_DOUBLEAXE = 16085; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_TWOBLADEDSWORD = 16086; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_DWAXE = 16087; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_WHIP = 16088; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_MINDBLADE = 16089; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW = 594; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_TRIDENT = 584; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_LANCE = 15951; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_PICK = 15952; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_PICK = 15953; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SAI = 15954; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_NUNCHAKU = 15955; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_FALCHION = 15956; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_SAP = 15957; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_KATAR = 15958; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_HEAVY_MACE = 15959; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_MAUL = 15960; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_DBL_SCIMITAR = 15961; +const int IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_GOAD = 15962; +const int IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE = 15981; +const int IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_THINBLADE = 15989; +const int IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_COURTBLADE = 15997; + +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_CLUB = 16100; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_DAGGER = 16101; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_DART = 16102; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_HEAVY_CROSSBOW = 16103; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_CROSSBOW = 16104; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_MACE = 16105; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_MORNING_STAR = 16106; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_STAFF = 16107; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SPEAR = 16108; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SICKLE = 16109; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SLING = 16110; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_UNARMED_STRIKE = 16111; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LONGBOW = 16112; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SHORTBOW = 16113; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SHORT_SWORD = 16114; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_RAPIER = 16115; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SCIMITAR = 16116; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LONG_SWORD = 16117; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_GREAT_SWORD = 16118; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_HAND_AXE = 16119; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_THROWING_AXE = 16120; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_BATTLE_AXE = 16121; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_GREAT_AXE = 16122; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_HALBERD = 16123; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_HAMMER = 16124; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_FLAIL = 16125; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_WAR_HAMMER = 16126; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_HEAVY_FLAIL = 16127; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_KAMA = 16128; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_KUKRI = 16129; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SHURIKEN = 16130; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SCYTHE = 16131; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_KATANA = 16132; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_BASTARD_SWORD = 16133; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_DIRE_MACE = 16134; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_DOUBLE_AXE = 16135; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_TWO_BLADED_SWORD = 16136; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_DWAXE = 16137; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_WHIP = 16138; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_MINDBLADE = 16139; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_RAY = 16140; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_LANCE = 15891; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_HEAVY_PICK = 15892; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_PICK = 15893; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SAI = 15894; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_NUNCHAKU = 15895; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_FALCHION = 15896; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_SAP = 15897; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_KATAR = 15898; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_HEAVY_MACE = 15899; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_MAUL = 15900; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_DBL_SCIMITAR = 15901; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_GOAD = 15902; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE = 15976; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE = 15984; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE = 15992; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW = 588; +const int IP_CONST_FEAT_EPIC_WEAPON_FOCUS_TRIDENT = 585; + +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB = 16150; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_DAGGER = 16151; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_DART = 16152; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW = 16153; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW = 16154; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_MACE = 16155; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_MORNING_STAR = 16156; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_STAFF = 16157; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SPEAR = 16158; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SICKLE = 16159; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SLING = 16160; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_UNARMED_STRIKE = 16161; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LONGBOW = 16162; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SHORTBOW = 16163; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SHORT_SWORD = 16164; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_RAPIER = 16165; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SCIMITAR = 16166; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LONG_SWORD = 16167; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_GREAT_SWORD = 16168; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_HAND_AXE = 16169; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_THROWING_AXE = 16170; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_BATTLE_AXE = 16171; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_GREAT_AXE = 16172; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_HALBERD = 16173; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_HAMMER = 16174; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_FLAIL = 16175; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_WAR_HAMMER = 16176; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_FLAIL = 16177; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_KAMA = 16178; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_KUKRI = 16179; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SHURIKEN = 16180; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SCYTHE = 16181; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_KATANA = 16182; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_BASTARD_SWORD = 16183; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_DIRE_MACE = 16184; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_DOUBLE_AXE = 16185; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD = 16186; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_DWAXE = 16187; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_WHIP = 16188; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_MINDBLADE = 16189; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW = 591; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_TRIDENT = 586; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_LANCE = 15915; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_PICK = 15916; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_PICK = 15917; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SAI = 15918; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_NUNCHAKU = 15919; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_FALCHION = 15920; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_SAP = 15921; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_KATAR = 15922; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_HEAVY_MACE = 15923; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_MAUL = 15924; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_DBL_SCIMITAR = 15925; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_GOAD = 15926; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE = 15978; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE = 15986; +const int IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE = 15994; + +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB = 16200; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_DAGGER = 16201; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_DART = 16202; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYCROSSBOW = 16203; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTCROSSBOW = 16204; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTMACE = 16205; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_MORNINGSTAR = 16206; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_QUARTERSTAFF = 16207; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSPEAR = 16208; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SICKLE = 16209; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SLING = 16210; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_UNARMED = 16211; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LONGBOW = 16212; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTBOW = 16213; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SHORTSWORD = 16214; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_RAPIER = 16215; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SCIMITAR = 16216; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LONGSWORD = 16217; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_GREATSWORD = 16218; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_HANDAXE = 16219; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_THROWINGAXE = 16220; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_BATTLEAXE = 16221; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_GREATAXE = 16222; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_HALBERD = 16223; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTHAMMER = 16224; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHTFLAIL = 16225; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_WARHAMMER = 16226; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVYFLAIL = 16227; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_KAMA = 16228; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_KUKRI = 16229; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SHURIKEN = 16230; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SCYTHE = 16231; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_KATANA = 16232; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_BASTARDSWORD = 16233; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_DIREMACE = 16234; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_DOUBLEAXE = 16235; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_TWOBLADEDSWORD = 16236; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_DWAXE = 16237; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_WHIP = 16238; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_MINDBLADE = 16239; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW = 594; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_TRIDENT = 587; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_LANCE = 15939; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_PICK = 15940; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_PICK = 15941; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SAI = 15942; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_NUNCHAKU = 15943; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_FALCHION = 15944; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_SAP = 15945; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_KATAR = 15946; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_HEAVY_MACE = 15947; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_MAUL = 15948; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_DBL_SCIMITAR = 15949; +const int IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_GOAD = 15950; +const int IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE = 15980; +const int IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_THINBLADE = 15988; +const int IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE = 15996; + +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SICKLE = 16250; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_KAMA = 16251; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_KUKRI = 16252; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_CLUB = 16253; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_DAGGER = 16254; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_LIGHTMACE = 16255; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_MORNINGSTAR = 16256; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_QUARTERSTAFF = 16257; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SHORTSPEAR = 16258; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SHORTSWORD = 16259; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_RAPIER = 16260; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SCIMITAR = 16261; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_LONGSWORD = 16262; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_GREATSWORD = 16263; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_HANDAXE = 16264; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_BATTLEAXE = 16265; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_GREATAXE = 16266; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_HALBERD = 16267; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_LIGHTHAMMER = 16268; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_LIGHTFLAIL = 16269; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_WARHAMMER = 16270; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_HEAVYFLAIL = 16271; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SCYTHE = 16272; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_KATANA = 16273; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_BASTARDSWORD = 16274; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_DIREMACE = 16275; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_DOUBLEAXE = 16276; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_TWOBLADEDSWORD = 16277; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_DWAXE = 16278; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_WHIP = 16279; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_MINDBLADE = 16280; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_TRIDENT = 595; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW = 596; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_LIGHT_LANCE = 15963; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_HEAVY_PICK = 15964; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_LIGHT_PICK = 15965; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SAI = 15966; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_NUNCHAKU = 15967; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_FALCHION = 15968; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_SAP = 15969; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_KATAR = 15970; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_HEAVY_MACE = 15971; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_MAUL = 15972; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_DBL_SCIMITAR = 15973; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_GOAD = 15974; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE = 15982; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE = 15990; +const int IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE = 15998; + +const int IP_CONST_FEAT_SUPERIOR_WEAPON_FOCUS = 24811; +const int IP_CONST_FEAT_EPIC_SUPERIOR_WEAPON_FOCUS = 24812; + +// Arcane Archer +const int IP_CONST_FEAT_FEAT_PRESTIGE_IMBUE_ARROW = 390; + +const int IP_CONST_FEAT_DIEHARD = 96; + +//undead feats +const int IP_CONST_FEAT_UNDEAD_HD = 213; +const int IP_CONST_FEAT_IMMUNITY_ABILITY_DECREASE = 214; +const int IP_CONST_FEAT_IMMUNITY_CRITICAL = 215; +const int IP_CONST_FEAT_IMMUNITY_DEATH = 216; +const int IP_CONST_FEAT_IMMUNITY_DISEASE = 217; +const int IP_CONST_FEAT_IMMUNITY_MIND_SPELLS = 218; +const int IP_CONST_FEAT_IMMUNITY_PARALYSIS = 219; +const int IP_CONST_FEAT_IMMUNITY_POISON = 220; +const int IP_CONST_FEAT_IMMUNITY_SNEAKATTACK = 221; + + +// IP_CONST_CASTSPELL + +const int IP_CONST_CASTSPELL_BLESSWEAPON_10 = 13; +const int IP_CONST_CASTSPELL_MAGICWEAPON_2 = 479; +const int IP_CONST_CASTSPELL_GREATERMAGICWEAPON_10 = 511; +const int IP_CONST_CASTSPELL_GREATERMAGICWEAPON_11 = 512; +const int IP_CONST_CASTSPELL_HOLY_SWORD_15 = 473; + +const int IP_CONST_CASTSPELL_GRENADE_POISONVIAL = 1140; +const int IP_CONST_CASTSPELL_POISON_WEAPON = 1141; +const int IP_CONST_CASTSPELL_POISON_ITEM = 1142; +const int IP_CONST_CASTSPELL_POISON_FOOD = 1143; +const int IP_CONST_CASTSPELL_CLEAN_POISON_OFF = 1144; + +const int IP_CONST_CASTSPELL_MINDBLADE_LUCKY = 1171; + + + +// IP_CONST_ONHIT_CASTSPELL +// Note: Do not add anything to this section, since multiple onhit properties conflict with each other. Instead implement your stuff via eventhook to EVENT_ONHIT. +const int IP_CONST_ONHIT_CASTSPELL_RAVAGEGOLDENICE = 200; +const int IP_CONST_ONHIT_CASTSPELL_NIGHTSHADEPOISON = 201; +const int IP_CONST_ONHIT_CASTSPELL_LINGDMG = 202; +const int IP_CONST_CASTSPELL_ELDRITCH_GLAIVE_ONHIT = 205; + +//:: DAMAGE_BONUS_ [iprp_damagecost.2da] +const int DAMAGE_BONUS_21 = 73; +const int DAMAGE_BONUS_22 = 74; +const int DAMAGE_BONUS_23 = 75; +const int DAMAGE_BONUS_24 = 76; +const int DAMAGE_BONUS_25 = 77; +const int DAMAGE_BONUS_26 = 78; +const int DAMAGE_BONUS_27 = 79; +const int DAMAGE_BONUS_28 = 80; +const int DAMAGE_BONUS_29 = 81; +const int DAMAGE_BONUS_30 = 82; +const int DAMAGE_BONUS_31 = 83; +const int DAMAGE_BONUS_32 = 84; +const int DAMAGE_BONUS_33 = 85; +const int DAMAGE_BONUS_34 = 86; +const int DAMAGE_BONUS_35 = 87; +const int DAMAGE_BONUS_36 = 88; +const int DAMAGE_BONUS_37 = 89; +const int DAMAGE_BONUS_38 = 90; +const int DAMAGE_BONUS_39 = 91; +const int DAMAGE_BONUS_40 = 92; +const int DAMAGE_BONUS_41 = 93; +const int DAMAGE_BONUS_42 = 94; +const int DAMAGE_BONUS_43 = 95; +const int DAMAGE_BONUS_44 = 96; +const int DAMAGE_BONUS_45 = 97; +const int DAMAGE_BONUS_46 = 98; +const int DAMAGE_BONUS_47 = 99; +const int DAMAGE_BONUS_48 = 100; +const int DAMAGE_BONUS_49 = 101; +const int DAMAGE_BONUS_50 = 102; + +//:: IP_CONST_DAMAGEBONUS [iprp_damagecost.2da] +const int IP_CONST_DAMAGEBONUS_11 = 21; +const int IP_CONST_DAMAGEBONUS_12 = 22; +const int IP_CONST_DAMAGEBONUS_13 = 23; +const int IP_CONST_DAMAGEBONUS_14 = 24; +const int IP_CONST_DAMAGEBONUS_15 = 25; +const int IP_CONST_DAMAGEBONUS_16 = 26; +const int IP_CONST_DAMAGEBONUS_17 = 27; +const int IP_CONST_DAMAGEBONUS_18 = 28; +const int IP_CONST_DAMAGEBONUS_19 = 29; +const int IP_CONST_DAMAGEBONUS_20 = 30; +const int IP_CONST_DAMAGEBONUS_3d6 = 31; +const int IP_CONST_DAMAGEBONUS_4d6 = 32; +const int IP_CONST_DAMAGEBONUS_5d6 = 33; +const int IP_CONST_DAMAGEBONUS_6d6 = 34; +const int IP_CONST_DAMAGEBONUS_7d6 = 35; +const int IP_CONST_DAMAGEBONUS_8d6 = 36; +const int IP_CONST_DAMAGEBONUS_3d4 = 37; +const int IP_CONST_DAMAGEBONUS_4d4 = 38; +const int IP_CONST_DAMAGEBONUS_5d4 = 39; +const int IP_CONST_DAMAGEBONUS_6d4 = 40; +const int IP_CONST_DAMAGEBONUS_3d8 = 41; +const int IP_CONST_DAMAGEBONUS_4d8 = 42; +const int IP_CONST_DAMAGEBONUS_5d8 = 43; +const int IP_CONST_DAMAGEBONUS_6d8 = 44; +const int IP_CONST_DAMAGEBONUS_7d8 = 45; +const int IP_CONST_DAMAGEBONUS_8d8 = 46; +const int IP_CONST_DAMAGEBONUS_9d8 = 47; +const int IP_CONST_DAMAGEBONUS_10d8 = 48; +const int IP_CONST_DAMAGEBONUS_7d4 = 49; +const int IP_CONST_DAMAGEBONUS_8d4 = 50; +const int IP_CONST_DAMAGEBONUS_9d4 = 51; +const int IP_CONST_DAMAGEBONUS_10d4 = 52; +const int IP_CONST_DAMAGEBONUS_9d6 = 53; +const int IP_CONST_DAMAGEBONUS_10d6 = 54; +const int IP_CONST_DAMAGEBONUS_3d10 = 55; +const int IP_CONST_DAMAGEBONUS_4d10 = 56; +const int IP_CONST_DAMAGEBONUS_5d10 = 57; +const int IP_CONST_DAMAGEBONUS_6d10 = 58; +const int IP_CONST_DAMAGEBONUS_7d10 = 59; +const int IP_CONST_DAMAGEBONUS_8d10 = 60; +const int IP_CONST_DAMAGEBONUS_9d10 = 61; +const int IP_CONST_DAMAGEBONUS_10d10 = 62; +const int IP_CONST_DAMAGEBONUS_3d12 = 63; +const int IP_CONST_DAMAGEBONUS_4d12 = 64; +const int IP_CONST_DAMAGEBONUS_5d12 = 65; +const int IP_CONST_DAMAGEBONUS_6d12 = 66; +const int IP_CONST_DAMAGEBONUS_7d12 = 67; +const int IP_CONST_DAMAGEBONUS_8d12 = 68; +const int IP_CONST_DAMAGEBONUS_9d12 = 69; +const int IP_CONST_DAMAGEBONUS_10d12 = 70; +const int IP_CONST_DAMAGEBONUS_1d3 = 71; +const int IP_CONST_DAMAGEBONUS_4d3 = 72; +const int IP_CONST_DAMAGEBONUS_21 = 73; +const int IP_CONST_DAMAGEBONUS_22 = 74; +const int IP_CONST_DAMAGEBONUS_23 = 75; +const int IP_CONST_DAMAGEBONUS_24 = 76; +const int IP_CONST_DAMAGEBONUS_25 = 77; +const int IP_CONST_DAMAGEBONUS_26 = 78; +const int IP_CONST_DAMAGEBONUS_27 = 79; +const int IP_CONST_DAMAGEBONUS_28 = 80; +const int IP_CONST_DAMAGEBONUS_29 = 81; +const int IP_CONST_DAMAGEBONUS_30 = 82; +const int IP_CONST_DAMAGEBONUS_31 = 83; +const int IP_CONST_DAMAGEBONUS_32 = 84; +const int IP_CONST_DAMAGEBONUS_33 = 85; +const int IP_CONST_DAMAGEBONUS_34 = 86; +const int IP_CONST_DAMAGEBONUS_35 = 87; +const int IP_CONST_DAMAGEBONUS_36 = 88; +const int IP_CONST_DAMAGEBONUS_37 = 89; +const int IP_CONST_DAMAGEBONUS_38 = 90; +const int IP_CONST_DAMAGEBONUS_39 = 91; +const int IP_CONST_DAMAGEBONUS_40 = 92; +const int IP_CONST_DAMAGEBONUS_41 = 93; +const int IP_CONST_DAMAGEBONUS_42 = 94; +const int IP_CONST_DAMAGEBONUS_43 = 95; +const int IP_CONST_DAMAGEBONUS_44 = 96; +const int IP_CONST_DAMAGEBONUS_45 = 97; +const int IP_CONST_DAMAGEBONUS_46 = 98; +const int IP_CONST_DAMAGEBONUS_47 = 99; +const int IP_CONST_DAMAGEBONUS_48 = 100; +const int IP_CONST_DAMAGEBONUS_49 = 101; +const int IP_CONST_DAMAGEBONUS_50 = 102; + + +// IP_CONST_IMMUNITYSPELL +const int IP_CONST_IMMUNITYSPELL_BOLT_WEB = 239; +const int IP_CONST_IMMUNITYSPELL_SHADOW_WEB = 240; +const int IP_CONST_IMMUNITYSPELL_BELETITH_WEB = 241; +const int IP_CONST_IMMUNITYSPELL_NS_WEB = 242; + + +// IP_CONST_ONHIT_SAVEDC [iprp_onhitcost.2da] +const int IP_CONST_ONHIT_SAVEDC_10 = 10; +const int IP_CONST_ONHIT_SAVEDC_11 = 11; +const int IP_CONST_ONHIT_SAVEDC_12 = 12; +const int IP_CONST_ONHIT_SAVEDC_13 = 13; +const int IP_CONST_ONHIT_SAVEDC_15 = 15; +const int IP_CONST_ONHIT_SAVEDC_17 = 17; +const int IP_CONST_ONHIT_SAVEDC_19 = 19; +const int IP_CONST_ONHIT_SAVEDC_21 = 21; +const int IP_CONST_ONHIT_SAVEDC_23 = 23; +const int IP_CONST_ONHIT_SAVEDC_25 = 25; +const int IP_CONST_ONHIT_SAVEDC_27 = 27; +const int IP_CONST_ONHIT_SAVEDC_28 = 28; +const int IP_CONST_ONHIT_SAVEDC_29 = 29; +const int IP_CONST_ONHIT_SAVEDC_30 = 30; +const int IP_CONST_ONHIT_SAVEDC_31 = 31; +const int IP_CONST_ONHIT_SAVEDC_32 = 32; +const int IP_CONST_ONHIT_SAVEDC_33 = 33; +const int IP_CONST_ONHIT_SAVEDC_34 = 34; +const int IP_CONST_ONHIT_SAVEDC_35 = 35; +const int IP_CONST_ONHIT_SAVEDC_36 = 36; +const int IP_CONST_ONHIT_SAVEDC_37 = 37; +const int IP_CONST_ONHIT_SAVEDC_38 = 38; +const int IP_CONST_ONHIT_SAVEDC_39 = 39; +const int IP_CONST_ONHIT_SAVEDC_40 = 40; +const int IP_CONST_ONHIT_SAVEDC_41 = 41; +const int IP_CONST_ONHIT_SAVEDC_42 = 42; +const int IP_CONST_ONHIT_SAVEDC_43 = 43; +const int IP_CONST_ONHIT_SAVEDC_44 = 44; +const int IP_CONST_ONHIT_SAVEDC_45 = 45; +const int IP_CONST_ONHIT_SAVEDC_46 = 46; +const int IP_CONST_ONHIT_SAVEDC_47 = 47; +const int IP_CONST_ONHIT_SAVEDC_48 = 48; +const int IP_CONST_ONHIT_SAVEDC_49 = 49; +const int IP_CONST_ONHIT_SAVEDC_50 = 50; +const int IP_CONST_ONHIT_SAVEDC_51 = 51; +const int IP_CONST_ONHIT_SAVEDC_52 = 52; +const int IP_CONST_ONHIT_SAVEDC_53 = 53; +const int IP_CONST_ONHIT_SAVEDC_54 = 54; +const int IP_CONST_ONHIT_SAVEDC_55 = 55; +const int IP_CONST_ONHIT_SAVEDC_56 = 56; +const int IP_CONST_ONHIT_SAVEDC_57 = 57; +const int IP_CONST_ONHIT_SAVEDC_58 = 58; +const int IP_CONST_ONHIT_SAVEDC_59 = 59; +const int IP_CONST_ONHIT_SAVEDC_60 = 60; +const int IP_CONST_ONHIT_SAVEDC_61 = 61; +const int IP_CONST_ONHIT_SAVEDC_62 = 62; +const int IP_CONST_ONHIT_SAVEDC_63 = 63; +const int IP_CONST_ONHIT_SAVEDC_64 = 64; +const int IP_CONST_ONHIT_SAVEDC_65 = 65; +const int IP_CONST_ONHIT_SAVEDC_66 = 66; +const int IP_CONST_ONHIT_SAVEDC_67 = 67; +const int IP_CONST_ONHIT_SAVEDC_68 = 68; +const int IP_CONST_ONHIT_SAVEDC_69 = 69; +const int IP_CONST_ONHIT_SAVEDC_70 = 70; + + +// Creature Epic Criticals +const int IP_CONST_FEAT_DEVCRITICAL_CREATURE = 271; +const int IP_CONST_FEAT_OVERCRITICAL_CREATURE = 272; +//const int IP_CONST_FEAT_WEAPON_PROF_CREATURE = 38; // provided by Bioware in 1.66 + +// Epic DR +const int IP_CONST_FEAT_EPIC_DR_3 = 273; +const int IP_CONST_FEAT_EPIC_DR_6 = 274; +const int IP_CONST_FEAT_EPIC_DR_9 = 275; + +//IP_CONST_DAMAGETYPE +//const int IP_CONST_DAMAGETYPE_BLOOD = 15; //:: Not a 3e PnP damage type +const int IP_CONST_DAMAGETYPE_POISON = 16; +//const int IP_CONST_DAMAGETYPE_SHADOW = 17; //:: Not a 3e PnP damage type +const int IP_CONST_DAMAGETYPE_PSYCHIC = 18; +//const int IP_CONST_DAMAGETYPE_NECROTIC = 19; //:: This is from 4e D&D +const int IP_CONST_DAMAGETYPE_RADIANT = 20; +const int IP_CONST_DAMAGETYPE_FORCE = 21; +const int IP_CONST_DAMAGETYPE_UNTYPED = 22; +const int IP_CONST_DAMAGETYPE_VILE = 23; + +//IP_CONST_DAMAGESOAK +const int IP_CONST_DAMAGESOAK_1_HP = 11; +const int IP_CONST_DAMAGESOAK_2_HP = 12; +const int IP_CONST_DAMAGESOAK_3_HP = 13; +const int IP_CONST_DAMAGESOAK_4_HP = 14; +const int IP_CONST_DAMAGESOAK_6_HP = 15; +const int IP_CONST_DAMAGESOAK_7_HP = 16; +const int IP_CONST_DAMAGESOAK_8_HP = 17; +const int IP_CONST_DAMAGESOAK_9_HP = 18; +const int IP_CONST_DAMAGESOAK_11_HP = 19; +const int IP_CONST_DAMAGESOAK_12_HP = 20; +const int IP_CONST_DAMAGESOAK_13_HP = 21; +const int IP_CONST_DAMAGESOAK_14_HP = 22; +const int IP_CONST_DAMAGESOAK_16_HP = 23; +const int IP_CONST_DAMAGESOAK_17_HP = 24; +const int IP_CONST_DAMAGESOAK_18_HP = 25; +const int IP_CONST_DAMAGESOAK_19_HP = 26; +const int IP_CONST_DAMAGESOAK_21_HP = 27; +const int IP_CONST_DAMAGESOAK_22_HP = 28; +const int IP_CONST_DAMAGESOAK_23_HP = 29; +const int IP_CONST_DAMAGESOAK_24_HP = 30; +const int IP_CONST_DAMAGESOAK_26_HP = 31; +const int IP_CONST_DAMAGESOAK_27_HP = 32; +const int IP_CONST_DAMAGESOAK_28_HP = 33; +const int IP_CONST_DAMAGESOAK_29_HP = 34; +const int IP_CONST_DAMAGESOAK_31_HP = 35; +const int IP_CONST_DAMAGESOAK_32_HP = 36; +const int IP_CONST_DAMAGESOAK_33_HP = 37; +const int IP_CONST_DAMAGESOAK_34_HP = 38; +const int IP_CONST_DAMAGESOAK_36_HP = 39; +const int IP_CONST_DAMAGESOAK_37_HP = 40; +const int IP_CONST_DAMAGESOAK_38_HP = 41; +const int IP_CONST_DAMAGESOAK_39_HP = 42; +const int IP_CONST_DAMAGESOAK_41_HP = 43; +const int IP_CONST_DAMAGESOAK_42_HP = 44; +const int IP_CONST_DAMAGESOAK_43_HP = 45; +const int IP_CONST_DAMAGESOAK_44_HP = 46; +const int IP_CONST_DAMAGESOAK_46_HP = 47; +const int IP_CONST_DAMAGESOAK_47_HP = 48; +const int IP_CONST_DAMAGESOAK_48_HP = 49; +const int IP_CONST_DAMAGESOAK_49_HP = 50; + +//IP_CONST_DAMAGERESIST +const int IP_CONST_DAMAGERESIST_500 = 20; // immunity alternative +const int IP_CONST_DAMAGERESIST_1 = 21; +const int IP_CONST_DAMAGERESIST_2 = 22; +const int IP_CONST_DAMAGERESIST_3 = 23; +const int IP_CONST_DAMAGERESIST_4 = 24; +const int IP_CONST_DAMAGERESIST_6 = 25; +const int IP_CONST_DAMAGERESIST_7 = 26; +const int IP_CONST_DAMAGERESIST_8 = 27; +const int IP_CONST_DAMAGERESIST_9 = 28; + +const int IP_CONST_FEAT_SHADOWMASTER_SHADES = 141; + +//IP_CONST_REDUCEDWEIGHT +const int IP_CONST_REDUCEDWEIGHT_50_PERCENT = 6; + +// IP_CONST_MONSTERDAMAGE +const int IP_CONST_MONSTERDAMAGE_7d4 = 58; + +//ACP feats +const int IP_CONST_ACP_QUICK_FEAT = 98; +const int IP_CONST_ACP_HEAVY_FEAT = 582; +const int IP_CONST_ACP_UNARMED_FEAT = 583; +//const int IP_CONST_ACP_FEAT = 98; + +// Improved Combat Casting +const int IP_CONST_IMP_CC = 97; + +// Cast Bonus Domain Radial Feats +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_ONE = 142; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_TWO = 143; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_THREE = 144; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_FOUR = 145; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_FIVE = 146; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_SIX = 147; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_SEVEN = 148; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_EIGHT = 149; +const int IP_CONST_FEAT_CAST_DOMAIN_LEVEL_NINE = 150; +const int IP_CONST_FEAT_CHECK_DOMAIN_SLOTS = 151; + +// Bonus Domain Power Feats +const int IP_CONST_FEAT_WAR_DOMAIN = 152; +const int IP_CONST_FEAT_STRENGTH_DOMAIN = 153; +const int IP_CONST_FEAT_PROTECTION_DOMAIN = 154; +const int IP_CONST_FEAT_LUCK_DOMAIN = 155; +const int IP_CONST_FEAT_DEATH_DOMAIN = 156; +const int IP_CONST_FEAT_AIR_DOMAIN = 157; +const int IP_CONST_FEAT_ANIMAL_DOMAIN = 158; +const int IP_CONST_FEAT_DESTRUCTION_DOMAIN = 159; +const int IP_CONST_FEAT_EARTH_DOMAIN = 160; +const int IP_CONST_FEAT_EVIL_DOMAIN = 161; +const int IP_CONST_FEAT_FIRE_DOMAIN = 162; +const int IP_CONST_FEAT_GOOD_DOMAIN = 163; +const int IP_CONST_FEAT_HEALING_DOMAIN = 164; +const int IP_CONST_FEAT_KNOWLEDGE_DOMAIN = 165; +const int IP_CONST_FEAT_MAGIC_DOMAIN = 166; +const int IP_CONST_FEAT_PLANT_DOMAIN = 167; +const int IP_CONST_FEAT_SUN_DOMAIN = 168; +const int IP_CONST_FEAT_TRAVEL_DOMAIN = 169; +const int IP_CONST_FEAT_TRICKERY_DOMAIN = 170; +const int IP_CONST_FEAT_WATER_DOMAIN = 171; +const int IP_CONST_FEAT_DARKNESS_DOMAIN = 172; +const int IP_CONST_FEAT_METAL_DOMAIN = 173; +const int IP_CONST_FEAT_STORM_DOMAIN = 174; +const int IP_CONST_FEAT_PORTAL_DOMAIN = 175; +const int IP_CONST_FEAT_DWARF_DOMAIN = 176; +const int IP_CONST_FEAT_ORC_DOMAIN = 177; +const int IP_CONST_FEAT_FORCE_DOMAIN = 178; +const int IP_CONST_FEAT_SLIME_DOMAIN = 179; +const int IP_CONST_FEAT_TIME_DOMAIN = 180; +const int IP_CONST_FEAT_CHARM_DOMAIN = 181; +const int IP_CONST_FEAT_SPELLS_DOMAIN = 182; +const int IP_CONST_FEAT_RUNE_DOMAIN = 183; +const int IP_CONST_FEAT_FATE_DOMAIN = 184; +const int IP_CONST_FEAT_DOMINATION_DOMAIN = 185; +const int IP_CONST_FEAT_UNDEATH_DOMAIN = 186; +const int IP_CONST_FEAT_FAMILY_DOMAIN = 187; +const int IP_CONST_FEAT_HALFLING_DOMAIN = 188; +const int IP_CONST_FEAT_ILLUSION_DOMAIN = 189; +const int IP_CONST_FEAT_HATRED_DOMAIN = 190; +const int IP_CONST_FEAT_NOBILITY_DOMAIN = 191; +const int IP_CONST_FEAT_RETRIBUTION_DOMAIN = 192; +const int IP_CONST_FEAT_SCALEYKIND_DOMAIN = 193; +const int IP_CONST_FEAT_GNOME_DOMAIN = 194; +const int IP_CONST_FEAT_ELF_DOMAIN = 195; +const int IP_CONST_FEAT_RENEWAL_DOMAIN = 196; +const int IP_CONST_FEAT_SPIDER_DOMAIN = 197; +const int IP_CONST_FEAT_TYRANNY_DOMAIN = 198; +const int IP_CONST_FEAT_OCEAN_DOMAIN = 199; +const int IP_CONST_FEAT_BLIGHTBRINGER = 200; +const int IP_CONST_FEAT_DRAGON_DOMAIN = 397; +const int IP_CONST_FEAT_COLD_DOMAIN = 230; +const int IP_CONST_FEAT_WINTER_DOMAIN = 232; + +// Lasher +const int IP_CONST_FEAT_IMPROVED_KNOCKDOWN = 264; +const int IP_CONST_FEAT_IMPROVED_DISARM = 265; + +// Spellfire +const int IP_CONST_FEAT_SPELLFIRE_INCREASE = 266; +const int IP_CONST_FEAT_SPELLFIRE_DECREASE = 267; +const int IP_CONST_FEAT_SPELLFIRE_QUICKSELECT = 268; + +//RotD & Dragon Magic +const int IP_CONST_FEAT_DRAGON = 396; +const int IP_CONST_FEAT_DRAGON_IMMUNE = 395; +const int IP_CONST_FEAT_TOUGHNESS = 394; +const int IP_CONST_FEAT_DRACONIC_GRACE_1_5 = 223; +const int IP_CONST_FEAT_DRACONIC_GRACE_6_9 = 224; +const int IP_CONST_FEAT_DRACONIC_BREATH_1_5 = 225; +const int IP_CONST_FEAT_DRACONIC_BREATH_6_9 = 226; + +// Status markers +const int IP_CONST_FEAT_INCORPOREAL = 227; +const int IP_CONST_FEAT_ETHEREAL = 228; + +//Racial feats +const int IP_CONST_FEAT_KEEN_SENSES = 86; +const int IP_CONST_FEAT_CRAFTGUILD = 4987; +const int IP_CONST_FEAT_TECHGUILD = 4988; +const int IP_CONST_FEAT_REMAIN_CONCIOUS = 269; +const int IP_CONST_FEAT_SHIFTER_BEASTHIDE = 4993; +const int IP_CONST_FEAT_SHIFTER_DREAMSIGHT = 4994; +const int IP_CONST_FEAT_SHIFTER_GOREBRUTE = 4995; +const int IP_CONST_FEAT_SHIFTER_LONGSTRIDE = 4996; +const int IP_CONST_FEAT_SHIFTER_LONGTOOTH = 4997; +const int IP_CONST_FEAT_SHIFTER_RAZORCLAW = 4998; +const int IP_CONST_FEAT_SHIFTER_WILDHUNT = 4999; +const int IP_CONST_FEAT_SHIFTER_WINTERHIDE = 4986; +const int IP_CONST_FEAT_TRACKLESS_STEP = 393; +const int IP_CONST_FEAT_MOUNTAIN_FOLK = 4989; +const int IP_CONST_FEAT_BAMBOO_FOLK = 4990; +const int IP_CONST_FEAT_RIVER_FOLK = 4991; +const int IP_CONST_FEAT_SEA_FOLK = 4992; +const int IP_CONST_FEAT_FAST_HEALING_1 = 4382; +const int IP_CONST_FEAT_TURNING_IMMUNITY = 4381; +const int IP_CONST_FEAT_IMMUNITY_TO_REBUKING = 4380; + +// Template Feats +const int IP_CONST_FEAT_TEMPLATE_CELESTIAL_SMITE_EVIL = 16301; +const int IP_CONST_FEAT_TEMPLATE_CELESTIAL_MARKER = 16302; +const int IP_CONST_FEAT_TEMPLATE_FIENDISH_SMITE_GOOD = 16303; +const int IP_CONST_FEAT_TEMPLATE_FIENDISH_MARKER = 16304; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_SMITE_EVIL = 16305; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_PROTECTION = 16306; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_BLESS = 16307; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_AID = 16308; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_DETECT = 16309; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_CURE_SERIOUS = 16310; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_NEUTRALIZE_POISON = 16311; +//const int IP_CONST_FEAT_TEMPLATE_FIENDISH_MARKER = 16312; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_REMOVE_DISEASE= 16313; +//const int IP_CONST_FEAT_TEMPLATE_FIENDISH_MARKER = 16314; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_HOLY_WORD = 16315; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_HOLY_AURA = 16316; +//const int IP_CONST_FEAT_TEMPLATE_FIENDISH_MARKER = 16317; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_MASS_CHARM = 16318; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_SUMMON_IX = 16319; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_RESURRECTION = 16320; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_DAYLIGHT = 16321; +const int IP_CONST_FEAT_TEMPLATE_HALF_CELESTIAL_MARKER = 16322; +const int IP_CONST_FEAT_TEMPLATE_NECROPOLITAN_MARKER = 16323; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_SMITE_GOOD = 16324; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_DARKNESS = 16325; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_DESECRATE = 16326; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_UNHOLY_BLIGHT = 16327; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_POISON = 16328; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_CONTAGION = 16329; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_BLASPHEMY = 16330; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_UNHOLY_AURA = 16331; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_UNHALLOW = 16332; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_HORRID_WILTING= 16333; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_SUMMON_IX = 16334; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_DESTRUCTION = 16335; +const int IP_CONST_FEAT_TEMPLATE_HALF_FIENDISH_MARKER = 16336; +const int IP_CONST_FEAT_TEMPLATE_LICH_FEAR_AURA = 16337; +const int IP_CONST_FEAT_TEMPLATE_LICH_PARALYZING_TOUCH = 16338; +const int IP_CONST_FEAT_TEMPLATE_LICH_APPEARANCE = 16339; +const int IP_CONST_FEAT_TEMPLATE_LICH_MARKER = 16340; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_ALTER_SELF = 16341; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_ASTRAL_PROJECTION = 16342; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_CREATE_GREATER_UNDEAD = 16343; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_CREATE_UNDEAD = 16344; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_DEATH_KNELL = 16345; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_ENERVATION = 16346; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_GREATER_DISPEL_MAGIC = 16347; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_HARM = 16348; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_I = 16349; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_II = 16350; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_III = 16351; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_IV = 16352; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_V = 16353; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_VI = 16354; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_VII = 16355; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_VIII = 16356; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_SUMMON_CREATURE_IX = 16357; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_TELEKINESIS = 16358; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_WEIRD = 16359; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_GREATER_PLANAR_ALLY = 16360; +const int IP_CONST_FEAT_TEMPLATE_DEMILICH_MARKER = 16361; +const int IP_CONST_FEAT_HDRAGON_BREATHWEAPON = 16362; +const int IP_CONST_FEAT_HD_CHROMATICBLACK_MARKER = 16363; +const int IP_CONST_FEAT_HD_CHROMATICBLUE_MARKER = 16364; +const int IP_CONST_FEAT_HD_CHROMATICGREEN_MARKER = 16365; +const int IP_CONST_FEAT_HD_CHROMATICRED_MARKER = 16366; +const int IP_CONST_FEAT_HD_CHROMATICWHITE_MARKER = 16367; +const int IP_CONST_FEAT_HD_GEMAMETHYST_MARKER = 16368; +const int IP_CONST_FEAT_HD_GEMCRYSTAL_MARKER = 16369; +const int IP_CONST_FEAT_HD_GEMEMERALD_MARKER = 16370; +const int IP_CONST_FEAT_HD_GEMSAPPHIRE_MARKER = 16371; +const int IP_CONST_FEAT_HD_GEMTOPAZ_MARKER = 16372; +const int IP_CONST_FEAT_HD_LUNGCHIANGLUNG_MARKER = 16373; +const int IP_CONST_FEAT_HD_LUNGLILUNG_MARKER = 16374; +const int IP_CONST_FEAT_HD_LUNGLUNGWANG_MARKER = 16375; +const int IP_CONST_FEAT_HD_LUNGPANLUNG_MARKER = 16376; +const int IP_CONST_FEAT_HD_LUNGSHENLUNG_MARKER = 16377; +const int IP_CONST_FEAT_HD_LUNGTIENLUNG_MARKER = 16378; +const int IP_CONST_FEAT_HD_LUNGTUNMILUNG_MARKER = 16379; +const int IP_CONST_FEAT_HD_METALLICBRASS_MARKER = 16380; +const int IP_CONST_FEAT_HD_METALLICBRONZE_MARKER = 16381; +const int IP_CONST_FEAT_HD_METALLICCOPPER_MARKER = 16382; +const int IP_CONST_FEAT_HD_METALLICGOLD_MARKER = 16383; +const int IP_CONST_FEAT_HD_METALLICSILVER_MARKER = 16384; +const int IP_CONST_FEAT_HD_OBSCUREBATTLE_MARKER = 16385; +const int IP_CONST_FEAT_HD_OBSCUREBROWN_MARKER = 16386; +const int IP_CONST_FEAT_HD_OBSCURECHAOS_MARKER = 16387; +const int IP_CONST_FEAT_HD_OBSCUREDEEP_MARKER = 16388; +const int IP_CONST_FEAT_HD_OBSCUREETHEREAL_MARKER = 16389; +const int IP_CONST_FEAT_HD_OBSCUREFANG_MARKER = 16390; +const int IP_CONST_FEAT_HD_OBSCUREHOWLING_MARKER = 16391; +const int IP_CONST_FEAT_HD_OBSCUREOCEANUS_MARKER = 16392; +const int IP_CONST_FEAT_HD_OBSCUREPYROCLASTIC_MARKER = 16393; +const int IP_CONST_FEAT_HD_OBSCURERADIANT_MARKER = 16394; +const int IP_CONST_FEAT_HD_OBSCURERUST_MARKER = 16395; +const int IP_CONST_FEAT_HD_OBSCURESHADOW_MARKER = 16396; +const int IP_CONST_FEAT_HD_OBSCURESONG_MARKER = 16397; +const int IP_CONST_FEAT_HD_OBSCURESTYX_MARKER = 16398; +const int IP_CONST_FEAT_HD_OBSCURETARTERIAN_MARKER = 16399; +const int IP_CONST_FEAT_EARTH_STRIKE = 4379; +const int IP_CONST_FEAT_IMMUNITY_TO_SLEEP = 16400; +const int IP_CONST_FEAT_BLOODED_WAR_CRY = 573; +const int IP_CONST_FEAT_OUTSIDER_RACIAL_TYPE = 24819; +const int IP_CONST_FEAT_REGENERATION_5 = 24820; +const int IP_CONST_FEAT_SCENT = 24821; +const int IP_CONST_FEAT_GIANT_RACIAL_TYPE = 24822; + +const int IP_CONST_FEAT_TEMPLATE_ARCHLICH_MARKER = 16401; //:: Archlich +const int IP_CONST_FEAT_TEMPLATE_TURN_UNDEAD = 16402; +const int IP_CONST_FEAT_TEMPLATE_PROJECTION = 24823; +const int IP_CONST_FEAT_TEMPLATE_END_PROJECTION = 24824; +const int IP_CONST_FEAT_TEMPLATE_ANIMATE_DEAD = 24825; +const int IP_CONST_FEAT_TEMPLATE_SAINT_SLA_BLESS = 16403; //:: Saint +//const int IP_CONST_FEAT_TEMPLATE_SAINT_SLA_GUIDANCE = 16404; +const int IP_CONST_FEAT_TEMPLATE_SAINT_SLA_RESISTANCE = 16405; +const int IP_CONST_FEAT_TEMPLATE_SAINT_SLA_VIRTUE = 16406; +const int IP_CONST_FEAT_TEMPLATE_SAINT_PROTECTIVE_AURA = 16407; +const int IP_CONST_FEAT_TEMPLATE_SAINT_HOLY_POWER = 16408; + +// Tome of Battle +const int IP_CONST_FEAT_RISING_PHOENIX = 4395; +const int IP_CONST_FEAT_UNCANNY_DODGE2 = 389; +const int IP_CONST_FEAT_CRUSADER_SMITE = 580; + +//Alienist +const int IP_CONST_PHOBIA_ABERRATION = 24705; +const int IP_CONST_PHOBIA_ANIMAL = 24706; +const int IP_CONST_PHOBIA_BEAST = 24707; +const int IP_CONST_PHOBIA_CONSTRUCT = 24708; +const int IP_CONST_PHOBIA_DRAGON = 24709; +const int IP_CONST_PHOBIA_GOBLINOID = 24710; +const int IP_CONST_PHOBIA_MONSTROUS = 24711; +const int IP_CONST_PHOBIA_ORC = 24712; +const int IP_CONST_PHOBIA_REPTILIAN = 24713; +const int IP_CONST_PHOBIA_ELEMENTAL = 24714; +const int IP_CONST_PHOBIA_FEY = 24715; +const int IP_CONST_PHOBIA_GIANT = 24716; +const int IP_CONST_PHOBIA_MAGICAL_BEAST = 24717; +const int IP_CONST_PHOBIA_SHAPECHANGER = 24718; +const int IP_CONST_PHOBIA_UNDEAD = 24719; +const int IP_CONST_PHOBIA_VERMIN = 24720; + +//PnP Familiars +const int IP_CONST_PNP_FAMILIAR = 231; + +// Metamagic Feat Abilities +const int IP_CONST_FEAT_EXTEND_SPELL_ABILITY = 4692; +const int IP_CONST_FEAT_SILENT_SPELL_ABILITY = 4693; +const int IP_CONST_FEAT_STILL_SPELL_ABILITY = 4694; +const int IP_CONST_FEAT_EMPOWER_SPELL_ABILITY = 4695; +const int IP_CONST_FEAT_MAXIMIZE_SPELL_ABILITY = 4696; +const int IP_CONST_FEAT_QUICKEN_SPELL_ABILITY = 4697; +//for use with New Spellbook System +const int IP_CONST_JPM_AUTO_QUICKEN = 4687; +const int IP_CONST_NSB_IMP_COMBAT_CAST = 4688; +const int IP_CONST_NSB_AUTO_STILL = 4689; +const int IP_CONST_NSB_AUTO_QUICKEN = 4690; +const int IP_CONST_NSB_AUTO_SILENT = 4691; + +//:: Sanctify Martial Strike +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SICKLE = 24721; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_DWARVENAXE = 24722; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_CLUB = 24723; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_DAGGER = 24724; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTMACE = 24725; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_MORNINGSTAR = 24726; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_QUARTERSTAFF = 24727; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SHORTSPEAR = 24728; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SHORTSWORD = 24729; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_RAPIER = 24730; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SCIMITAR = 24731; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LONGSWORD = 24732; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_GREATSWORD = 24733; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_HANDAXE = 24734; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_BATTLEAXE = 24735; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_GREATAXE = 24736; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_HALBERD = 24737; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTHAMMER = 24738; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTFLAIL = 24739; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_WARHAMMER = 24740; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVYFLAIL = 24741; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SCYTHE = 24742; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_KATANA = 24743; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_BASTARDSWORD = 24744; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_DIREMACE = 24745; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_DOUBLEAXE = 24746; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_TWOBLADEDSWORD = 24747; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_KAMA = 24748; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_KUKRI = 24749; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVYCROSSBOW = 24750; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHTCROSSBOW = 24751; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SLING = 24752; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LONGBOW = 24753; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SHORTBOW = 24754; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SHURIKEN = 24755; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_DART = 24756; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_WHIP = 24757; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_TRIDENT = 24758; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_MINDBLADE = 24759; +//:: New Weapons +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW = 26550; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHT_LANCE = 26551; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVY_PICK = 26552; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_LIGHT_PICK = 26553; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SAI = 26554; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_NUNCHAKU = 26555; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_FALCHION = 26556; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_SAP = 26557; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_KATAR = 26558; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_HEAVY_MACE = 26559; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_MAUL = 26560; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_DBL_SCIMITAR = 26561; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_GOAD = 26562; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_LIGHTBLADE = 26563; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_THINBLADE = 26564; +const int IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE = 26565; + +//:: Vile Martial Strike +const int IP_CONST_FEAT_VILE_MARTIAL_CLUB = 24760; +const int IP_CONST_FEAT_VILE_MARTIAL_DAGGER = 24761; +const int IP_CONST_FEAT_VILE_MARTIAL_LIGHTMACE = 24762; +const int IP_CONST_FEAT_VILE_MARTIAL_MORNINGSTAR = 24763; +const int IP_CONST_FEAT_VILE_MARTIAL_QUARTERSTAFF = 24764; +const int IP_CONST_FEAT_VILE_MARTIAL_SHORTSPEAR = 24765; +const int IP_CONST_FEAT_VILE_MARTIAL_SHORTSWORD = 24766; +const int IP_CONST_FEAT_VILE_MARTIAL_RAPIER = 24767; +const int IP_CONST_FEAT_VILE_MARTIAL_SCIMITAR = 24768; +const int IP_CONST_FEAT_VILE_MARTIAL_LONGSWORD = 24769; +const int IP_CONST_FEAT_VILE_MARTIAL_GREATSWORD = 24770; +const int IP_CONST_FEAT_VILE_MARTIAL_HANDAXE = 24771; +const int IP_CONST_FEAT_VILE_MARTIAL_BATTLEAXE = 24772; +const int IP_CONST_FEAT_VILE_MARTIAL_GREATAXE = 24773; +const int IP_CONST_FEAT_VILE_MARTIAL_HALBERD = 24774; +const int IP_CONST_FEAT_VILE_MARTIAL_LIGHTHAMMER = 24775; +const int IP_CONST_FEAT_VILE_MARTIAL_LIGHTFLAIL = 24776; +const int IP_CONST_FEAT_VILE_MARTIAL_WARHAMMER = 24777; +const int IP_CONST_FEAT_VILE_MARTIAL_HEAVYFLAIL = 24778; +const int IP_CONST_FEAT_VILE_MARTIAL_SCYTHE = 24779; +const int IP_CONST_FEAT_VILE_MARTIAL_KATANA = 24780; +const int IP_CONST_FEAT_VILE_MARTIAL_BASTARDSWORD = 24781; +const int IP_CONST_FEAT_VILE_MARTIAL_DIREMACE = 24782; +const int IP_CONST_FEAT_VILE_MARTIAL_DOUBLEAXE = 24783; +const int IP_CONST_FEAT_VILE_MARTIAL_TWOBLADEDSWORD = 24784; +const int IP_CONST_FEAT_VILE_MARTIAL_KAMA = 24785; +const int IP_CONST_FEAT_VILE_MARTIAL_KUKRI = 24786; +const int IP_CONST_FEAT_VILE_MARTIAL_HEAVYCROSSBOW = 24787; +const int IP_CONST_FEAT_VILE_MARTIAL_LIGHTCROSSBOW = 24788; +const int IP_CONST_FEAT_VILE_MARTIAL_SLING = 24789; +const int IP_CONST_FEAT_VILE_MARTIAL_LONGBOW = 24790; +const int IP_CONST_FEAT_VILE_MARTIAL_SHORTBOW = 24791; +const int IP_CONST_FEAT_VILE_MARTIAL_SHURIKEN = 24792; +const int IP_CONST_FEAT_VILE_MARTIAL_DART = 24793; +const int IP_CONST_FEAT_VILE_MARTIAL_SICKLE = 24794; +const int IP_CONST_FEAT_VILE_MARTIAL_DWARVENAXE = 24795; +const int IP_CONST_FEAT_VILE_MARTIAL_WHIP = 24796; +const int IP_CONST_FEAT_VILE_MARTIAL_TRIDENT = 24797; +const int IP_CONST_FEAT_VILE_MARTIAL_MINDBLADE = 24798; + +//:: New Weapons +const int IP_CONST_FEAT_VILE_MARTIAL_EAGLE_CLAW = 26500; +const int IP_CONST_FEAT_VILE_MARTIAL_LIGHT_LANCE = 26501; +const int IP_CONST_FEAT_VILE_MARTIAL_HEAVY_PICK = 26502; +const int IP_CONST_FEAT_VILE_MARTIAL_LIGHT_PICK = 26503; +const int IP_CONST_FEAT_VILE_MARTIAL_SAI = 26504; +const int IP_CONST_FEAT_VILE_MARTIAL_NUNCHAKU = 26505; +const int IP_CONST_FEAT_VILE_MARTIAL_FALCHION = 26506; +const int IP_CONST_FEAT_VILE_MARTIAL_SAP = 26507; +const int IP_CONST_FEAT_VILE_MARTIAL_KATAR = 26508; +const int IP_CONST_FEAT_VILE_MARTIAL_HEAVY_MACE = 26509; +const int IP_CONST_FEAT_VILE_MARTIAL_MAUL = 26510; +const int IP_CONST_FEAT_VILE_MARTIAL_DBL_SCIMITAR = 26511; +const int IP_CONST_FEAT_VILE_MARTIAL_GOAD = 26512; +const int IP_CONST_FEAT_VILE_MARTIAL_ELVEN_LIGHTBLADE = 26513; +const int IP_CONST_FEAT_VILE_MARTIAL_ELVEN_THINBLADE = 26514; +const int IP_CONST_FEAT_VILE_MARTIAL_ELVEN_COURTBLADE = 26515; + + +//Epic Dodge, Epic Prowess +const int IP_CONST_FEAT_EPIC_DODGE = 24799; +const int IP_CONST_FEAT_EPIC_PROWESS = 24810; + +//Epic Toughness +const int IP_CONST_FEAT_EPIC_TOUGHNESS_1 = 24800; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_2 = 24801; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_3 = 24802; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_4 = 24803; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_5 = 24804; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_6 = 24805; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_7 = 24806; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_8 = 24807; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_9 = 24808; +const int IP_CONST_FEAT_EPIC_TOUGHNESS_10 = 24809; + +//Eye of Beholder +const int IP_FEAT_RAY_CHARM_PERSON = 4678; +const int IP_FEAT_RAY_CHARM_MONSTER = 4679; +const int IP_FEAT_RAY_SLEEP = 4680; +const int IP_FEAT_RAY_FLESH_TO_STONE = 4681; +const int IP_FEAT_RAY_DISINTEGRATE = 4682; +const int IP_FEAT_RAY_FEAR = 4683; +const int IP_FEAT_RAY_SLOW = 4684; +const int IP_FEAT_RAY_INFLICT_MODERATE_WOUNDS = 4685; +const int IP_FEAT_RAY_FINGER_OF_DEATH = 4686; + +//Hellfire Warlock +const int IP_FEAT_HELLFIRE_SPEAR = 4671; +const int IP_FEAT_HELLFIRE_GLAIVE = 4672; +const int IP_FEAT_HELLFIRE_BLOW = 4673; +const int IP_FEAT_HELLFIRE_CHAIN = 4674; +const int IP_FEAT_HELLFIRE_CONE = 4675; +const int IP_FEAT_HELLFIRE_LINE = 4676; +const int IP_FEAT_HELLFIRE_DOOM = 4677; + +// Player's Handbook +const int IP_CONST_FEAT_IMPROVED_GRAPPLE = 222; +const int IP_CONST_FEAT_IMPROVED_TRIP = 4394; +const int IP_CONST_FEAT_PRC_IMPROVED_DISARM = 4393; +const int IP_CONST_FEAT_GREAT_FORTITUDE = 202; +const int IP_CONST_FEAT_SCRIBE_SCROLL = 203; + +// Weapons of Legacy Feats +const int IP_CONST_FEAT_LEAST_LEGACY = 4390; +const int IP_CONST_FEAT_LESSER_LEGACY = 4391; +const int IP_CONST_FEAT_GREATER_LEGACY = 4392; + +// Sandstorm +const int IP_CONST_FEAT_IMP_HEAT_ENDURANCE = 4388; +const int IP_CONST_FEAT_HEAT_ENDURANCE = 4389; + +// Frostburn +const int IP_CONST_FEAT_IMP_COLD_ENDURANCE = 4386; +const int IP_CONST_FEAT_COLD_ENDURANCE = 4387; + +// Combat Maneuver +const int IP_CONST_FEAT_COMBAT_MOVE_1 = 4383; +const int IP_CONST_FEAT_COMBAT_MOVE_2 = 4384; +const int IP_CONST_FEAT_COMBAT_MOVE_3 = 4385; + + + +/*////////////////////////////////////////////////// +//////////////// SHADOWCASTING////////////////////// +//////////////////////////////////////////////////*/ + +const int IP_CONST_FEAT_MYST_FLICKER = 4538; +const int IP_CONST_FEAT_DARK_SOUL = 4537; +const int IP_CONST_FEAT_UMBRAL_FIST = 4367; + +const int IP_CONST_FEAT_PATH_FOCUS_CLOAK_SHADOWS = 4411; +const int IP_CONST_FEAT_PATH_FOCUS_DARK_TERRAIN = 4412; +const int IP_CONST_FEAT_PATH_FOCUS_EBON_WHISPERS = 4413; +const int IP_CONST_FEAT_PATH_FOCUS_EYES_DARKNESS = 4414; +const int IP_CONST_FEAT_PATH_FOCUS_SHUTTERS_CLOUDS = 4415; +const int IP_CONST_FEAT_PATH_FOCUS_TOUCH_TWILIGHT = 4416; +const int IP_CONST_FEAT_PATH_FOCUS_UMBRAL_MIND = 4417; +const int IP_CONST_FEAT_PATH_FOCUS_BLACK_MAGIC = 4418; +const int IP_CONST_FEAT_PATH_FOCUS_BODY_SOUL = 4419; +const int IP_CONST_FEAT_PATH_FOCUS_DARK_REFLECTIONS = 4420; +const int IP_CONST_FEAT_PATH_FOCUS_EBON_ROADS = 4421; +const int IP_CONST_FEAT_PATH_FOCUS_ELEMENTAL_SHADOWS = 4422; +const int IP_CONST_FEAT_PATH_FOCUS_UNBINDING_SHADE = 4423; +const int IP_CONST_FEAT_PATH_FOCUS_VEIL_SHADOWS = 4424; +const int IP_CONST_FEAT_PATH_FOCUS_BREATH_TWILIGHT = 4425; +const int IP_CONST_FEAT_PATH_FOCUS_DARK_METAMORPHOSIS = 4426; +const int IP_CONST_FEAT_PATH_FOCUS_EBON_WALLS = 4427; +const int IP_CONST_FEAT_PATH_FOCUS_EYES_NIGHT_SKY = 4428; +const int IP_CONST_FEAT_PATH_FOCUS_HEART_SOUL = 4429; +const int IP_CONST_FEAT_PATH_FOCUS_SHADOW_CALLING = 4430; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_CLOAK_SHADOWS = 4431; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_DARK_TERRAIN = 4432; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_EBON_WHISPERS = 4433; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_EYES_DARKNESS = 4434; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_SHUTTERS_CLOUDS = 4435; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_TOUCH_TWILIGHT = 4436; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_UMBRAL_MIND = 4437; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_BLACK_MAGIC = 4438; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_BODY_SOUL = 4439; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_DARK_REFLECTIONS = 4440; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_EBON_ROADS = 4441; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_ELEMENTAL_SHADOWS = 4442; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_UNBINDING_SHADE = 4443; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_VEIL_SHADOWS = 4444; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_BREATH_TWILIGHT = 4445; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_DARK_METAMORPHOSIS = 4446; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_EBON_WALLS = 4447; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_EYES_NIGHT_SKY = 4448; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_HEART_SOUL = 4449; +const int IP_CONST_FEAT_GREATER_PATH_FOCUS_SHADOW_CALLING = 4450; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_CLOAK_SHADOWS = 4451; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_DARK_TERRAIN = 4452; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_EBON_WHISPERS = 4453; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_EYES_DARKNESS = 4454; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_SHUTTERS_CLOUDS = 4455; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_TOUCH_TWILIGHT = 4456; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_UMBRAL_MIND = 4457; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_BLACK_MAGIC = 4458; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_BODY_SOUL = 4459; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_DARK_REFLECTIONS = 4460; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_EBON_ROADS = 4461; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_ELEMENTAL_SHADOWS = 4462; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_UNBINDING_SHADE = 4463; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_VEIL_SHADOWS = 4464; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_BREATH_TWILIGHT = 4465; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_DARK_METAMORPHOSIS = 4466; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_EBON_WALLS = 4467; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_EYES_NIGHT_SKY = 4468; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_HEART_SOUL = 4469; +const int IP_CONST_FEAT_NOCTURNAL_CASTER_SHADOW_CALLING = 4470; +const int IP_CONST_FEAT_FAV_MYST_BENDPERSPECTIVE = 4471; +const int IP_CONST_FEAT_FAV_MYST_CARPETSHADOW = 4472; +const int IP_CONST_FEAT_FAV_MYST_DUSKANDDAWN = 4473; +const int IP_CONST_FEAT_FAV_MYST_LIFEFADES = 4474; +const int IP_CONST_FEAT_FAV_MYST_MESMERIZINGSHADE = 4475; +const int IP_CONST_FEAT_FAV_MYST_STEELSHADOWS = 4476; +const int IP_CONST_FEAT_FAV_MYST_VOICEOFSHADOW = 4477; +const int IP_CONST_FEAT_FAV_MYST_BLACKFIRE = 4478; +const int IP_CONST_FEAT_FAV_MYST_CONGRESSSHADOWS = 4479; +const int IP_CONST_FEAT_FAV_MYST_FLESHFAILS = 4480; +const int IP_CONST_FEAT_FAV_MYST_PIERCINGSIGHT = 4481; +const int IP_CONST_FEAT_FAV_MYST_SHADOWSKIN = 4482; +const int IP_CONST_FEAT_FAV_MYST_SIGHTECLIPSED = 4483; +const int IP_CONST_FEAT_FAV_MYST_THOUGHTSSHADOW = 4484; +const int IP_CONST_FEAT_FAV_MYST_AFRAIDOFTHEDARK = 4485; +const int IP_CONST_FEAT_FAV_MYST_CLINGINGDARKNESS = 4486; +const int IP_CONST_FEAT_FAV_MYST_DANCINGSHADOWS = 4487; +const int IP_CONST_FEAT_FAV_MYST_FLICKER = 4488; +const int IP_CONST_FEAT_FAV_MYST_KILLINGSHADOWS = 4489; +const int IP_CONST_FEAT_FAV_MYST_SHARPSHADOWS = 4490; +const int IP_CONST_FEAT_FAV_MYST_UMBRALTOUCH = 4491; +const int IP_CONST_FEAT_FAV_MYST_AURAOFSHADE = 4492; +const int IP_CONST_FEAT_FAV_MYST_BOLSTER = 4493; +const int IP_CONST_FEAT_FAV_MYST_SHADOWEVOCATION = 4494; +const int IP_CONST_FEAT_FAV_MYST_SHADOWVISION = 4495; +const int IP_CONST_FEAT_FAV_MYST_SHADOWSFADE = 4496; +const int IP_CONST_FEAT_FAV_MYST_STEPINTOSHADOW = 4497; +const int IP_CONST_FEAT_FAV_MYST_WARPSPELL = 4498; +const int IP_CONST_FEAT_FAV_MYST_CURTAINSHADOWS = 4499; +const int IP_CONST_FEAT_FAV_MYST_DARKAIR = 4500; +const int IP_CONST_FEAT_FAV_MYST_ECHOSPELL = 4501; +const int IP_CONST_FEAT_FAV_MYST_FEIGNLIFE = 4502; +const int IP_CONST_FEAT_FAV_MYST_LANGUOR = 4503; +const int IP_CONST_FEAT_FAV_MYST_PASSINTOSHADOW = 4504; +const int IP_CONST_FEAT_FAV_MYST_UNRAVELDWEOMER = 4505; +const int IP_CONST_FEAT_FAV_MYST_FLOODSHADOWS = 4506; +const int IP_CONST_FEAT_FAV_MYST_GREATERSHADOWEVOCATION = 4507; +const int IP_CONST_FEAT_FAV_MYST_SHADOWINVESTITURE = 4508; +const int IP_CONST_FEAT_FAV_MYST_SHADOWSTORM = 4509; +const int IP_CONST_FEAT_FAV_MYST_SHADOWSFADE_GREATER = 4510; +const int IP_CONST_FEAT_FAV_MYST_UNVEIL = 4511; +const int IP_CONST_FEAT_FAV_MYST_VOYAGESHADOW = 4512; +const int IP_CONST_FEAT_FAV_MYST_DARKSOUL = 4513; +const int IP_CONST_FEAT_FAV_MYST_EPHEMERALIMAGE = 4514; +const int IP_CONST_FEAT_FAV_MYST_LIFEFADESGREATER = 4515; +const int IP_CONST_FEAT_FAV_MYST_PRISONNIGHT = 4516; +const int IP_CONST_FEAT_FAV_MYST_UMBRALSERVANT = 4517; +const int IP_CONST_FEAT_FAV_MYST_TRUTHREVEALED = 4518; +const int IP_CONST_FEAT_FAV_MYST_FARSIGHT = 4519; +const int IP_CONST_FEAT_FAV_MYST_GRFLESHFAILS = 4520; +const int IP_CONST_FEAT_FAV_MYST_SHADOWPLAGUE = 4521; +const int IP_CONST_FEAT_FAV_MYST_SOULPUPPET = 4522; +const int IP_CONST_FEAT_FAV_MYST_TOMBNIGHT = 4523; +const int IP_CONST_FEAT_FAV_MYST_UMBRALBODY = 4524; +const int IP_CONST_FEAT_FAV_MYST_ARMYSHADOW = 4525; +const int IP_CONST_FEAT_FAV_MYST_CONSUMEESSENCE = 4526; +const int IP_CONST_FEAT_FAV_MYST_EPHEMERALSTORM = 4527; +const int IP_CONST_FEAT_FAV_MYST_REFLECTIONS = 4528; +const int IP_CONST_FEAT_FAV_MYST_SHADOWSURGE = 4529; +const int IP_CONST_FEAT_FAV_MYST_SHADOWTIME = 4530; +const int IP_CONST_FEAT_SHADOW_CAST = 4531; +const int IP_CONST_FEAT_EMPOWER_MYSTERY = 4532; +const int IP_CONST_FEAT_EXTEND_MYSTERY = 4533; +const int IP_CONST_FEAT_MAXIMIZE_MYSTERY = 4534; +const int IP_CONST_FEAT_QUICKEN_MYSTERY = 4535; +const int IP_CONST_FEAT_STILL_MYSTERY = 4536; +const int IP_CONST_FEAT_DARK_TEMPLATE_MARKER = 4410; + +// Web Enhancement +const int IP_CONST_PATH_FOCUS_NIGHTS_LONG_FINGERS = 4358; +const int IP_CONST_PATH_FOCUS_DARKENED_ALLEYS = 4359; +const int IP_CONST_PATH_FOCUS_SHADOWSCAPE = 4360; +const int IP_CONST_GREATER_PATH_FOCUS_NIGHTS_LONG_FINGERS = 4361; +const int IP_CONST_GREATER_PATH_FOCUS_DARKENED_ALLEYS = 4362; +const int IP_CONST_GREATER_PATH_FOCUS_SHADOWSCAPE = 4363; +const int IP_CONST_NOCTURNAL_CASTER_NIGHTS_LONG_FINGERS = 4364; +const int IP_CONST_NOCTURNAL_CASTER_DARKENED_ALLEYS = 4365; +const int IP_CONST_NOCTURNAL_CASTER_SHADOWSCAPE = 4366; +const int IP_CONST_FAV_MYST_QUICKERTHANTHEEYE = 4349; +const int IP_CONST_FAV_MYST_TRAILHAZE = 4350; +const int IP_CONST_FAV_MYST_UMBRALFIST = 4351; +const int IP_CONST_FAV_MYST_FEARFULGLOOM = 4352; +const int IP_CONST_FAV_MYST_SICKENINGSHADOW = 4353; +const int IP_CONST_FAV_MYST_DEADLYSHADE = 4354; +const int IP_CONST_FAV_MYST_GRASPINGSHADOWS = 4355; +const int IP_CONST_FAV_MYST_MENAGERIEDARKNESS = 4356; +const int IP_CONST_FAV_MYST_BLACKLABYRINTH = 4357; + +/*////////////////////////////////////////////////// +//////////////// END SHADOWCASTING////////////////// +//////////////////////////////////////////////////*/ \ No newline at end of file diff --git a/src/include/prc_misc_const.nss b/src/include/prc_misc_const.nss new file mode 100644 index 0000000..0575811 --- /dev/null +++ b/src/include/prc_misc_const.nss @@ -0,0 +1,414 @@ +/** @file + * + * Include file for various constants that don't really belong in any of the other files, + * but aren't numerous enough to warrant their own. + */ + + //::////////////////////////////////////////////// + //:: New base item types +//::////////////////////////////////////////////// + +const int BASE_ITEM_GOLEM = 23; //:: not actually used for anything else +const int BASE_ITEM_LIGHT_LANCE = 92; +const int BASE_ITEM_ALCHEMY = 99; //:: not actually used for anything else +const int BASE_ITEM_POISON = 100; //:: not actually used for anything else +const int BASE_ITEM_HEAVY_PICK = 115; +const int BASE_ITEM_LIGHT_PICK = 116; +const int BASE_ITEM_SAI = 117; +const int BASE_ITEM_NUNCHAKU = 118; +const int BASE_ITEM_FALCHION = 119; +const int BASE_ITEM_SAP = 120; +const int BASE_ITEM_KATAR = 121; +const int BASE_ITEM_HEAVY_MACE = 122; +const int BASE_ITEM_MAUL = 123; +const int BASE_ITEM_DOUBLE_SCIMITAR = 124; +const int BASE_ITEM_GOAD = 125; +const int BASE_ITEM_EAGLE_CLAW = 126; +const int BASE_ITEM_CRAFTED_ROD = 199; +const int BASE_ITEM_CRAFTED_STAFF = 201; +const int BASE_ITEM_ELVEN_LIGHTBLADE = 202; +const int BASE_ITEM_ELVEN_THINBLADE = 203; +const int BASE_ITEM_ELVEN_COURTBLADE = 204; + +//::////////////////////////////////////////////// +//:: Player Health Const +//::////////////////////////////////////////////// + +const int ALIVE = 0; +const int BLEEDING = 1; +const int STABLE = 2; +const int DISABLED = 3; +const int DEAD = 4; +const int ALTERED_STATE = 5; + +//::////////////////////////////////////////////// +//:: Area of Effect Const +//::////////////////////////////////////////////// + +const int AOE_MOB_PESTILENCE = 150; +const int AOE_PER_TELEPORTATIONCIRCLE = 151; +const int AOE_PER_DEEPER_DARKNESS = 213; +const int AOE_PER_OBSCURING_MIST = 141; +const int AOE_PER_ACHAIERAI = 206; +const int AOE_PER_UTTERDARK = 205; +const int AOE_PER_DAMNDARK = 203; +const int VFX_MOB_BRILLIANT_EMANATION = 204; +const int AOE_PER_BLNDGLORY = 183; +const int VFX_AOE_RAIN_OF_BLACK_TULIPS = 182; +const int VFX_AOE_RAIN_OF_ROSES = 181; +const int AOE_PER_REPEL_VERMIN = 180; +const int AOE_PER_SOLID_FOG = 179; +const int AOE_PER_REPULSION = 178; +const int AOE_PER_CALM_EMOTIONS = 177; +const int VFX_PER_AVASMASS = 141; +const int VFX_PER_SICKEN_EVIL = 176; +const int VFX_PER_SLEET_STORM = 175; +const int VFX_PER_OTILUKES_RESILIENT_SPHERE = 174; +const int VFX_PER_PRISMATIC_SPHERE = 173; +const int VFX_MOB_RING_OF_BLADES = 172; +const int VFX_PER_PRISMATIC_WALL = 171; +const int AOE_PER_FIRE_TRAP = 170; +const int AOE_PER_PYROTECHNICS_SMOKE = 169; +const int VFX_PER_SNARE = 168; +const int AOE_MOB_MOUNTAIN_AVALANCHE = 167; +const int VFX_MOB_DAYLIGHT = 109; +const int VFX_MOB_CLOUDY_CONJURATION = 99; +const int AOE_PER_CRACKLEPOWDER = 98; +const int AOE_MOB_LUMINOUS_ARMOR = 97; +const int VFX_PER_GREEN_FOG = 96; +const int VFX_PER_RAIN_OF_BLOOD = 95; +const int VFX_PER_RAIN_OF_FROGS = 94; +const int VFX_PER_VIOLET_RAIN = 93; +const int AOE_PER_WALLFROST = 207; +const int AOE_PER_CONSECRATE = 115; +const int AOE_PER_DESECRATE = 111; +const int AOE_PER_WALLDISPEL = 116; + +// Psionic Area of Effects +const int AOE_PER_PSIGREASE = 131; +const int AOE_PER_ESHAMBLER = 132; +const int AOE_PER_ENERGYWALL = 133; +const int AOE_MOB_CATAPSI = 134; +const int AOE_PER_NULL_PSIONICS_FIELD = 135; +const int AOE_MOB_FORM_DOOM = 136; +const int AOE_PER_ENERGYWALL_WIDENED = 137; +const int AOE_PER_ESHAMBLER_WIDENED = 138; +const int AOE_PER_NULL_PSIONICS_FIELD_WIDENED = 139; +const int VFX_PER_NEW_TIMESTOP = 140; + + +// Invisible Area of Effects +const int VFX_PER_5_FT_INVIS = 184; +const int VFX_PER_10_FT_INVIS = 185; +const int VFX_PER_15_FT_INVIS = 186; +const int VFX_PER_20_FT_INVIS = 187; +const int VFX_PER_25_FT_INVIS = 188; +const int VFX_PER_30_FT_INVIS = 189; +const int VFX_PER_5M_INVIS = 190; +const int VFX_PER_10M_INVIS = 191; +const int VFX_PER_15M_INVIS = 192; +const int VFX_PER_20M_INVIS = 193; +const int VFX_PER_25M_INVIS = 194; +const int VFX_PER_30M_INVIS = 195; +const int VFX_PER_35M_INVIS = 196; +const int VFX_PER_40M_INVIS = 197; +const int VFX_PER_45M_INVIS = 198; +const int VFX_PER_50M_INVIS = 199; + +// Knight Area of Effects +const int AOE_MOB_BULWARK_DEFENSE = 162; +const int AOE_MOB_VIGILANT_DEFENDER = 163; + +//Exhaled Barrier Area of Effect +const int AOE_PER_WALLBREATH = 152; + +//Racial AOEs +const int AOE_PER_NYMPH_BLINDING = 225; + +// DNecro Area of Effects +const int AOE_MOB_DN_FEAR_AURA = 254; + +// DragonShaman/Marshal Auras +const int AOE_MOB_DRACONIC_AURA_1 = 153; +const int AOE_MOB_DRACONIC_AURA_2 = 154; +const int AOE_MOB_MARSHAL_MINOR_AURA = 155; +const int AOE_MOB_MARSHAL_MAJOR_AURA = 156; + +//::////////////////////////////////////////////// +//:: Disease Const +//::////////////////////////////////////////////// + +const int DISEASE_CONTAGION_BLINDING_SICKNESS = 20; +const int DISEASE_CONTAGION_CACKLE_FEVER = 21; +const int DISEASE_CONTAGION_FILTH_FEVER = 22; +const int DISEASE_CONTAGION_MINDFIRE = 23; +const int DISEASE_CONTAGION_RED_ACHE = 24; +const int DISEASE_CONTAGION_SHAKES = 25; +const int DISEASE_CONTAGION_SLIMY_DOOM = 26; +const int DISEASE_PESTILENCE = 51; +const int DISEASE_BLUE_GUTS = 53; +const int DISEASE_SOUL_ROT = 54; +const int DISEASE_AGONY_ADDICTION = 55; +const int DISEASE_BACCARAN_ADDICTION = 56; +const int DISEASE_DEVILWEED_ADDICTION = 57; +const int DISEASE_LUHIX_ADDICTION = 58; +const int DISEASE_MUSHROOM_POWDER_ADDICTION = 59; +const int DISEASE_SANNISH_ADDICTION = 60; +const int DISEASE_TERRAN_BRANDY_ADDICTION = 61; +const int DISEASE_VODARE_ADDICTION = 62; + +//::////////////////////////////////////////////// +//:: Poison Const +//::////////////////////////////////////////////// + +const int POISON_TINY_CENTIPEDE_POISON = 122; +const int POISON_MEDIUM_CENTIPEDE_POISON = 123; +const int POISON_LARGE_CENTIPEDE_POISON = 124; +const int POISON_HUGE_CENTIPEDE_POISON = 125; +const int POISON_GARGANTUAN_CENTIPEDE_POISON = 126; +const int POISON_COLOSSAL_CENTIPEDE_POISON = 127; + +const int POISON_TINY_SCORPION_VENOM = 128; +const int POISON_SMALL_SCORPION_VENOM = 129; +const int POISON_MEDIUM_SCORPION_VENOM = 130; +const int POISON_HUGE_SCORPION_VENOM = 131; +const int POISON_GARGANTUAN_SCORPION_VENOM = 132; +const int POISON_COLOSSAL_SCOPRION_VENOM = 133; + +const int POISON_EYEBLAST = 134; +const int POISON_BALOR_BILE = 135; +const int POISON_VILESTAR = 136; +const int POISON_SASSON_JUICE = 137; + +const int POISON_SUFFERFUME = 138; +const int POISON_URTHANYK = 139; +const int POISON_MIST_OF_NOURN = 140; +const int POISON_ISHENTAV = 141; +const int POISON_BURNING_ANGEL_WING_FUMES = 142; + +const int POISON_RAVAGE_GOLDEN_ICE = 100; +const int POISON_RAVAGE_CELESTIAL_LIGHTSBLOOD = 143; +const int POISON_RAVAGE_JADE_WATER = 144; +const int POISON_RAVAGE_PURIFIED_COUATL_VENOM = 145; +const int POISON_RAVAGE_UNICORN_BLOOD = 146; + +//::////////////////////////////////////////////// +//:: Skill Const +//::////////////////////////////////////////////// + +const int SKILL_JUMP = 28; +const int SKILL_TRUESPEAK = 29; +const int SKILL_SENSE_MOTIVE = 30; +const int SKILL_MARTIAL_LORE = 31; +const int SKILL_BALANCE = 32; +const int SKILL_IAIJUTSU_FOCUS = 33; // moved for ride +const int SKILL_CRAFT_ALCHEMY = 34; +const int SKILL_CRAFT_POISON = 35; +const int SKILL_PSICRAFT = 36; +const int SKILL_CLIMB = 37; +const int SKILL_CRAFT_GENERAL = 38; + +//::////////////////////////////////////////////// +//:: Size Const +//::////////////////////////////////////////////// + +const int CREATURE_SIZE_FINE = -1; +/** + * Yes, this is the same as CREATURE_SIZE_INVALID, live with it. + * If it weren't, the constants wouldn't be straight series any longer. + */ +const int CREATURE_SIZE_DIMINUTIVE = 0; +const int CREATURE_SIZE_GARGANTUAN = 6; +const int CREATURE_SIZE_COLOSSAL = 7; + + +//::////////////////////////////////////////////// +//:: Psionic Discipline Const +//::////////////////////////////////////////////// + +// Psionic Disciplines +const int DISCIPLINE_NONE = 0; +const int DISCIPLINE_PSYCHOMETABOLISM = 1; +const int DISCIPLINE_PSYCHOKINESIS = 2; +const int DISCIPLINE_PSYCHOPORTATION = 3; +const int DISCIPLINE_CLAIRSENTIENCE = 4; +const int DISCIPLINE_METACREATIVITY = 5; +const int DISCIPLINE_TELEPATHY = 6; + + +//::////////////////////////////////////////////// +//:: Polymorph Const +//::////////////////////////////////////////////// + +const int POLYMORPH_TYPE_BAT = 47; +const int POLYMORPH_TYPE_WOLF_0 = 133; +const int POLYMORPH_TYPE_WOLF_1 = 134; +const int POLYMORPH_TYPE_WOLF_2 = 135; +const int POLYMORPH_TYPE_WEREWOLF_0 = 136; +const int POLYMORPH_TYPE_WEREWOLF_1 = 137; +const int POLYMORPH_TYPE_WEREWOLF_2 = 138; + +const int POLYMORPH_TYPE_WOLF_0s = 139; +const int POLYMORPH_TYPE_WOLF_1s = 140; +const int POLYMORPH_TYPE_WOLF_2s = 141; +const int POLYMORPH_TYPE_WEREWOLF_0s = 142; +const int POLYMORPH_TYPE_WEREWOLF_1s = 143; +const int POLYMORPH_TYPE_WEREWOLF_2s = 144; + +const int POLYMORPH_TYPE_WOLF_0l = 145; +const int POLYMORPH_TYPE_WOLF_1l = 146; +const int POLYMORPH_TYPE_WOLF_2l = 147; +const int POLYMORPH_TYPE_WEREWOLF_0l = 148; +const int POLYMORPH_TYPE_WEREWOLF_1l = 149; +const int POLYMORPH_TYPE_WEREWOLF_2l = 150; + +//:: Wild Shape: Plant +const int POLYMORPH_TYPE_TREANT = 160; +const int POLYMORPH_TYPE_SHAMBLING_MOUND = 161; +const int POLYMORPH_TYPE_TWIG_BLIGHT = 162; +const int POLYMORPH_TYPE_MYCONID = 163; +const int POLYMORPH_TYPE_ALGOID = 164; + +//:://///////////////// +//:: DOMAIN CONSTANTS +//:: These constants are off by 1 to allow 0 to be the FALSE return value. +//:://///////////////// + +const int PRC_DOMAIN_AIR = 1; +const int PRC_DOMAIN_ANIMAL = 2; +const int PRC_DOMAIN_DEATH = 4; +const int PRC_DOMAIN_DESTRUCTION = 5; +const int PRC_DOMAIN_EARTH = 6; +const int PRC_DOMAIN_EVIL = 7; +const int PRC_DOMAIN_FIRE = 8; +const int PRC_DOMAIN_GOOD = 9; +const int PRC_DOMAIN_HEALING = 10; +const int PRC_DOMAIN_KNOWLEDGE = 11; +const int PRC_DOMAIN_MAGIC = 14; +const int PRC_DOMAIN_PLANT = 15; +const int PRC_DOMAIN_PROTECTION = 16; +const int PRC_DOMAIN_STRENGTH = 17; +const int PRC_DOMAIN_SUN = 18; +const int PRC_DOMAIN_TRAVEL = 19; +const int PRC_DOMAIN_TRICKERY = 20; +const int PRC_DOMAIN_WAR = 21; +const int PRC_DOMAIN_WATER = 22; +const int PRC_DOMAIN_DARKNESS = 31; +const int PRC_DOMAIN_STORM = 32; +const int PRC_DOMAIN_METAL = 33; +const int PRC_DOMAIN_PORTAL = 34; +const int PRC_DOMAIN_FORCE = 35; +const int PRC_DOMAIN_SLIME = 36; +const int PRC_DOMAIN_TYRANNY = 37; +const int PRC_DOMAIN_DOMINATION = 38; +const int PRC_DOMAIN_SPIDER = 39; +const int PRC_DOMAIN_UNDEATH = 40; +const int PRC_DOMAIN_TIME = 41; +const int PRC_DOMAIN_DWARF = 42; +const int PRC_DOMAIN_CHARM = 43; +const int PRC_DOMAIN_ELF = 44; +const int PRC_DOMAIN_FAMILY = 45; +const int PRC_DOMAIN_FATE = 46; +const int PRC_DOMAIN_GNOME = 47; +const int PRC_DOMAIN_ILLUSION = 48; +const int PRC_DOMAIN_HATRED = 49; +const int PRC_DOMAIN_HALFLING = 50; +const int PRC_DOMAIN_NOBILITY = 51; +const int PRC_DOMAIN_OCEAN = 52; +const int PRC_DOMAIN_ORC = 53; +const int PRC_DOMAIN_RENEWAL = 54; +const int PRC_DOMAIN_RETRIBUTION = 55; +const int PRC_DOMAIN_RUNE = 56; +const int PRC_DOMAIN_SPELLS = 57; +const int PRC_DOMAIN_SCALEYKIND = 58; +const int PRC_DOMAIN_BLIGHTBRINGER = 59; +const int PRC_DOMAIN_DRAGON = 60; +const int PRC_DOMAIN_COLD = 61; +const int PRC_DOMAIN_WINTER = 62; + +//::////////////////////////////////////////////// +//:: Trap Const +//::////////////////////////////////////////////// + +const int TRAP_BASE_TYPE_PRGT = 100; +const int TRAP_BASE_TYPE_TELECIRCLE = 101; +const int TRAP_BASE_TYPE_SYMBOL = 102; + +//::////////////////////////////////////////////// +//:: Breath Const +//::////////////////////////////////////////////// + +const int BREATH_NORMAL = 0; +const int BREATH_PYROCLASTIC = 1; +const int BREATH_SHADOW = 2; +const int BREATH_SWIFT_WING = 3; +const int BREATH_TOPAZ = 4; +const int BREATH_PARALYZE = 5; +const int BREATH_SLEEP = 6; +const int BREATH_SLOW = 7; +const int BREATH_WEAKENING = 8; +const int BREATH_SICKENING = 9; + +// ONHIT Spells +const int IP_CONST_ONHIT_CASTSPELL_DRAGONFIRE = 203; +const int IP_CONST_ONHIT_KAPAK_POISON = 204; + + +//::////////////////////////////////////////////// +//:: Wing Const +//::////////////////////////////////////////////// + +const int PRC_WING_TYPE_DRAGON_BRASS = 59; +const int PRC_WING_TYPE_DRAGON_BRONZE = 60; +const int PRC_WING_TYPE_DRAGON_COPPER = 61; +const int PRC_WING_TYPE_DRAGON_SILVER = 62; +const int PRC_WING_TYPE_DRAGON_GOLD = 63; +const int PRC_WING_TYPE_DRAGON_WHITE = 64; +const int PRC_WING_TYPE_DRAGON_BLACK = 65; +const int PRC_WING_TYPE_DRAGON_GREEN = 66; +const int PRC_WING_TYPE_DRAGON_BLUE = 67; +const int PRC_WING_TYPE_DRAGON_RED = 68; +const int PRC_WING_TYPE_DRAGON_BRASS2 = 69; +const int PRC_WING_TYPE_DRAGON_BRONZE2 = 70; +const int PRC_WING_TYPE_DRAGON_COPPER2 = 71; +const int PRC_WING_TYPE_DRAGON_SILVER2 = 72; +const int PRC_WING_TYPE_DRAGON_GOLD2 = 73; +const int PRC_WING_TYPE_DRAGON_WHITE2 = 74; +const int PRC_WING_TYPE_DRAGON_BLACK2 = 75; +const int PRC_WING_TYPE_DRAGON_GREEN2 = 76; +const int PRC_WING_TYPE_DRAGON_BLUE2 = 77; +const int PRC_WING_TYPE_DRAGON_RED2 = 78; + +//::////////////////////////////////////////////// +//:: Tail Const +//::////////////////////////////////////////////// + +const int PRC_TAIL_TYPE_DRAGON_BRASS = 4; +const int PRC_TAIL_TYPE_DRAGON_BRONZE = 5; +const int PRC_TAIL_TYPE_DRAGON_COPPER = 6; +const int PRC_TAIL_TYPE_DRAGON_SILVER = 7; +const int PRC_TAIL_TYPE_DRAGON_GOLD = 8; +const int PRC_TAIL_TYPE_DRAGON_BLACK = 9; +const int PRC_TAIL_TYPE_DRAGON_BLUE = 10; +const int PRC_TAIL_TYPE_DRAGON_GREEN = 11; +const int PRC_TAIL_TYPE_DRAGON_RED = 12; +const int PRC_TAIL_TYPE_DRAGON_WHITE = 13; + +//Morninglord +const int MOD_DAWN_START_HOUR = 6; +const float INTERVAL_BLESSING_OF_DAWN = 15.0; + +//Healer +const int ASSOCIATE_TYPE_CELESTIALCOMPANION = 9; + +//::////////////////////////////////////////////// +//:: Fortification Constants +//::////////////////////////////////////////////// + +const int FORTIFICATION_LIGHT = 25; +const int FORTIFICATION_MEDIUM = 50; +const int FORTIFICATION_MODERATE = 75; +const int FORTIFICATION_HEAVY = 100; + + +//:: void main (){} \ No newline at end of file diff --git a/src/include/prc_nui_consts.nss b/src/include/prc_nui_consts.nss new file mode 100644 index 0000000..0cb0efa --- /dev/null +++ b/src/include/prc_nui_consts.nss @@ -0,0 +1,113 @@ +//:://///////////////////////////////////////////// +//:: NUI Constants +//:: prc_nui_consts +//::////////////////////////////////////////////// +/* + This file holds all the constants used by the various PRC NUI scripts. +*/ +//::////////////////////////////////////////////// +//:: Created By: Rakiov +//:: Created On: 24.05.2005 +//::////////////////////////////////////////////// + +const int NUI_PAYLOAD_BUTTON_LEFT_CLICK = 0; +const int NUI_PAYLOAD_BUTTON_MIDDLE_CLICK = 1; +const int NUI_PAYLOAD_BUTTON_RIGHT_CLICK = 2; + + +////////////////////////////////////////////////// +// // +// NUI Spellbook // +// // +////////////////////////////////////////////////// + +// This is the NUI Spellbook window ID +const string PRC_SPELLBOOK_NUI_WINDOW_ID = "prcSpellbookNui"; + +// This is the base Id for the Class buttons in the NUI Spellbook, the ID will +// have the ClassID attached to it (i.e. spellbookClassButton_123) +const string PRC_SPELLBOOK_NUI_CLASS_BUTTON_BASEID = "spellbookClassButton_"; + +// This is the base Id for the Spell Circle buttons in the NUI Spellbook, the ID will +// have the Circle attached to it (i.e. spellbookCircleButton__6) +const string PRC_SPELLBOOK_NUI_CIRCLE_BUTTON_BASEID = "spellbookCircleButton_"; + +// This is the base Id for the Spell Buttons in the NUI Spellbook, the ID will +// have the SpellbookId (the row of the class's spell's 2da or equivalent) +// attached to it (i.e. spellbookSpellButton_6) +const string PRC_SPELLBOOK_NUI_SPELL_BUTTON_BASEID = "spellbookSpellButton_"; + +// This is the base Id for the Meta Feat buttons in the NUI Spellbook, the ID will +// have the FeatID attached to it (i.e. spellbookMetaButton_12345) +const string PRC_SPELLBOOK_NUI_META_BUTTON_BASEID = "spellbookMetaButton_"; + +// This is the selected ClassID var used to store what class was selected to the Player +// locally +const string PRC_SPELLBOOK_SELECTED_CLASSID_VAR = "prcSpellbookSelectedClassID"; + +// This is the selected Circle var used to store what spell circle was selected +// to the Player locally +const string PRC_SPELLBOOK_SELECTED_CIRCLE_VAR = "prcSpellbookSelectedCircle"; + +// This is the Spellbook NUI geomeotry var, used to allow the location and sizing +// of the NUI to be remembered if it is ever rerendered. +const string PRC_SPELLBOOK_NUI_GEOMETRY_VAR = "sbNuiGeometry"; + +// This is the Selected SpellID Var, used to tell the OnTarget script what spell +// we are using after manual targetting +const string NUI_SPELLBOOK_SELECTED_SPELLID_VAR = "NUI_Spellbook_SpellId"; + +// This is the Selected FeatID Var, used to tell the OnTarget script what feat +// we are using after manual targetting +const string NUI_SPELLBOOK_SELECTED_FEATID_VAR = "NUI_Spellbook_FeatID"; + +// This is the Selected SubSpellID Var, used in conjuncture with the Selected FeatID +// to allow radial spells to work (it needs the master spell's featID and the sub spell's +// SpellID for it to work. +const string NUI_SPELLBOOK_SELECTED_SUBSPELL_SPELLID_VAR = "NUI_Spellbook_SubSpellID"; + +// This is the OnTarget action var saved to the player locally to say if we are +// using the NUI Spellbook spell or not. +const string NUI_SPELLBOOK_ON_TARGET_ACTION_VAR = "ONPLAYERTARGET_ACTION"; + +// This is a Boolean to tell the target script if the selected feat is a persoanl feat +// and can only be used on the executing object. +const string NUI_SPELLBOOK_ON_TARGET_IS_PERSONAL_FEAT = "NUI_Spellbook_IsPersonalFeat"; + +const string NUI_SPELL_DESCRIPTION_WINDOW_ID = "NUI_Spell_Description"; +const string NUI_SPELL_DESCRIPTION_OK_BUTTON = "NUIDescriptionOKButton"; + +// This is the limit of how many spell buttons we can have in a row before we +// need to start a new row on the NUI Spellbook. +const int NUI_SPELLBOOK_SPELL_BUTTON_LENGTH = 9; + +const string NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR = "NUI_Spellbook_GetBinderSpellToFeatDictionaryCache"; +const string NUI_SPELLBOOK_CLASS_STANCES_CACHE_BASE_VAR = "NUI_Spellbook_GetToBStanceSpellListCache_"; +const string NUI_SPELLBOOK_CLASS_SHAPES_CACHE_BASE_VAR = "NUI_Spellbook_GetInvokerShapeSpellListCache_"; +const string NUI_SPELLBOOK_CLASS_ESSENCE_CACHE_BASE_VAR = "NUISpellbookClassEssence_"; + + + +////////////////////////////////////////////////// +// // +// NUI Power Attack // +// // +////////////////////////////////////////////////// + +// The Window ID for the Power Attack NUI +const string NUI_PRC_POWER_ATTACK_WINDOW = "nui_prc_power_attack_window"; + +// LocalVar for the geometry of the Power Attack NUI window +const string NUI_PRC_PA_GEOMETRY_VAR = "paNuiGeometry"; + +// Event For Left "-" button of the Power Attack NUI +const string NUI_PRC_PA_LEFT_BUTTON_EVENT = "nui_prc_pa_left_button_event"; +// Event For Right "+" Button of the Power Attack NUI +const string NUI_PRC_PA_RIGHT_BUTTON_EVENT = "nui_prc_pa_right_button_event"; + +// Bind for Text of the Power Attack NUI saying what the current Power Attack level is +const string NUI_PRC_PA_TEXT_BIND = "nui_prc_pa_text_bind"; +// Left Button Enabled Bind for Power Attack NUI +const string NUI_PRC_PA_LEFT_BUTTON_ENABLED_BIND = "leftButtonEnabled"; +// Right Button Enabled Bind for Power Attack NUI +const string NUI_PRC_PA_RIGHT_BUTTON_ENABLED_BIND = "rightButtonEnabled"; \ No newline at end of file diff --git a/src/include/prc_nui_sc_inc.nss b/src/include/prc_nui_sc_inc.nss new file mode 100644 index 0000000..a1a768e --- /dev/null +++ b/src/include/prc_nui_sc_inc.nss @@ -0,0 +1,1148 @@ +//:://///////////////////////////////////////////// +//:: PRC Spellbook Script +//:: prc_nui_sb_inc +//::////////////////////////////////////////////// +/* + This is the script that handles some backend work for the PRC Spellbook + NUI View +*/ +//::////////////////////////////////////////////// +//:: Created By: Rakiov +//:: Created On: 24.05.2005 +//::////////////////////////////////////////////// +#include "inc_newspellbook" +#include "psi_inc_psifunc" +#include "inc_lookups" +#include "prc_nui_consts" + +// +// GetSpellListForCircle +// Gets the spell list for a specified class at the specified circle. +// +// Arguments: +// oPlayer:object the player +// nClass:int the ClassID +// circle:int the circle we want to grab for +// +// Returns: +// json:Array a list of all the spellIDs of the given circle +// +json GetSpellListForCircle(object oPlayer, int nClass, int circle); + +// +// GetSupportedNUISpellbookClasses +// Gets the list of support PRC classes that can use the NUi spellbook that +// the player has. +// +// Arguments: +// oPlayer:object the player this is being determined for +// +// Returns: +// json:int list of class ids that have the player has that can use the +// NUI spellbook. +// +json GetSupportedNUISpellbookClasses(object oPlayer); + +// +// GetCurrentSpellLevel +// Gets the current spell level the class can achieve at the current +// caster level (ranging from 0-9) +// +// Arguments: +// nClass:int the ClassID +// nLevel:int the caster level +// +// Returns: +// int the circle the class can achieve currently +// +int GetCurrentSpellLevel(int nClass, int nLevel); + +// +// GetMaxSpellLevel +// Gets the highest possible circle the class can achieve (from 0-9) +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int the highest circle that can be achieved +// +int GetMaxSpellLevel(int nClass); + +// +// GetMinSpellLevel +// Gets the lowest possible circle the class can achieve (from 0-9) +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int the lowest circle that can be achieved +// +int GetMinSpellLevel(int nClass); + +// +// GetHighestLevelPossibleInClass +// Given a class Id this will determine what the max level of a class can be +// achieved +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int the highest possible level the class can achieve +// +int GetHighestLevelPossibleInClass(int nClass); + +// +// GetClassSpellbookFile +// Gets the class 2da spellbook/ability for the given class Id +// +// Arguments: +// nClass:int the classID +// +// Returns: +// string the 2da file name for the spell/abilities of the ClassID +// +string GetClassSpellbookFile(int nClass); + +// +// IsSpellKnown +// Returns whether the player with the given class, spell file, and spellbook id +// knows the spell or not +// +// Arguments: +// oPlayer;Object the player +// nClass:int the class ID +// spellId:int the spell ID to check +// +// Returns: +// int:Boolean TRUE if spell is known, FALSE otherwise +// +int IsSpellKnown(object oPlayer, int nClass, int spellId); + +// +// IsClassAllowedToUseNUISpellbook +// Takes a player and a classId and determines if thee class is allowed to +// be using the NUI spellbook. +// +// Arguments: +// oPlayer:Object the player +// nClass:int the ClassID +// +// Returns: +// int:Boolean TRUE if allowed to use the spellbook, FALSE otherwise +// +int IsClassAllowedToUseNUISpellbook(object oPlayer, int nClass); + +// +// CanClassUseMetamagicFeats +// Given a class id determines if it is allowed to use the Metamagic feats +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int:Boolean TRUE if allowed to use the set of feats, FALSE otherwise +// +int CanClassUseMetamagicFeats(int nClass); + +// +// CanClassUseSuddenMetamagicFeats +// Given a class id determines if it is allowed to use the Sudden Metamagic feats +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int:Boolean TRUE if allowed to use the set of feats, FALSE otherwise +// +int CanClassUseSuddenMetamagicFeats(int nClass); + +// +// CanClassUseMetaPsionicFeats +// Given a class id determines if it is allowed to use the MetaPsionic feats +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int:Boolean TRUE if allowed to use the set of feats, FALSE otherwise +// +int CanClassUseMetaPsionicFeats(int nClass); + +// +// CanClassUseMetaMysteryFeats +// Given a class id determines if it is allowed to use the MetaMystery feats +// +// Arguments: +// nClass:int the ClassID +// +// Returns: +// int:Boolean TRUE if allowed to use the set of feats, FALSE otherwise +// +int CanClassUseMetaMysteryFeats(int nClass); + +// +// GetMetaMagicFeatList +// Gets the list of MetaMagic featIDs +// +// Returns: +// json:Array the list of FeatIDs associated with the meta feats +// +json GetMetaMagicFeatList(); + +// +// GetSuddenMetaMagicFeatList +// Gets the list of Sudden MetaMagic featIDs +// +// Returns: +// json:Array the list of FeatIDs associated with the meta feats +// +json GetSuddenMetaMagicFeatList(); + +// +// GetMetaPsionicFeatList +// Gets the list of MetaPsionic featIDs +// +// Returns: +// json:Array the list of FeatIDs associated with the meta feats +// +json GetMetaPsionicFeatList(); + +// +// GetMetaMagicMysteryList +// Gets the list of MetaMystery featIDs +// +// Returns: +// json:Array the list of FeatIDs associated with the meta feats +// +json GetMetaMysteryFeatList(); + +// +// GetTrueClassIfRHD +// Checks to make sure if the provided RHD class and player's race +// match up to give them their proper spell caster class (ie Glouras have +// bard spells and thus should be treated like a bard class) +// +// Arguments: +// oPlayer:object the player +// nClass:int the ClassID +// +// Returns: +// int the true ClassID to use, otherwise nClass +// +int GetTrueClassIfRHD(object oPlayer, int nClass); + +// +// GetBinderSpellToFeatDictionary +// Sets up the Binder Spell Dictionary that is used to match a binder's vestige +// to their feat. This is constructed based off the binder's known location of +// their feat and spell ranges in the base 2das respectivly. After constructing +// this it will be saved to the player locally as a cached result since we do +// not need to call this again. +// +// Argument: +// oPlayer:object the player +// +// Returns: +// json:Dictionary a dictionary of mapping between the SpellID +// and the FeatID of a vestige ability +// +json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF); + +// +// ShouldAddSpell +// Given a spellId and a class, determines if the spell should be added to the +// spellbook (as some are added in it's own special row or for other reasons) +// +// Arguments: +// nClass:int the ClassID +// spellId:int the SpellID +// oPlayer:object the player +// +// Returns: +// int:Boolean TRUE if the spell should be added, FALSE otherwise +// +int ShouldAddSpell(int nClass, int spellId, object oPlayer=OBJECT_SELF); + +// +// GetToBStanceSpellList +// Gets the ToB Stance Spell List for the given class +// +// Arguments: +// nClass:int ClassID +// oPlayer:object the player +// +// Returns: +// json:Array the list of stances' SpellIDs +// +json GetToBStanceSpellList(int nClass, object oPlayer=OBJECT_SELF); + +// +// GetInvokerShapeSpellList +// Gets the Invoker Shapes Spell List for the given class +// +// Arguments: +// nClass:int ClassID +// oPlayer:object the player +// +// Returns: +// json:Array the list of shapes' SpellIDs +// +json GetInvokerShapeSpellList(int nClass, object oPlayer=OBJECT_SELF); + +// +// GetInvokerEssenceSpellList +// Gets the Invoker Essences Spell List for the given class +// +// Arguments: +// nClass:int ClassID +// oPlayer:object the player +// +// Returns: +// json:Array the list of essences' SpellIDs +// +json GetInvokerEssenceSpellList(int nClass, object oPlayer=OBJECT_SELF); + +// +// JsonArrayContainsInt +// A helper function that takes a json array list and sees if the int item is within i +// +// Arguments: +// list:json:Array the list of ints +// item:int the item we are looking for +// +// Returns: +// int:Boolean TRUE if item is found, FALSE otherwise +// +int JsonArrayContainsInt(json list, int item); + +json GetSpellListForCircle(object oPlayer, int nClass, int circle) +{ + json retValue = JsonArray(); + string sFile = GetClassSpellbookFile(nClass); + int totalSpells; + json binderDictKeys; + //Special case for Binder since they don't have their own spellbook 2da + if (nClass == CLASS_TYPE_BINDER) + { + json binderDict = GetBinderSpellToFeatDictionary(oPlayer); + + // we loop through the list of SpellIDs + binderDictKeys = JsonObjectKeys(binderDict); + totalSpells = JsonGetLength(binderDictKeys); + } + else + totalSpells = Get2DARowCount(sFile); + + int i; + for (i = 0; i < totalSpells; i++) + { + int currentSpell; + if (nClass == CLASS_TYPE_BINDER) + currentSpell = StringToInt(JsonGetString(JsonArrayGet(binderDictKeys, i))); + else + currentSpell = StringToInt(Get2DACache(sFile, "SpellID", i)); + + if (ShouldAddSpell(nClass, currentSpell, oPlayer)) + { + string sSpellLevel = Get2DACache("spells", "Innate", currentSpell); + int iSpellLevel = StringToInt(sSpellLevel); + + if (nClass == CLASS_TYPE_BINDER && IsSpellKnown(oPlayer, nClass, currentSpell)) + { + retValue = JsonArrayInsert(retValue, JsonInt(currentSpell)); + } + else if ((iSpellLevel == circle && IntToString(iSpellLevel) == sSpellLevel)) + { + // We add the spell if it is known and is not a radial master spell (since those don't work) + if (IsSpellKnown(oPlayer, nClass, currentSpell)) + retValue = JsonArrayInsert(retValue, JsonInt(i)); + } + } + } + + return retValue; +} + +int ShouldAddSpell(int nClass, int spellId, object oPlayer=OBJECT_SELF) +{ + int isRadialMasterSpell = StringToInt(Get2DACache("spells", "SubRadSpell1", spellId)); + // We don't add radial master spells + if (isRadialMasterSpell) + return FALSE; + // we don't add essences and shapes + if (nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGONFIRE_ADEPT + || nClass == CLASS_TYPE_DRAGON_SHAMAN) + { + json ignoreList = GetInvokerShapeSpellList(nClass, oPlayer); + if (JsonArrayContainsInt(ignoreList, spellId)) + return FALSE; + ignoreList = GetInvokerEssenceSpellList(nClass, oPlayer); + if (JsonArrayContainsInt(ignoreList, spellId)) + return FALSE; + } + // we don't add stances + if (nClass == CLASS_TYPE_WARBLADE + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_CRUSADER) + { + json ignoreList = GetToBStanceSpellList(nClass, oPlayer); + if (JsonArrayContainsInt(ignoreList, spellId)) + return FALSE; + } + + return TRUE; +} + +json GetBinderSpellToFeatDictionary(object oPlayer=OBJECT_SELF) +{ + // a dictionary of + json binderDict = GetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR); + // if this hasn't been created, create it now. + if (binderDict == JsonNull()) + binderDict = JsonObject(); + else + return binderDict; + + // the starting row for binder spells + int spellIndex = 19070; + // the starting row for binder feats + int featIndex = 9030; + //the end of the binder spells/feats + while (spellIndex <= 19156 && featIndex <= 9104) + { + // get the SpellID tied to the feat + int spellID = StringToInt(Get2DACache("feat", "SPELLID", featIndex)); + // if the spellID matches the current index, then this is the spell + // attached to the feat + if (spellID == spellIndex) + { + binderDict = JsonObjectSet(binderDict, IntToString(spellID), JsonInt(featIndex)); + + // move to next spell/feat + featIndex++; + spellIndex++; + } + // else we have reached a subdial spell + else + { + // loop through until we reach back at spellID + while (spellIndex < spellID) + { + int masterSpell = StringToInt(Get2DACache("spells", "Master", spellIndex)); + + // add the sub radial to the dict, tied to the master's FeatID + int featId = JsonGetInt(JsonObjectGet(binderDict, IntToString(masterSpell))); + binderDict = JsonObjectSet(binderDict, IntToString(spellIndex), JsonInt(featId)); + + spellIndex++; + } + + + // some feats overlap the same FeatID, can cause this to get stuck. + // if it happens then move on + if (spellIndex > spellID) + featIndex++; + } + } + + // cache the result + SetLocalJson(oPlayer, NUI_SPELLBOOK_BINDER_DICTIONARY_CACHE_VAR, binderDict); + return binderDict; +} + +string GetClassSpellbookFile(int nClass) +{ + string sFile; + // Spontaneous casters use a specific file name structure + if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS + || nClass == CLASS_TYPE_ARCHIVIST) + { + sFile = GetFileForClass(nClass); + } + // everyone else uses this structure + else + { + sFile = GetAMSDefinitionFileName(nClass); + + if (nClass == CLASS_TYPE_BINDER) + { + sFile = "vestiges"; + } + } + + return sFile; +} + +json GetSupportedNUISpellbookClasses(object oPlayer) +{ + json retValue = JsonArray(); + int i = 1; + while(i >= 1) + { + int classId = GetClassByPosition(i, oPlayer); + if (classId != CLASS_TYPE_INVALID) + { + if (IsClassAllowedToUseNUISpellbook(oPlayer, classId)) + { + classId = GetTrueClassIfRHD(oPlayer, classId); + retValue = JsonArrayInsert(retValue, JsonInt(classId)); + } + i++; + } + else + { + i = -1; + } + } + + return retValue; +} + +int IsSpellKnown(object oPlayer, int nClass, int spellId) +{ + // special case for Binders since they don't have a spell book 2da. + if (nClass == CLASS_TYPE_BINDER) + { + json binderDict = GetBinderSpellToFeatDictionary(oPlayer); + int featID = JsonGetInt(JsonObjectGet(binderDict, IntToString(spellId))); + return GetHasFeat(featID, oPlayer); + } + + int currentSpell = spellId; + int masterSpell = StringToInt(Get2DACache("spells", "Master", currentSpell)); + if (masterSpell) // If this is not 0 then this is a radial spell, check the radial master + currentSpell = masterSpell; + + string sFeatID = Get2DACache("spells", "FeatID", currentSpell); + int iFeatID = StringToInt(sFeatID); + + if (IntToString(iFeatID) == sFeatID) + return GetHasFeat(iFeatID, oPlayer); + + return FALSE; +} + +int GetCurrentSpellLevel(int nClass, int nLevel) +{ + int currentLevel = nLevel; + + // ToB doesn't have a concept of spell levels, but still match up to it + if(nClass == CLASS_TYPE_WARBLADE + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_SHADOWCASTER) + { + return 9; + } + + + // Binders don't really have a concept of spell level + if (nClass == CLASS_TYPE_BINDER + || nClass == CLASS_TYPE_DRAGON_SHAMAN) // they can only reach 1st circle + return 1; + + //Shadowsmith has no concept of spell levels + if (nClass == CLASS_TYPE_SHADOWSMITH) + return 2; + + if (nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGONFIRE_ADEPT) + return 4; + + // Spont casters have their own function + if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS + || nClass == CLASS_TYPE_ARCHIVIST) + { + + int maxLevel = GetMaxSpellLevelForCasterLevel(nClass, currentLevel); + return maxLevel; + } + else + { + // everyone else uses this + string spellLevel2da = GetAMSKnownFileName(nClass); + + currentLevel = nLevel - 1; // Level is 1 off of the row in the 2da + + if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN + || nClass == CLASS_TYPE_PSION + || nClass == CLASS_TYPE_PSYWAR + || nClass == CLASS_TYPE_WILDER + || nClass == CLASS_TYPE_PSYCHIC_ROGUE + || nClass == CLASS_TYPE_WARMIND) + currentLevel = GetManifesterLevel(OBJECT_SELF, nClass, TRUE) - 1; + + int totalLevel = Get2DARowCount(spellLevel2da); + + // in case we somehow go over bounds just don't :) + if (currentLevel >= totalLevel) + currentLevel = totalLevel - 1; + + //Psionics have MaxPowerLevel as their column name + string columnName = "MaxPowerLevel"; + + //Invokers have MaxInvocationLevel + if (nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGON_SHAMAN + || nClass == CLASS_TYPE_DRAGONFIRE_ADEPT) + columnName = "MaxInvocationLevel"; + + // Truenamers have 3 sets of utterances, ranging from 1-6, EvolvingMind covers the entire range + if (nClass == CLASS_TYPE_TRUENAMER) + { + columnName = "EvolvingMind"; + spellLevel2da = "cls_true_maxlvl"; //has a different 2da we want to look at + } + + if (nClass == CLASS_TYPE_BINDER) + { + columnName = "VestigeLvl"; + spellLevel2da = "cls_bind_binder"; + } + + // ToB doesn't have a concept of this, but we don't care. + + int maxLevel = StringToInt(Get2DACache(spellLevel2da, columnName, currentLevel)); + return maxLevel; + } +} + +int GetMinSpellLevel(int nClass) +{ + // again sponts have their own function + if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS + || nClass == CLASS_TYPE_ARCHIVIST) + { + return GetMinSpellLevelForCasterLevel(nClass, GetHighestLevelPossibleInClass(nClass)); + } + else + { + if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN + || nClass == CLASS_TYPE_PSION + || nClass == CLASS_TYPE_PSYWAR + || nClass == CLASS_TYPE_WILDER + || nClass == CLASS_TYPE_PSYCHIC_ROGUE + || nClass == CLASS_TYPE_WARMIND + || nClass == CLASS_TYPE_WARBLADE + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGONFIRE_ADEPT + || nClass == CLASS_TYPE_DRAGON_SHAMAN + || nClass == CLASS_TYPE_SHADOWCASTER + || nClass == CLASS_TYPE_SHADOWSMITH + || nClass == CLASS_TYPE_BINDER) + return 1; + + return GetCurrentSpellLevel(nClass, 1); + } + +} + +int GetMaxSpellLevel(int nClass) +{ + if (nClass == CLASS_TYPE_WILDER + || nClass == CLASS_TYPE_PSION) + return 9; + if (nClass == CLASS_TYPE_PSYCHIC_ROGUE + || nClass == CLASS_TYPE_FIST_OF_ZUOKEN + || nClass == CLASS_TYPE_WARMIND) + return 5; + if (nClass == CLASS_TYPE_PSYWAR) + return 6; + + return GetCurrentSpellLevel(nClass, GetHighestLevelPossibleInClass(nClass)); +} + +int GetHighestLevelPossibleInClass(int nClass) +{ + string sFile; + + //sponts have their spells in the classes.2da + if(GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS + || nClass == CLASS_TYPE_ARCHIVIST) + { + sFile = Get2DACache("classes", "SpellGainTable", nClass); + } + else + { + // everyone else uses this + sFile = GetAMSKnownFileName(nClass); + + if (nClass == CLASS_TYPE_TRUENAMER) + { + sFile = "cls_true_maxlvl"; //has a different 2da we want to look at + } + + if (nClass == CLASS_TYPE_BINDER) + { + sFile = "cls_bind_binder"; + } + } + + return Get2DARowCount(sFile); +} + +int IsClassAllowedToUseNUISpellbook(object oPlayer, int nClass) +{ + // This controls who can use the Spellbook NUI, if for some reason you don't + // want a class to be allowed to use this you can comment out their line here + + // Bard and Sorc are allowed if they took a PRC that makes them use the spellbook + if ((nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_SORCERER) + && GetPrCAdjustedClassLevel(nClass, oPlayer) > GetLevelByClass(nClass, oPlayer)) + return TRUE; + + // Arcane Spont + if (nClass == CLASS_TYPE_ASSASSIN + || nClass == CLASS_TYPE_BEGUILER + || nClass == CLASS_TYPE_CELEBRANT_SHARESS + || nClass == CLASS_TYPE_DREAD_NECROMANCER + || nClass == CLASS_TYPE_DUSKBLADE + || nClass == CLASS_TYPE_HARPER + || nClass == CLASS_TYPE_HEXBLADE + || nClass == CLASS_TYPE_KNIGHT_WEAVE + || nClass == CLASS_TYPE_SHADOWLORD + || nClass == CLASS_TYPE_SUBLIME_CHORD + || nClass == CLASS_TYPE_SUEL_ARCHANAMACH + || nClass == CLASS_TYPE_WARMAGE) + return TRUE; + + // Psionics + if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN + || nClass == CLASS_TYPE_PSION + || nClass == CLASS_TYPE_PSYWAR + || nClass == CLASS_TYPE_WILDER + || nClass == CLASS_TYPE_PSYCHIC_ROGUE + || nClass == CLASS_TYPE_WARMIND) + return TRUE; + + // Invokers + if (nClass == CLASS_TYPE_WARLOCK + || nClass == CLASS_TYPE_DRAGON_SHAMAN + || nClass == CLASS_TYPE_DRAGONFIRE_ADEPT) + return TRUE; + + // Divine Spont + if (nClass == CLASS_TYPE_ARCHIVIST //while technically prepared, they use the spont system of casting + || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_JUSTICEWW) + return TRUE; + + // ToB Classes + if (nClass == CLASS_TYPE_WARBLADE + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_CRUSADER) + return TRUE; + + // Mystery Classes + if (nClass == CLASS_TYPE_SHADOWCASTER + || nClass == CLASS_TYPE_SHADOWSMITH) + return TRUE; + + // Truenamers + if (nClass == CLASS_TYPE_TRUENAMER) + return TRUE; + + // RHD Casters + if ((nClass == CLASS_TYPE_SHAPECHANGER + && GetRacialType(oPlayer) == RACIAL_TYPE_ARANEA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_OUTSIDER + && GetRacialType(oPlayer) == RACIAL_TYPE_RAKSHASA + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_ABERRATION + && GetRacialType(oPlayer) == RACIAL_TYPE_DRIDER + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_ARKAMOI + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_HOBGOBLIN_WARSOUL + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_REDSPAWN_ARCANISS + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_MARRUTACT + && !GetLevelByClass(CLASS_TYPE_SORCERER)) + || (nClass == CLASS_TYPE_FEY + && GetRacialType(oPlayer) == RACIAL_TYPE_GLOURA + && !GetLevelByClass(CLASS_TYPE_BARD))) + return TRUE; + + // Binders + if (nClass == CLASS_TYPE_BINDER) + return TRUE; + + return FALSE; +} + +int GetTrueClassIfRHD(object oPlayer, int nClass) +{ + if (nClass == CLASS_TYPE_SHAPECHANGER + && GetRacialType(oPlayer) == RACIAL_TYPE_ARANEA) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_OUTSIDER + && GetRacialType(oPlayer) == RACIAL_TYPE_RAKSHASA) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_ABERRATION + && GetRacialType(oPlayer) == RACIAL_TYPE_DRIDER) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_ARKAMOI) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_REDSPAWN_ARCANISS) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPlayer) == RACIAL_TYPE_MARRUTACT) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_FEY + && GetRacialType(oPlayer) == RACIAL_TYPE_GLOURA) + return CLASS_TYPE_BARD; + + return nClass; +} + +int CanClassUseMetamagicFeats(int nClass) +{ + // I don't want to spend the time looping through each class's + // feat 2da so this is the list of all classes that are allowed to use the + // Spellbook NUI and can use Metamagic + return (nClass == CLASS_TYPE_ASSASSIN + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_BEGUILER + || nClass == CLASS_TYPE_DREAD_NECROMANCER + || nClass == CLASS_TYPE_DUSKBLADE + || nClass == CLASS_TYPE_HEXBLADE + || nClass == CLASS_TYPE_JUSTICEWW + || nClass == CLASS_TYPE_SUBLIME_CHORD + || nClass == CLASS_TYPE_SUEL_ARCHANAMACH + || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_WARMAGE); +} + +int CanClassUseSuddenMetamagicFeats(int nClass) +{ + // I don't want to spend the time looping through each class's + // feat 2da so this is the list of all classes that are allowed to use the + // Spellbook NUI and can use Sudden Metamagic + return (nClass == CLASS_TYPE_SHADOWLORD + || nClass == CLASS_TYPE_ARCHIVIST + || nClass == CLASS_TYPE_ASSASSIN + || nClass == CLASS_TYPE_BARD + || nClass == CLASS_TYPE_BEGUILER + || nClass == CLASS_TYPE_DREAD_NECROMANCER + || nClass == CLASS_TYPE_DUSKBLADE + || nClass == CLASS_TYPE_FAVOURED_SOUL + || nClass == CLASS_TYPE_HEXBLADE + || nClass == CLASS_TYPE_JUSTICEWW + || nClass == CLASS_TYPE_KNIGHT_WEAVE + || nClass == CLASS_TYPE_SUBLIME_CHORD + || nClass == CLASS_TYPE_SORCERER + || nClass == CLASS_TYPE_SUEL_ARCHANAMACH + || nClass == CLASS_TYPE_WARMAGE); +} + +int CanClassUseMetaPsionicFeats(int nClass) +{ + // I don't want to spend the time looping through each class's + // feat 2da so this is the list of all classes that are allowed to use the + // Spellbook NUI and can use Metapsionics + return (nClass == CLASS_TYPE_FIST_OF_ZUOKEN + || nClass == CLASS_TYPE_PSION + || nClass == CLASS_TYPE_PSYCHIC_ROGUE + || nClass == CLASS_TYPE_PSYWAR + || nClass == CLASS_TYPE_WARMIND + || nClass == CLASS_TYPE_WILDER); +} + +int CanClassUseMetaMysteryFeats(int nClass) +{ + // I don't want to spend the time looping through each class's + // feat 2da so this is the list of all classes that are allowed to use the + // Spellbook NUI and can use Metamysteries + return (nClass == CLASS_TYPE_SHADOWCASTER + || nClass == CLASS_TYPE_SHADOWSMITH); +} + +json GetMetaMagicFeatList() +{ + json metaFeats = JsonArray(); + int spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_EXTEND_SPELL_ABILITY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_EMPOWER_SPELL_ABILITY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_MAXIMIZE_SPELL_ABILITY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_QUICKEN_SPELL_ABILITY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_STILL_SPELL_ABILITY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SILENT_SPELL_ABILITY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + + return metaFeats; +} + +json GetSuddenMetaMagicFeatList() +{ + json metaFeats = JsonArray(); + int spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SUDDEN_EXTEND)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SUDDEN_EMPOWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SUDDEN_MAXIMIZE)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SUDDEN_WIDEN)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + + return metaFeats; +} + +json GetMetaPsionicFeatList() +{ + json metaFeats = JsonArray(); + int spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_EXTEND_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_EMPOWER_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_MAXIMIZE_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_QUICKEN_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_WIDEN_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_CHAIN_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_TWIN_POWER)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SPLIT_PSIONIC_RAY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + + return metaFeats; +} + +json GetMetaMysteryFeatList() +{ + json metaFeats = JsonArray(); + int spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_EXTEND_MYSTERY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_EMPOWER_MYSTERY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_MAXIMIZE_MYSTERY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_QUICKEN_MYSTERY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_STILL_MYSTERY)); + metaFeats = JsonArrayInsert(metaFeats, JsonInt(spellId)); + + return metaFeats; +} + +json GetToBStanceSpellList(int nClass, object oPlayer=OBJECT_SELF) +{ + // caching + json stanceSpells = GetLocalJson(oPlayer, NUI_SPELLBOOK_CLASS_STANCES_CACHE_BASE_VAR + IntToString(nClass)); + if (stanceSpells == JsonNull()) + stanceSpells = JsonArray(); + else + return stanceSpells; + + string sFile = GetClassSpellbookFile(nClass); + int totalRows = Get2DARowCount(sFile); + + int i; + for (i = 0; i < totalRows; i++) + { + int Type = StringToInt(Get2DACache(sFile, "Type", i)); + if (Type == 1) + { + int spellId = StringToInt(Get2DACache(sFile, "SpellID", i)); + stanceSpells = JsonArrayInsert(stanceSpells, JsonInt(spellId)); + } + } + + SetLocalJson(oPlayer, NUI_SPELLBOOK_CLASS_STANCES_CACHE_BASE_VAR + IntToString(nClass), stanceSpells); + return stanceSpells; +} + +json GetInvokerShapeSpellList(int nClass, object oPlayer=OBJECT_SELF) +{ + // caching + json shapeSpells = GetLocalJson(oPlayer, NUI_SPELLBOOK_CLASS_SHAPES_CACHE_BASE_VAR + IntToString(nClass)); + if (shapeSpells == JsonNull()) + shapeSpells = JsonArray(); + else + return shapeSpells; + + string sFile = GetClassSpellbookFile(nClass); + int totalRows = Get2DARowCount(sFile); + + if (nClass == CLASS_TYPE_WARLOCK) + { + // Add the ELdritch Blast shapes + // TODO: Replace these magic SpellID ints with consts + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(INVOKE_ELDRITCH_BLAST)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(18216)); // Eldritch Chain + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(18245)); // Eldritch Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(18261)); // Eldritch Doom + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(18172)); // Glaive + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(18246)); // Eldritch Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(18173)); // Eldritch Spear + } + + if (nClass == CLASS_TYPE_DRAGON_SHAMAN) + { + // Add the Dragon Shaman Auras + int spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_ENERGY)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_ENERGYSHLD)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_INSIGHT)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_MAGICPOWER)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_POWER)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_PRESENCE)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_RESISTANCE)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_RESOLVE)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_SENSES)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_STAMINA)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_SWIFTNESS)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_TOUGHNESS)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_DRAGONSHAMAN_AURA_VIGOR)); + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(spellId)); + } + + if (nClass == CLASS_TYPE_DRAGONFIRE_ADEPT) + { + // Add Dragon Adept Breaths + // TODO: Replace these magic SpellID ints with consts + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2102)); // Fire Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2103)); // Fire Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2104)); // Frost Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2105)); // Electric Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2106)); // Sickness Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2108)); // Acid Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2109)); // Acid Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2111)); // Slow Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2112)); // Weakening Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2115)); // Sleep Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2116)); // Thunder Cone + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2117)); // Bahamut Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2118)); // Force Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2119)); // Paralyzation Line + shapeSpells = JsonArrayInsert(shapeSpells, JsonInt(2120)); // Tiamat Breath + } + + + SetLocalJson(oPlayer, NUI_SPELLBOOK_CLASS_SHAPES_CACHE_BASE_VAR + IntToString(nClass), shapeSpells); + return shapeSpells; +} + +json GetInvokerEssenceSpellList(int nClass, object oPlayer=OBJECT_SELF) +{ + //caching + json essenceSpells = GetLocalJson(oPlayer, NUI_SPELLBOOK_CLASS_ESSENCE_CACHE_BASE_VAR + IntToString(nClass)); + if (essenceSpells == JsonNull()) + essenceSpells = JsonArray(); + else + return essenceSpells; + + if (nClass == CLASS_TYPE_WARLOCK) + { + // Add Eldritch Essences + // TODO: Replace these magic SpellID ints with consts + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18177)); // Hideous Blow + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18189)); // Baneful Abberation + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18190)); // Baneful Beast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18191)); // Baneful Construct + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18192)); // Baneful Dragon + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18193)); // Baneful Dwarf + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18194)); // Baneful Elemental + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18195)); // Baneful Elf + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18196)); // baneful Fey + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18197)); // Baneful Giant + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18198)); // Baneful Goblinoid + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18199)); // Baneful Gnome + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18200)); // Baneful Halfling + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18201)); // Baneful Human + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18202)); // Baneful Monsterous + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18203)); // Baneful Orc + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18204)); // Baneful Outsider + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18205)); // Baneful Plant + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18206)); // Baneful Reptilian + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18207)); // Baneful Shapechanger + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18208)); // Baneful Undead + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18209)); // Baneful Vermin + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18210)); // Beshadowed Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18240)); // Bewitching Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18257)); // Binding Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18211)); // Brimstone Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18175)); // Frightful Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18176)); // Hammer Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18183)); // Sickening Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HEALING_BLAST)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_BLAST)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_BLOW)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_CHAIN)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_CONE)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_DOOM)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_GLAIVE)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_LINE)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(INVOKE_HELLFIRE_SPEAR)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18220)); // Hellrime Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18177)); // Hideous Blow + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18249)); // Hindering Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18251)); // Noxious Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18253)); // Penetrating Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18267)); // Utterdark Blast + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(18255)); // Vitriolic Blast + } + + if (nClass == CLASS_TYPE_DRAGONFIRE_ADEPT) + { + // Add the Dragonfire Adept Shapes + int spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_SHAPED_ADEPTBREATH)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_CLOUD_ADEPTBREATH)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(spellId)); + spellId = StringToInt(Get2DACache("feat", "SPELLID", FEAT_ENDURE_ADEPTBREATH)); + essenceSpells = JsonArrayInsert(essenceSpells, JsonInt(spellId)); + } + + SetLocalJson(oPlayer, NUI_SPELLBOOK_CLASS_ESSENCE_CACHE_BASE_VAR + IntToString(nClass), essenceSpells); + return essenceSpells; +} + +int JsonArrayContainsInt(json list, int item) +{ + int totalCount = JsonGetLength(list); + + int i; + for (i = 0; i < totalCount; i++) + { + if (JsonGetInt(JsonArrayGet(list, i)) == item) + return TRUE; + } + + return FALSE; +} \ No newline at end of file diff --git a/src/include/prc_nui_scd_inc.nss b/src/include/prc_nui_scd_inc.nss new file mode 100644 index 0000000..5f14b7e --- /dev/null +++ b/src/include/prc_nui_scd_inc.nss @@ -0,0 +1,98 @@ +//:://///////////////////////////////////////////// +//:: PRC Spellbook Description NUI +//:: prc_nui_scd_inc +//::////////////////////////////////////////////// +/* + This is the view for the Spell Description NUI +*/ +//::////////////////////////////////////////////// +//:: Created By: Rakiov +//:: Created On: 29.05.2005 +//::////////////////////////////////////////////// +#include "nw_inc_nui" +#include "prc_nui_consts" +#include "inc_2dacache" + +// +// CreateSpellDescriptionNUI +// Creates a Spell Description NUI mimicing the description GUI of NWN +// +// Arguments: +// oPlayer:Object the player object +// featID:int the FeatID +// spellId:int the SpellID +// realSpellId:int the RealSpellID +// +void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0); + +void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0) +{ + // look for existing window and destroy + int nPreviousToken = NuiFindWindow(OBJECT_SELF, NUI_SPELL_DESCRIPTION_WINDOW_ID); + if(nPreviousToken != 0) + { + NuiDestroy(OBJECT_SELF, nPreviousToken); + } + + // in order of accuracy for names it goes RealSpellID > SpellID > FeatID + string spellName; + if (realSpellId) + spellName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", realSpellId))); + else if (spellId) + spellName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", spellId))); + else + spellName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", featID))); + // Descriptions and Icons are accuratly stored on the feat + string spellDesc = GetStringByStrRef(StringToInt(Get2DACache("feat", "DESCRIPTION", featID))); + string spellIcon = Get2DACache("feat", "ICON", featID); + + json jRoot = JsonArray(); + json jGroup = JsonArray(); + + json jRow = JsonArray(); + + json jImage = NuiImage(JsonString(spellIcon), JsonInt(NUI_ASPECT_EXACT), JsonInt(NUI_HALIGN_LEFT), JsonInt(NUI_VALIGN_TOP)); + jImage = NuiWidth(jImage, 32.0f); + jRow = JsonArrayInsert(jRow, jImage); + jRow = NuiCol(jRow); + jGroup = JsonArrayInsert(jGroup, jRow); + + jRow = JsonArray(); + json jText = NuiText(JsonString(spellDesc), FALSE, NUI_SCROLLBARS_AUTO); + jRow = JsonArrayInsert(jRow, jText); + jRow = NuiCol(jRow); + jGroup = JsonArrayInsert(jGroup, jRow); + + jGroup = NuiRow(jGroup); + jGroup = NuiGroup(jGroup, TRUE, NUI_SCROLLBARS_NONE); + jRoot = JsonArrayInsert(jRoot, jGroup); + + jRow = JsonArray(); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + json jButton = NuiId(NuiButton(JsonString("OK")), NUI_SPELL_DESCRIPTION_OK_BUTTON); + jButton = NuiWidth(jButton, 175.0f); + jButton = NuiHeight(jButton, 48.0f); + jRow = JsonArrayInsert(jRow, jButton); + jRow = NuiRow(jRow); + + jRoot = JsonArrayInsert(jRoot, jRow); + jRoot = NuiCol(jRoot); + + + // This is the main window with jRoot as the main pane. It includes titles and parameters (more on those later) + json nui = NuiWindow(jRoot, JsonString(spellName), NuiBind("geometry"), NuiBind("resizable"), JsonBool(FALSE), NuiBind("closable"), NuiBind("transparent"), NuiBind("border")); + + // finally create it and it'll return us a non-zero token. + int nToken = NuiCreate(oPlayer, nui, NUI_SPELL_DESCRIPTION_WINDOW_ID); + + // get the geometry of the window in case we opened this before and have a + // preference for location + json geometry = NuiRect(893.0f,346.0f, 426.0f, 446.0f); + + // Set the binds to their default values + NuiSetBind(oPlayer, nToken, "geometry", geometry); + NuiSetBind(oPlayer, nToken, "resizable", JsonBool(FALSE)); + NuiSetBind(oPlayer, nToken, "closable", JsonBool(FALSE)); + NuiSetBind(oPlayer, nToken, "transparent", JsonBool(FALSE)); + NuiSetBind(oPlayer, nToken, "border", JsonBool(TRUE)); +} diff --git a/src/include/prc_racial_const.nss b/src/include/prc_racial_const.nss new file mode 100644 index 0000000..f99b8e6 --- /dev/null +++ b/src/include/prc_racial_const.nss @@ -0,0 +1,316 @@ +// Racialtypes + +//Greyhawk Races +const int RACIAL_TYPE_DERRO = 999; + +//Darksun races +const int RACIAL_TYPE_AARAKOCRA = 999; +const int RACIAL_TYPE_DS_DWARF = 999; +const int RACIAL_TYPE_DS_ELF = 999; +const int RACIAL_TYPE_DS_HALFELF = 999; +const int RACIAL_TYPE_DS_HALFGIANT = 999; +const int RACIAL_TYPE_DS_HALFLING = 999; +const int RACIAL_TYPE_MUL = 999; +const int RACIAL_TYPE_PTERRAN = 999; +const int RACIAL_TYPE_THRIKREEN = 999; + +//Dragonlance Races +const int RACIAL_TYPE_BAAZ = 999; +const int RACIAL_TYPE_BOZAK = 999; +const int RACIAL_TYPE_DARK_DWARF = 999; +const int RACIAL_TYPE_GULLY_DWARF = 999; +const int RACIAL_TYPE_IRDA = 999; +const int RACIAL_TYPE_KAGONESTI_ELF = 999; +const int RACIAL_TYPE_KAPAK = 999; +const int RACIAL_TYPE_KENDER = 999; +const int RACIAL_TYPE_KRYNN_HOGRE = 999; +const int RACIAL_TYPE_KRYNN_MINOTAUR = 999; +const int RACIAL_TYPE_SILVANESTI_ELF = 999; +const int RACIAL_TYPE_TINK_GNOME = 999; + +//Eberron Races +const int RACIAL_TYPE_WARFORGED_CHARGER = 145; +const int RACIAL_TYPE_SHIFTER = 146; +const int RACIAL_TYPE_CHANGELING = 147; +const int RACIAL_TYPE_KALASHTAR = 148; +const int RACIAL_TYPE_WARFORGED = 149; +const int RACIAL_TYPE_ZAKYA_RAKSHASA = 150; +const int RACIAL_TYPE_EMPTY_VESSEL = 154; + +//Planescape Races +const int RACIAL_TYPE_BARIAUR = 207; +const int RACIAL_TYPE_BLADELING = 195; +const int RACIAL_TYPE_CHAOND = 196; +const int RACIAL_TYPE_NATHRI = 237; +const int RACIAL_TYPE_TULADHARA = 197; +const int RACIAL_TYPE_ZENYRTHRI = 206; + +//Ravenloft Races +const int RACIAL_TYPE_HVISTANI = 146; +const int RACIAL_TYPE_VISTANI = 147; + +//:: Rokugan/Kara-Tur Races +const int RACIAL_TYPE_TASLOI = 140; +const int RACIAL_TYPE_SPIRIT_FOLK = 243; +const int RACIAL_TYPE_KOROBKURU = 245; +const int RACIAL_TYPE_NEZUMI = 246; + + +//Spelljammer Races +const int RACIAL_TYPE_SCRO = 182; +const int RACIAL_TYPE_XIXCHIL = 181; + +// DMG +const int RACIAL_TYPE_PLANT = 52; + +//Draconic Races +const int RACIAL_TYPE_DRAGONBORN = 128; +const int RACIAL_TYPE_SPELLSCALE = 129; +const int RACIAL_TYPE_REDSPAWN_ARCANISS = 72; +const int RACIAL_TYPE_SPIRETOPDRAGON = 77; + +//Fey-type Races +const int RACIAL_TYPE_BRALANI = 159; +const int RACIAL_TYPE_BROWNIE = 53; +const int RACIAL_TYPE_GRIG = 133; +const int RACIAL_TYPE_JAEBRIN = 78; +const int RACIAL_TYPE_NIXIE = 134; +const int RACIAL_TYPE_NYMPH = 135; +const int RACIAL_TYPE_PIXIE = 226; +const int RACIAL_TYPE_SATYR = 136; +const int RACIAL_TYPE_HYBSIL = 66; + +//Outsider Races +const int RACIAL_TYPE_ASURA = 80; +const int RACIAL_TYPE_AZER = 227; +const int RACIAL_TYPE_BUOMMANS = 238; +const int RACIAL_TYPE_DJINNI = 81; +const int RACIAL_TYPE_EFREETI = 82; +const int RACIAL_TYPE_FORMIAN = 232; +const int RACIAL_TYPE_GITHYANKI = 222; +const int RACIAL_TYPE_GITHZERAI = 223; +const int RACIAL_TYPE_GLOAMING = 83; +const int RACIAL_TYPE_HOUND_ARCHON = 84; +const int RACIAL_TYPE_KHAASTA = 94; +const int RACIAL_TYPE_JANNI = 85; +const int RACIAL_TYPE_MEPHIT_AIR = 86; +const int RACIAL_TYPE_MEPHIT_EARTH = 87; +const int RACIAL_TYPE_MEPHIT_FIRE = 88; +const int RACIAL_TYPE_MEPHIT_WATER = 89; +const int RACIAL_TYPE_MEPHLING_AIR = 90; +const int RACIAL_TYPE_MEPHLING_EARTH = 91; +const int RACIAL_TYPE_MEPHLING_FIRE = 92; +const int RACIAL_TYPE_MEPHLING_WATER = 93; +const int RACIAL_TYPE_NERAPHIM = 235; +const int RACIAL_TYPE_RAKSHASA = 224; +const int RACIAL_TYPE_SALAMANDER = 95; +const int RACIAL_TYPE_SHADE = 210; +const int RACIAL_TYPE_SPIKER = 239; +const int RACIAL_TYPE_WILDREN = 240; +const int RACIAL_TYPE_NAZTHARUNE_RAKSHASA = 96; +const int RACIAL_TYPE_RETH_DEKALA = 67; + +//Planetouched Races +const int RACIAL_TYPE_AASIMAR = 198; +const int RACIAL_TYPE_AIR_GEN = 199; +const int RACIAL_TYPE_EARTH_GEN = 200; +const int RACIAL_TYPE_FEYRI = 201; +const int RACIAL_TYPE_FIRE_GEN = 202; +const int RACIAL_TYPE_MORTIF = 132; +const int RACIAL_TYPE_TANARUKK = 203; +const int RACIAL_TYPE_TIEFLING = 204; +const int RACIAL_TYPE_WATER_GEN = 205; +const int RACIAL_TYPE_SHADOWSWYFT = 236; + +//Serpent Kingdom and Reptillian Races +const int RACIAL_TYPE_ABOM_YUAN = 228; +const int RACIAL_TYPE_ASABI = 999; +const int RACIAL_TYPE_ASABI_STINGTAIL = 999; +const int RACIAL_TYPE_KUOTOA = 999; +const int RACIAL_TYPE_LIZARDFOLK = 219; +const int RACIAL_TYPE_LIZARDKING = 68; +const int RACIAL_TYPE_MEDUSA = 69; +const int RACIAL_TYPE_OPHIDIAN = 999; +const int RACIAL_TYPE_POISON_DUSK = 248; +const int RACIAL_TYPE_PURE_YUAN = 220; +const int RACIAL_TYPE_SAHUAGIN = 71; +const int RACIAL_TYPE_SK_YUANTI = 233; +const int RACIAL_TYPE_TREN = 72; +const int RACIAL_TYPE_VILETOOTH_LIZARDFOLK = 112; +const int RACIAL_TYPE_MUCKDWELLER = 74; + +//Underdark Races +const int RACIAL_TYPE_ARANEA = 75; +const int RACIAL_TYPE_BEHOLDER = -1; +const int RACIAL_TYPE_DRIDER = 50; +const int RACIAL_TYPE_GRIMLOCK = 77; +const int RACIAL_TYPE_ILLITHID = 225; +const int RACIAL_TYPE_IMASKARI = 230; +const int RACIAL_TYPE_SLYTH = 78; +const int RACIAL_TYPE_TROGLODYTE = 234; +const int RACIAL_TYPE_UMBER_HULK = 79; +const int RACIAL_TYPE_GLOURA = 73; + +//Other Monsterous Races +const int RACIAL_TYPE_CENTAUR = 208; +const int RACIAL_TYPE_CATFOLK = 209; +const int RACIAL_TYPE_DIABOLUS = 113; +const int RACIAL_TYPE_DIOPSID = 114; +const int RACIAL_TYPE_DRAGONKIN = 58; +const int RACIAL_TYPE_ETTERCAP = 73; +const int RACIAL_TYPE_FIRENEWT = 59; +const int RACIAL_TYPE_GARGOYLE = 185; +const int RACIAL_TYPE_KIRLANAN = 60; +const int RACIAL_TYPE_LUPIN = 186; +const int RACIAL_TYPE_PTERAFOLK = 61; +const int RACIAL_TYPE_RAPTORAN = 130; +const int RACIAL_TYPE_SAURIAL_BLADEBACK = 62; +const int RACIAL_TYPE_SAURIAL_FINHEAD = 63; +const int RACIAL_TYPE_SAURIAL_FLYER = 64; +const int RACIAL_TYPE_SAURIAL_HORNHEAD = 65; +const int RACIAL_TYPE_TORTLE = 118; +const int RACIAL_TYPE_VOLODNI = 131; +const int RACIAL_TYPE_WEMIC = 51; +const int RACIAL_TYPE_ARKAMOI = 68; +const int RACIAL_TYPE_LASHEMOI = 69; +const int RACIAL_TYPE_TURLEMOI = 70; +const int RACIAL_TYPE_HADRIMOI = 71; + +// XPH +const int RACIAL_TYPE_DROMITE = 249; +const int RACIAL_TYPE_ELAN = 250; +const int RACIAL_TYPE_MAENADS = 252; +const int RACIAL_TYPE_PH_HALFGIANT = 251; +const int RACIAL_TYPE_XEPH = 253; + +//Unapproachable East +const int RACIAL_TYPE_HAGSPAWN = 58; +const int RACIAL_TYPE_TAER = 59; + +//Races of the Wild +const int RACIAL_TYPE_KILLOREN = 244; + +// Underdark +const int RACIAL_TYPE_CHITINE = 76; + +//Frostburn +const int RACIAL_TYPE_NEANDERTHAL = 61; +const int RACIAL_TYPE_FROST_FOLK = 62; +const int RACIAL_TYPE_ULDRA = 63; + +//Magic of Incarnum +const int RACIAL_TYPE_AZURIN = 130; +const int RACIAL_TYPE_DUSKLING = 129; +const int RACIAL_TYPE_RILKAN = 128; +const int RACIAL_TYPE_SKARN = 127; + +//Races of Destiny +const int RACIAL_TYPE_ILLUMIAN = 120; +const int RACIAL_TYPE_MONGRELFOLK = 121; +const int RACIAL_TYPE_SHARAKIM = 122; +const int RACIAL_TYPE_UNDERFOLK = 123; +const int RACIAL_TYPE_SKULK = 124; +const int RACIAL_TYPE_DOPPELGANGER = 125; + +//Races of Stone +const int RACIAL_TYPE_GOLIATH = 55; +const int RACIAL_TYPE_FERAL_GARGUN = 56; +const int RACIAL_TYPE_STONECHILD = 57; + +//Sandstorm +const int RACIAL_TYPE_ASHERATI = 115; +const int RACIAL_TYPE_BHUKA = 116; +const int RACIAL_TYPE_MARRULURK = 117; +const int RACIAL_TYPE_CRUCIAN = 118; +const int RACIAL_TYPE_MARRUSAULT = 119; +const int RACIAL_TYPE_MARRUTACT = 120; + +//Stormwrack +const int RACIAL_TYPE_DARFELLAN = 117; +const int RACIAL_TYPE_HADOZEE = 180; + +//Champions of Ruin +const int RACIAL_TYPE_KRINTH = 54; +const int RACIAL_TYPE_EXTAMINAAR = 64; + +// Tome of Magic +const int RACIAL_TYPE_KARSITE = 65; + +/* Standard Races */ + +//:: Human +const int RACIAL_TYPE_SILVERBROW_HUMAN = 110; + +//:: Elf +const int RACIAL_TYPE_AQELF = 161; +const int RACIAL_TYPE_AVARIEL = 162; +const int RACIAL_TYPE_DROW_FEMALE = 163; +const int RACIAL_TYPE_DROW_MALE = 164; +const int RACIAL_TYPE_FORESTLORD_ELF = 107; +const int RACIAL_TYPE_HALFDROW = 183; +const int RACIAL_TYPE_SNOW_ELF = 165; +const int RACIAL_TYPE_STAR_ELF = 160; +const int RACIAL_TYPE_SUN_ELF = 166; +const int RACIAL_TYPE_WILD_ELF = 167; +const int RACIAL_TYPE_WOOD_ELF = 168; +const int RACIAL_TYPE_GREY_ELF = 169; + +//:: Dwarf +const int RACIAL_TYPE_ARC_DWARF = 151; +const int RACIAL_TYPE_DREAM_DWARF = 157; +const int RACIAL_TYPE_FIREBLOOD_DWARF = 106; +const int RACIAL_TYPE_FROST_DWARF = 158; +const int RACIAL_TYPE_GOLD_DWARF = 152; +const int RACIAL_TYPE_DUERGAR = 153; +const int RACIAL_TYPE_GLACIER_DWARF = 154; +const int RACIAL_TYPE_URDINNIR = 155; +const int RACIAL_TYPE_WILD_DWARF = 156; + +//:: Gnome +const int RACIAL_TYPE_CHAOS_GNOME = 177; +const int RACIAL_TYPE_DEEP_GNOME = 174; +const int RACIAL_TYPE_FIRE_GNOME = 173; +const int RACIAL_TYPE_FOR_GNOME = 175; +const int RACIAL_TYPE_ICE_GNOME = 178; +const int RACIAL_TYPE_ROCK_GNOME = 176; +const int RACIAL_TYPE_STONEHUNTER_GNOME = 105; +const int RACIAL_TYPE_SVIRFNEBLIN = 174; +const int RACIAL_TYPE_WHISPER_GNOME = 179; + +//:: Halfling +const int RACIAL_TYPE_DEEP_HALFLING = 194; +const int RACIAL_TYPE_GHOSTWISE_HALFLING = 190; +const int RACIAL_TYPE_GLIMMERSKIN_HALFING = 109; +const int RACIAL_TYPE_JARREN = 189; +const int RACIAL_TYPE_SHOAL_HALFLING = 188; +const int RACIAL_TYPE_STRONGHEART_HALFLING = 192; +const int RACIAL_TYPE_TALLFELLOW_HALFLING = 193; +const int RACIAL_TYPE_TUNDRA_HALFLING = 191; + +//:: Goblinoid +const int RACIAL_TYPE_BLUE_GOBLIN = 143; +const int RACIAL_TYPE_DEKANTER = 142; +const int RACIAL_TYPE_GOBLIN = 213; +const int RACIAL_TYPE_SNOW_GOBLIN = 141; +const int RACIAL_TYPE_BUGBEAR = 217; +const int RACIAL_TYPE_HOBGOBLIN = 221; +const int RACIAL_TYPE_SUNSCORCH_HOBGOBLIN = 111; +const int RACIAL_TYPE_VARAG = 232; +const int RACIAL_TYPE_HOBGOBLIN_WARSOUL = 233; + +//:: "Greenskin" Races +const int RACIAL_TYPE_FLIND = 211; +const int RACIAL_TYPE_FROSTBLOOD_ORC = 108; +const int RACIAL_TYPE_GNOLL = 216; +const int RACIAL_TYPE_GRAYORC = 184; +const int RACIAL_TYPE_HALFOGRE = 229; +const int RACIAL_TYPE_KOBOLD = 215; +const int RACIAL_TYPE_MINOTAUR = 218; +const int RACIAL_TYPE_OGRE = 212; +const int RACIAL_TYPE_OMAGE = 211; +const int RACIAL_TYPE_OROG = 187; +const int RACIAL_TYPE_ORC = 214; +const int RACIAL_TYPE_TROLL = 231; + + + diff --git a/src/include/prc_shifter_info.nss b/src/include/prc_shifter_info.nss new file mode 100644 index 0000000..2a9ed78 --- /dev/null +++ b/src/include/prc_shifter_info.nss @@ -0,0 +1,1436 @@ + +//:: Updated for .35 by Jaysyn 2023/03/10 + +#include "prc_inc_function" +#include "inc_nwnx_funcs" + +//:: Test Void +//void main (){} + + +const int DEBUG_NATURAL_AC_CALCULATION = FALSE; +int MAX_BONUS = GetPRCSwitch(PRC_PNP_SHIFTER_BONUS); +const int MAX_PENALTY = 10; +int NWNX_STR_LIMIT = 100 - MAX_BONUS; + +struct _prc_inc_ability_info_struct{ + int nTemplateSTR; + int nTemplateDEX; + int nTemplateCON; + + int nShifterSTR; + int nShifterDEX; + int nShifterCON; + + int nDeltaSTR; + int nDeltaDEX; + int nDeltaCON; + + int nItemSTR; + int nItemDEX; + int nItemCON; + + int nExtraSTR; + int nExtraDEX; + int nExtraCON; + + int nItemDeltaSTR; + int nItemDeltaDEX; + int nItemDeltaCON; +}; + +//TODO: also count item penalties? +struct _prc_inc_ability_info_struct _prc_inc_CountItemAbilities(object oCreature) +{ + struct _prc_inc_ability_info_struct rInfoStruct; + rInfoStruct.nItemSTR = 0; + rInfoStruct.nItemDEX = 0; + rInfoStruct.nItemCON = 0; + + object oItem; + itemproperty iProperty; + int nSlot; + for(nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++) + { + switch (nSlot) + { + case INVENTORY_SLOT_CARMOUR: + case INVENTORY_SLOT_CWEAPON_R: + case INVENTORY_SLOT_CWEAPON_L: + case INVENTORY_SLOT_CWEAPON_B: + break; + + default: + { + oItem = GetItemInSlot(nSlot, oCreature); + if (GetIsObjectValid(oItem)) + { + iProperty = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(iProperty)) + { + if (GetItemPropertyType(iProperty) == ITEM_PROPERTY_ABILITY_BONUS && + GetItemPropertyDurationType(iProperty) == DURATION_TYPE_PERMANENT + ) + { + int nSubType = GetItemPropertySubType(iProperty); + int nCostTableValue = GetItemPropertyCostTableValue(iProperty); + if (nSubType == IP_CONST_ABILITY_STR) + rInfoStruct.nItemSTR += nCostTableValue; + else if (nSubType == IP_CONST_ABILITY_DEX) + rInfoStruct.nItemDEX += nCostTableValue; + else if (nSubType == IP_CONST_ABILITY_CON) + rInfoStruct.nItemCON += nCostTableValue; + } + // Next item property. + iProperty = GetNextItemProperty(oItem); + } + } + } + } + } + return rInfoStruct; +} + +struct _prc_inc_ability_info_struct _prc_inc_shifter_GetAbilityInfo(object oTemplate, object oShifter) +{ + int bFuncs = GetPRCSwitch(PRC_NWNX_FUNCS); + + //Initialize with item ability bonuses + + struct _prc_inc_ability_info_struct rInfoStruct = _prc_inc_CountItemAbilities(oShifter); + + //Get template creature abilities + + rInfoStruct.nTemplateSTR = GetAbilityScore(oTemplate, ABILITY_STRENGTH, TRUE); + rInfoStruct.nTemplateDEX = GetAbilityScore(oTemplate, ABILITY_DEXTERITY, TRUE); + rInfoStruct.nTemplateCON = GetAbilityScore(oTemplate, ABILITY_CONSTITUTION, TRUE); + //TODO: merge in "Ability Bonus: Strength" from item property from template hide here (not too important, as not many templates use this) + //TODO: merge in "Ability Bonus: Dexterity" from item property from template hide here (not too important, as not many templates use this) + //TODO: merge in "Ability Bonus: Constitution" from item property from template hide here (not too important, as not many templates use this) + + //Calculate how they compare to the shifter's abilities + + rInfoStruct.nShifterSTR = GetAbilityScore(oShifter, ABILITY_STRENGTH, TRUE); + rInfoStruct.nShifterDEX = GetAbilityScore(oShifter, ABILITY_DEXTERITY, TRUE); + rInfoStruct.nShifterCON = GetAbilityScore(oShifter, ABILITY_CONSTITUTION, TRUE); + + rInfoStruct.nDeltaSTR = rInfoStruct.nTemplateSTR - rInfoStruct.nShifterSTR; + rInfoStruct.nDeltaDEX = rInfoStruct.nTemplateDEX - rInfoStruct.nShifterDEX; + rInfoStruct.nDeltaCON = rInfoStruct.nTemplateCON - rInfoStruct.nShifterCON; + + //Handle stat boosting items + if (rInfoStruct.nItemSTR > MAX_BONUS) + rInfoStruct.nItemSTR = MAX_BONUS; + else if (rInfoStruct.nItemSTR < -MAX_PENALTY) + rInfoStruct.nItemSTR = -MAX_PENALTY; + + if (rInfoStruct.nItemDEX > MAX_BONUS) + rInfoStruct.nItemDEX = MAX_BONUS; + else if (rInfoStruct.nItemDEX < -MAX_PENALTY) + rInfoStruct.nItemDEX = -MAX_PENALTY; + + if (rInfoStruct.nItemCON > MAX_BONUS) + rInfoStruct.nItemCON = MAX_BONUS; + else if (rInfoStruct.nItemCON < -MAX_PENALTY) + rInfoStruct.nItemCON = -MAX_PENALTY; + + //Handle changes that exceed bonus or penalty caps + + rInfoStruct.nItemDeltaSTR = rInfoStruct.nDeltaSTR + rInfoStruct.nItemSTR; + if (bFuncs) + { + //NWNX boosts aren't capped, so we don't need to handle caps, generally speaking. + rInfoStruct.nExtraSTR = 0; + + //However, due to a Bioware issue, if STR, including bonuses, goes greater than 100, + //the amount of weight the PC can carry drops to 0. So, cap STR to make sure this doesn't happen. + + if (rInfoStruct.nTemplateSTR > NWNX_STR_LIMIT) + { + rInfoStruct.nExtraSTR = rInfoStruct.nTemplateSTR - NWNX_STR_LIMIT; + rInfoStruct.nTemplateSTR = NWNX_STR_LIMIT; + rInfoStruct.nDeltaSTR = rInfoStruct.nTemplateSTR - rInfoStruct.nShifterSTR; + } + } + else if (rInfoStruct.nItemDeltaSTR > MAX_BONUS) + rInfoStruct.nExtraSTR = rInfoStruct.nItemDeltaSTR - MAX_BONUS; + else if(rInfoStruct.nItemDeltaSTR < -MAX_PENALTY) + rInfoStruct.nExtraSTR = rInfoStruct.nItemDeltaSTR + MAX_PENALTY; + + rInfoStruct.nItemDeltaDEX = rInfoStruct.nDeltaDEX + rInfoStruct.nItemDEX; + if (bFuncs) + rInfoStruct.nExtraDEX = 0; //NWNX boosts aren't capped, so we don't need to handle caps + else if (rInfoStruct.nItemDeltaDEX > MAX_BONUS) + rInfoStruct.nExtraDEX = rInfoStruct.nItemDeltaDEX - MAX_BONUS; + else if(rInfoStruct.nItemDeltaDEX < -MAX_PENALTY) + rInfoStruct.nExtraDEX = rInfoStruct.nItemDeltaDEX + MAX_PENALTY; + + rInfoStruct.nItemDeltaCON = rInfoStruct.nDeltaCON + rInfoStruct.nItemCON; + if (bFuncs) + rInfoStruct.nExtraCON = 0; //NWNX boosts aren't capped, so we don't need to handle caps + else if (rInfoStruct.nItemDeltaCON > MAX_BONUS) + rInfoStruct.nExtraCON = rInfoStruct.nItemDeltaCON - MAX_BONUS; + else if(rInfoStruct.nItemDeltaCON < -MAX_PENALTY) + rInfoStruct.nExtraCON = rInfoStruct.nItemDeltaCON + MAX_PENALTY; + + return rInfoStruct; +} + +int _prc_inc_GetItemACBonus(object oItem) +{ + int nArmorBonus = 0; + itemproperty iProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProp)) + { + if(GetItemPropertyType(iProp) == ITEM_PROPERTY_AC_BONUS && GetItemPropertyDurationType(iProp) == DURATION_TYPE_PERMANENT) + nArmorBonus = PRCMax(nArmorBonus, GetItemPropertyCostTableValue(iProp)); //TODO: pick the biggest? the first? stack them? + iProp = GetNextItemProperty(oItem); + } + return nArmorBonus; +} + +int _prc_inc_GetArmorMaxDEXBonus(object oArmor, int nMaxDexACBonus = 100) +{ + if (GetIsObjectValid(oArmor)) + { + int nArmorAC = GetItemACValue(oArmor) - _prc_inc_GetItemACBonus(oArmor); //Exclude magical AC bonus to figure out armor type + switch(nArmorAC) + { + //TODO: CAN THESE BE LOOKED UP IN A 2DA OR SOMEWHERE? + case 8: case 7: case 6: + nMaxDexACBonus = 1; break; + case 5: + nMaxDexACBonus = 2; break; + case 4: case 3: + nMaxDexACBonus = 4; break; + case 2: + nMaxDexACBonus = 6; break; + case 1: + nMaxDexACBonus = 8; break; + } + } + return nMaxDexACBonus; +} + +struct _prc_inc_ac_info_struct{ + int nArmorBase; + int nArmorBonus; + + int nShieldBase; + int nShieldBonus; + + int nDodgeBonus; + int nNaturalBonus; + int nDeflectionBonus; + + int nDEXBonus; +}; + +struct _prc_inc_ac_info_struct _prc_inc_ACInfo(object oTemplate) +{ + struct _prc_inc_ac_info_struct ac_info; + + object oArmorItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTemplate); + ac_info.nArmorBonus = _prc_inc_GetItemACBonus(oArmorItem); + ac_info.nArmorBase = GetItemACValue(oArmorItem) - ac_info.nArmorBonus; + + ac_info.nDodgeBonus = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BOOTS, oTemplate)); + ac_info.nNaturalBonus = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_NECK, oTemplate)); + + ac_info.nDeflectionBonus = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_HEAD, oTemplate)); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CLOAK, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTRING, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BELT, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_ARROWS, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BULLETS, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_BOLTS, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTemplate))); + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate))); + + object oOffHandItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTemplate); + ac_info.nShieldBase = 0; + ac_info.nShieldBonus = 0; + switch (GetBaseItemType(oOffHandItem)) + { + case BASE_ITEM_SMALLSHIELD: + ac_info.nShieldBase = 1; + ac_info.nShieldBonus = GetItemACValue(oOffHandItem) - ac_info.nShieldBase; + break; + case BASE_ITEM_LARGESHIELD: + ac_info.nShieldBase = 2; + ac_info.nShieldBonus = GetItemACValue(oOffHandItem) - ac_info.nShieldBase; + break; + case BASE_ITEM_TOWERSHIELD: + ac_info.nShieldBase = 3; + ac_info.nShieldBonus = GetItemACValue(oOffHandItem) - ac_info.nShieldBase; + break; + default: //A weapon + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(oOffHandItem)); + break; + } + + object oArmsItem = GetItemInSlot(INVENTORY_SLOT_ARMS, oTemplate); + switch (GetBaseItemType(oArmsItem)) + { + case BASE_ITEM_BRACER: + ac_info.nShieldBonus = PRCMax(ac_info.nShieldBonus, GetItemACValue(oArmsItem)); + break; + case BASE_ITEM_GLOVES: + default: + ac_info.nDeflectionBonus = PRCMax(ac_info.nDeflectionBonus, GetItemACValue(oArmsItem)); + break; + } + + if (ac_info.nArmorBonus > 20) + ac_info.nArmorBonus = 20; + if (ac_info.nDodgeBonus > 20) + ac_info.nDodgeBonus = 20; + if (ac_info.nNaturalBonus > 20) + ac_info.nNaturalBonus = 20; + if (ac_info.nDeflectionBonus > 20) + ac_info.nDeflectionBonus = 20; + if (ac_info.nShieldBonus > 20) + ac_info.nShieldBonus = 20; + + ac_info.nDEXBonus = PRCMin(GetAbilityModifier(ABILITY_DEXTERITY, oTemplate), _prc_inc_GetArmorMaxDEXBonus(oArmorItem)); + //TODO: make sure this isn't < 0? + + return ac_info; +} + +//Estimate natural AC of the creature oTemplate +int _prc_inc_CreatureNaturalAC(object oTemplate) +{ + struct _prc_inc_ac_info_struct ac_info = _prc_inc_ACInfo(oTemplate); + + //TODO: GetAC(oTemplate) often returns an AC different (usually higher) than the combat debugging log indicates it should be. + //Note that combat debugging doesn't report DEX bonus, Monk WIS bonus, etc.; where does this come in? + int nNaturalAC = GetAC(oTemplate) + - 10 // Adjust for base AC + - ac_info.nDEXBonus // And Dex bonus + - ac_info.nArmorBase // Etc... + - ac_info.nArmorBonus + - ac_info.nDodgeBonus + - ac_info.nNaturalBonus + - ac_info.nDeflectionBonus + - ac_info.nShieldBase + - ac_info.nShieldBonus; + + //TODO: + //Subtract +4 Dodge bonus if template has Haste? + //Subtract +1 AC / each 5 points of the Tumble skill? + //Subtract Monk AC from level progression? + //Subtract WIS AC if Monk/Ninja, etc.? + //Make sure nNaturalAC is not < 0 (it was for me once using the old method of calculation, which is why I created this new one) + + if (DEBUG_NATURAL_AC_CALCULATION || DEBUG) + { + DoDebug("_prc_inc_CreatureNaturalAC: total ac: " + IntToString(GetAC(oTemplate))); + DoDebug("_prc_inc_CreatureNaturalAC: base ac: " + IntToString(10)); + DoDebug("_prc_inc_CreatureNaturalAC: armor base ac: " + IntToString(ac_info.nArmorBase)); + DoDebug("_prc_inc_CreatureNaturalAC: armor bonus ac: " + IntToString(ac_info.nArmorBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: shield base ac: " + IntToString(ac_info.nShieldBase)); + DoDebug("_prc_inc_CreatureNaturalAC: shield bonus ac: " + IntToString(ac_info.nShieldBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: dodge bonus ac: " + IntToString(ac_info.nDodgeBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: natural bonus ac: " + IntToString(ac_info.nNaturalBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: deflection bonus ac: " + IntToString(ac_info.nDeflectionBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: dex ac: " + IntToString(ac_info.nDEXBonus)); + DoDebug("_prc_inc_CreatureNaturalAC: calculated natural ac: " + IntToString(nNaturalAC)); + } + + //TODO: combat debugging shows actual natural AC (as well as other type); compare with that to debug. + + return nNaturalAC; +} + +int _prc_inc_GetFeatDeathAttackLevel(int nFeat) +{ + switch(nFeat) + { + case FEAT_PRESTIGE_DEATH_ATTACK_1: return 1; + case FEAT_PRESTIGE_DEATH_ATTACK_2: return 2; + case FEAT_PRESTIGE_DEATH_ATTACK_3: return 3; + case FEAT_PRESTIGE_DEATH_ATTACK_4: return 4; + case FEAT_PRESTIGE_DEATH_ATTACK_5: return 5; + case FEAT_PRESTIGE_DEATH_ATTACK_6: return 6; + case FEAT_PRESTIGE_DEATH_ATTACK_7: return 7; + case FEAT_PRESTIGE_DEATH_ATTACK_8: return 8; + case FEAT_PRESTIGE_DEATH_ATTACK_9: return 9; + case FEAT_PRESTIGE_DEATH_ATTACK_10: return 10; + case FEAT_PRESTIGE_DEATH_ATTACK_11: return 11; + case FEAT_PRESTIGE_DEATH_ATTACK_12: return 12; + case FEAT_PRESTIGE_DEATH_ATTACK_13: return 13; + case FEAT_PRESTIGE_DEATH_ATTACK_14: return 14; + case FEAT_PRESTIGE_DEATH_ATTACK_15: return 15; + case FEAT_PRESTIGE_DEATH_ATTACK_16: return 16; + case FEAT_PRESTIGE_DEATH_ATTACK_17: return 17; + case FEAT_PRESTIGE_DEATH_ATTACK_18: return 18; + case FEAT_PRESTIGE_DEATH_ATTACK_19: return 19; + case FEAT_PRESTIGE_DEATH_ATTACK_20: return 20; + } + return 0; +} + +int _prc_inc_GetHasFeat(object oTemplate, int nFeat) +{ + //If oTemplate has the feat FEAT_SNEAK_ATTACK_10, GetHasFeat() always says + //it has FEAT_PRESTIGE_DEATH_ATTACK_1 through FEAT_PRESTIGE_DEATH_ATTACK_20, + //whether it actually does or not. Work around this as follows: + int nSuppress=0; + int FEAT_SNEAK_ATTACK_10 = 353; + if(GetHasFeat(FEAT_SNEAK_ATTACK_10, oTemplate)) + { + int nFeatDeathAttackLevel = _prc_inc_GetFeatDeathAttackLevel(nFeat); + if(nFeatDeathAttackLevel) + { + int nActualDeathAttackLevel = 0; + nActualDeathAttackLevel += (GetLevelByClass(CLASS_TYPE_ASSASSIN, oTemplate) + 1) / 2; + //TODO: Add other classes here? OR use GetTotalSneakAttackDice(), etc. from prc_inc_sneak instead? + if(nFeatDeathAttackLevel > nActualDeathAttackLevel) + nSuppress = 1; + } + } + + return GetHasFeat(nFeat, oTemplate) && !nSuppress; +} + +int _prc_inc_shifting_GetIsCreatureHarmless(object oTemplate) +{ + return GetChallengeRating(oTemplate) < 1.0; +} + +int _prc_inc_shifting_CharacterLevelRequirement(object oTemplate) +{ + return GetPRCSwitch(PNP_SHFT_USECR) ? FloatToInt(GetChallengeRating(oTemplate)) : GetHitDice(oTemplate); +} + +int _prc_inc_shifting_ShifterLevelRequirement(object oTemplate) +{ + int nRacialType = MyPRCGetRacialType(oTemplate); + int nSize = PRCGetCreatureSize(oTemplate); + int nLevelRequired = 0; + + // Size tests + if(nSize >= CREATURE_SIZE_HUGE) + nLevelRequired = PRCMax(nLevelRequired, 7); + if(nSize == CREATURE_SIZE_LARGE) + nLevelRequired = PRCMax(nLevelRequired, 3); + if(nSize == CREATURE_SIZE_MEDIUM) + nLevelRequired = PRCMax(nLevelRequired, 1); + if(nSize == CREATURE_SIZE_SMALL) + nLevelRequired = PRCMax(nLevelRequired, 1); + if(nSize <= CREATURE_SIZE_TINY) + nLevelRequired = PRCMax(nLevelRequired, 3); + + // Type tests + if(nRacialType == RACIAL_TYPE_OUTSIDER) + nLevelRequired = PRCMax(nLevelRequired, 9); + if(nRacialType == RACIAL_TYPE_ELEMENTAL) + nLevelRequired = PRCMax(nLevelRequired, 9); + if(nRacialType == RACIAL_TYPE_CONSTRUCT) + nLevelRequired = PRCMax(nLevelRequired, 8); + if(nRacialType == RACIAL_TYPE_UNDEAD) + nLevelRequired = PRCMax(nLevelRequired, 8); + if(nRacialType == RACIAL_TYPE_DRAGON) + nLevelRequired = PRCMax(nLevelRequired, 7); + if(nRacialType == RACIAL_TYPE_ABERRATION) + nLevelRequired = PRCMax(nLevelRequired, 6); + if(nRacialType == RACIAL_TYPE_OOZE) + nLevelRequired = PRCMax(nLevelRequired, 6); + if(nRacialType == RACIAL_TYPE_MAGICAL_BEAST) + nLevelRequired = PRCMax(nLevelRequired, 5); + if(nRacialType == RACIAL_TYPE_GIANT) + nLevelRequired = PRCMax(nLevelRequired, 4); + if(nRacialType == RACIAL_TYPE_VERMIN) + nLevelRequired = PRCMax(nLevelRequired, 4); + if(nRacialType == RACIAL_TYPE_BEAST) + nLevelRequired = PRCMax(nLevelRequired, 3); + if(nRacialType == RACIAL_TYPE_ANIMAL) + nLevelRequired = PRCMax(nLevelRequired, 2); + if(nRacialType == RACIAL_TYPE_HUMANOID_MONSTROUS) + nLevelRequired = PRCMax(nLevelRequired, 2); + if(nRacialType == RACIAL_TYPE_DWARF || + nRacialType == RACIAL_TYPE_ELF || + nRacialType == RACIAL_TYPE_GNOME || + nRacialType == RACIAL_TYPE_HUMAN || + nRacialType == RACIAL_TYPE_HALFORC || + nRacialType == RACIAL_TYPE_HALFELF || + nRacialType == RACIAL_TYPE_HALFLING || + nRacialType == RACIAL_TYPE_HUMANOID_ORC || + nRacialType == RACIAL_TYPE_HUMANOID_REPTILIAN + ) + nLevelRequired = PRCMax(nLevelRequired, 1); + + return nLevelRequired; +} + +int _prc_inc_shifting_GetCanFormCast(object oTemplate) +{ + int nRacialType = MyPRCGetRacialType(oTemplate); + + // Need to have hands, and the ability to speak + + switch (nRacialType) + { + case RACIAL_TYPE_ABERRATION: + case RACIAL_TYPE_ANIMAL: + case RACIAL_TYPE_BEAST: + case RACIAL_TYPE_MAGICAL_BEAST: + case RACIAL_TYPE_VERMIN: + case RACIAL_TYPE_OOZE: +// case RACIAL_TYPE_PLANT: + // These forms can't cast spells + return FALSE; + case RACIAL_TYPE_DWARF: + case RACIAL_TYPE_ELF: + case RACIAL_TYPE_GNOME: + case RACIAL_TYPE_HALFLING: + case RACIAL_TYPE_HALFELF: + case RACIAL_TYPE_HALFORC: + case RACIAL_TYPE_HUMAN: + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_DRAGON: + case RACIAL_TYPE_HUMANOID_GOBLINOID: + case RACIAL_TYPE_HUMANOID_MONSTROUS: + case RACIAL_TYPE_HUMANOID_ORC: + case RACIAL_TYPE_HUMANOID_REPTILIAN: + case RACIAL_TYPE_ELEMENTAL: + case RACIAL_TYPE_FEY: + case RACIAL_TYPE_GIANT: + case RACIAL_TYPE_OUTSIDER: + case RACIAL_TYPE_SHAPECHANGER: + case RACIAL_TYPE_UNDEAD: + // Break and go return TRUE at the end of the function + break; + + default:{ + if(DEBUG) DoDebug("prc_inc_shifting: _GetCanFormCast(): Unknown racial type: " + IntToString(nRacialType)); + } + } + + return TRUE; +} + +string _prc_inc_AbilityTypeString(int nAbilityType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_abilities", "Name", nAbilityType))); +} + +string _prc_inc_AlignmentGroupString(int nAlignmentGroup) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_aligngrp", "Name", nAlignmentGroup))); +} + +string _prc_inc_BonusFeatTypeString(int nBonusFeatType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_feats", "Name", nBonusFeatType))); +} + +string _prc_inc_ClassTypeString(int nClassType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", nClassType))); +} + +string _prc_inc_CostTableEntryString(int nCostTable, int nCostTableValue) +{ + string sCostTableName = Get2DACache("iprp_costtable", "Name", nCostTable); + if(sCostTableName == "" || sCostTableName == "****") + return "??? (" + IntToString(nCostTable) + " / " + IntToString(nCostTableValue) + ")"; + string sCostTableEntry = Get2DACache(sCostTableName, "Name", nCostTableValue); + if(sCostTableEntry == "" || sCostTableEntry == "****") + return "??? (" + sCostTableName + " / " + IntToString(nCostTableValue) + ")"; + return GetStringByStrRef(StringToInt(sCostTableEntry)); +} + +string _prc_inc_DamageTypeString(int nDamageType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_damagetype", "Name", nDamageType))); +} + +string _prc_inc_ImmunityTypeString(int nImmunityType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_immunity", "Name", nImmunityType))); +} + +string _prc_inc_OnHitSpellTypeString(int nOnHitSpellType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_onhitspell", "Name", nOnHitSpellType))); +} + +string _prc_inc_OnHitTypeString(int nOnHitSpellType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_onhit", "Name", nOnHitSpellType))); +} + +string _prc_inc_OnMonsterHitTypeString(int nOnMonsterHitType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_monsterhit", "Name", nOnMonsterHitType))); +} + +string _prc_inc_SavingThrowElementTypeString(int nSavingThrowType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_saveelement", "Name", nSavingThrowType))); +} + +string _prc_inc_SavingThrowTypeString(int nSavingThrowType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_savingthrow", "Name", nSavingThrowType))); +} + +string _prc_inc_SkillTypeString(int nSkillType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("skills", "Name", nSkillType))); +} + +string _prc_inc_SpecialWalkTypeString(int nWalkType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_walk", "Name", nWalkType))); +} + +string _prc_inc_SpellSchoolTypeString(int nSpellSchoolType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_spellshl", "Name", nSpellSchoolType))); +} + +string _prc_inc_SpellTypeString(int nOnHitSpellType) +{ + return GetStringByStrRef(StringToInt(Get2DACache("iprp_spells", "Name", nOnHitSpellType))); +} + +string _prc_inc_VisualEffectString(int nVisualEffect) +{ + //TODO: Look up in 2da (which one?) + switch(nVisualEffect) + { + case ITEM_VISUAL_ACID: + return "Acid"; + case ITEM_VISUAL_COLD: + return "Cold"; + case ITEM_VISUAL_ELECTRICAL: + return "Electrical"; + case ITEM_VISUAL_FIRE: + return "Fire"; + case ITEM_VISUAL_SONIC: + return "Sonic"; + case ITEM_VISUAL_HOLY: + return "Holy"; + case ITEM_VISUAL_EVIL: + return "Evil"; + } + return "???"; +} + +string _prc_inc_ItemPropertyString(itemproperty iprop) +{ + int nType = GetItemPropertyType(iprop); + int nSubType = GetItemPropertySubType(iprop); + int nDurationType = GetItemPropertyDurationType(iprop); + int nParam1 = GetItemPropertyParam1(iprop); + int nParam1Value = GetItemPropertyParam1Value(iprop); + int nCostTable = GetItemPropertyCostTable(iprop); + int nCostTableValue = GetItemPropertyCostTableValue(iprop); + string sType = IntToString(nType); + string sSubType = IntToString(nSubType); + string sDurationType = IntToString(nDurationType); + string sParam1 = IntToString(nParam1); + string sParam1Value = IntToString(nParam1Value); + string sCostTable = IntToString(nCostTable); + string sCostTableValue = IntToString(nCostTableValue); + string sResult = + "Typ: " + sType + "; " + + "SubTyp: " + sSubType + "; " + + "DurTyp: " + sDurationType + "; " + + "Parm: " + sParam1 + "; " + + "ParmVal: " + sParam1Value + "; " + + "CTab: " + sCostTable + "; " + + "CVal: " + sCostTableValue; + string sTypeName = GetStringByStrRef(StringToInt(Get2DACache("itempropdef", "Name", nType))); + switch (nType) + { + //TODO: these are all the possible cases; need to handle more of them. + //DONE case ITEM_PROPERTY_ABILITY_BONUS: + //DONE case ITEM_PROPERTY_AC_BONUS: + // case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE: + // case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP: + // case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT: + //DONE case ITEM_PROPERTY_ENHANCEMENT_BONUS: + //DONE case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP: + // case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT: + // case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER + //DONE case ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION + //DONE case ITEM_PROPERTY_BONUS_FEAT: + // case ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N: + //DONE case ITEM_PROPERTY_CAST_SPELL: + //DONE case ITEM_PROPERTY_DAMAGE_BONUS: + //DONE case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + //case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP: + //case ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT: + //DONE case ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE: + // case ITEM_PROPERTY_DECREASED_DAMAGE: + //DONE case ITEM_PROPERTY_DAMAGE_REDUCTION: + //DONE case ITEM_PROPERTY_DAMAGE_RESISTANCE: + //DONE case ITEM_PROPERTY_DAMAGE_VULNERABILITY: + //DONE case ITEM_PROPERTY_DARKVISION: + //DONE case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: + // case ITEM_PROPERTY_DECREASED_AC: + // case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER: //TODO: e.g. S1-Tomb of Horrors: DesertDragon + // case ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT: + //DONE case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: + // case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE: + //DONE case ITEM_PROPERTY_HASTE: + // case ITEM_PROPERTY_HOLY_AVENGER: + //DONE case ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS: + //DONE case ITEM_PROPERTY_IMPROVED_EVASION: + //DONE case ITEM_PROPERTY_SPELL_RESISTANCE: + //DONE case ITEM_PROPERTY_SAVING_THROW_BONUS: + //DONE case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: + //DONE case ITEM_PROPERTY_KEEN: + //DONE case ITEM_PROPERTY_LIGHT: + // case ITEM_PROPERTY_MIGHTY: + // case ITEM_PROPERTY_MIND_BLANK: + // case ITEM_PROPERTY_NO_DAMAGE: + //DONE case ITEM_PROPERTY_ON_HIT_PROPERTIES: + //DONE case ITEM_PROPERTY_DECREASED_SAVING_THROWS: + //DONE case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: + //DONE case ITEM_PROPERTY_REGENERATION: + //DONE case ITEM_PROPERTY_SKILL_BONUS: + //DONE case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL: + //DONE case ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL: + // case ITEM_PROPERTY_THIEVES_TOOLS: + //DONE case ITEM_PROPERTY_ATTACK_BONUS: + //DONE case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP: + // case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT: + // case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER: + // case ITEM_PROPERTY_UNLIMITED_AMMUNITION: + // case ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP: + // case ITEM_PROPERTY_USE_LIMITATION_CLASS: + // case ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE: + // case ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT: + // case ITEM_PROPERTY_USE_LIMITATION_TILESET: + //DONE case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + // case ITEM_PROPERTY_TRAP: + //DONE case ITEM_PROPERTY_TRUE_SEEING: + //DONE case ITEM_PROPERTY_ON_MONSTER_HIT: + //DONE case ITEM_PROPERTY_TURN_RESISTANCE: + //DONE case ITEM_PROPERTY_MASSIVE_CRITICALS: + //DONE case ITEM_PROPERTY_FREEDOM_OF_MOVEMENT: + //DONE case ITEM_PROPERTY_MONSTER_DAMAGE: + //DONE case ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL: + //DONE case ITEM_PROPERTY_SPECIAL_WALK: + // case ITEM_PROPERTY_HEALERS_KIT: + // case ITEM_PROPERTY_WEIGHT_INCREASE: + //DONE case ITEM_PROPERTY_ONHITCASTSPELL: + //DONE case ITEM_PROPERTY_VISUALEFFECT: + // case ITEM_PROPERTY_ARCANE_SPELL_FAILURE: + + //Completely ignore + case ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION: + return ""; + + //Property name only + case ITEM_PROPERTY_DARKVISION: + case ITEM_PROPERTY_FREEDOM_OF_MOVEMENT: + case ITEM_PROPERTY_HASTE: + case ITEM_PROPERTY_IMPROVED_EVASION: + case ITEM_PROPERTY_KEEN: + case ITEM_PROPERTY_TRUE_SEEING: + return sTypeName; + + //Interpret cost table information + case ITEM_PROPERTY_AC_BONUS: + case ITEM_PROPERTY_ATTACK_BONUS: + case ITEM_PROPERTY_ENHANCEMENT_BONUS: + case ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL: + case ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL: + case ITEM_PROPERTY_LIGHT: + case ITEM_PROPERTY_MASSIVE_CRITICALS: + case ITEM_PROPERTY_MONSTER_DAMAGE: + case ITEM_PROPERTY_REGENERATION: + case ITEM_PROPERTY_SPELL_RESISTANCE: + case ITEM_PROPERTY_TURN_RESISTANCE: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + + //Interpret cost table information; interpret subtype as damage type + case ITEM_PROPERTY_DAMAGE_BONUS: + case ITEM_PROPERTY_DAMAGE_RESISTANCE: + case ITEM_PROPERTY_DAMAGE_VULNERABILITY: + case ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue) + " " + _prc_inc_DamageTypeString(nSubType); + + //Interpret cost table information; interpret subtype as racial group + case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP: + case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP: + return sTypeName + ": " + _prc_inc_AlignmentGroupString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + + //Special handling + case ITEM_PROPERTY_ABILITY_BONUS: + case ITEM_PROPERTY_DECREASED_ABILITY_SCORE: + return sTypeName + ": " + _prc_inc_AbilityTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_BONUS_FEAT: + return sTypeName + ": " + _prc_inc_BonusFeatTypeString(nSubType); + case ITEM_PROPERTY_CAST_SPELL: + return sTypeName + ": " + _prc_inc_SpellTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP: + return sTypeName + ": " + _prc_inc_AlignmentGroupString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue) + " " + _prc_inc_DamageTypeString(nParam1Value); + case ITEM_PROPERTY_DAMAGE_REDUCTION: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue) + " / " + IntToString(StringToInt(sSubType)+1); + case ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE: + case ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE: + return sTypeName + ": " + _prc_inc_DamageTypeString(nSubType); + case ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS: + return sTypeName + ": " + _prc_inc_ImmunityTypeString(nSubType); + case ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL: + return sTypeName + ": " + _prc_inc_SpellSchoolTypeString(nSubType); + case ITEM_PROPERTY_ON_HIT_PROPERTIES: + return sTypeName + ": " + _prc_inc_OnHitTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_ONHITCASTSPELL: + return sTypeName + ": " + _prc_inc_OnHitSpellTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_ON_MONSTER_HIT: + return sTypeName + ": " + _prc_inc_OnMonsterHitTypeString(nSubType) + " " + IntToString(nCostTableValue+1); + case ITEM_PROPERTY_SKILL_BONUS: + return sTypeName + ": " + _prc_inc_SkillTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_SPECIAL_WALK: + return sTypeName + ": " + _prc_inc_SpecialWalkTypeString(nSubType); + case ITEM_PROPERTY_REGENERATION_VAMPIRIC: + return sTypeName + ": " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + case ITEM_PROPERTY_VISUALEFFECT: + return sTypeName + ": " + _prc_inc_VisualEffectString(nSubType); + + case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC: + case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: + return sTypeName + ": " + _prc_inc_SavingThrowTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + + case ITEM_PROPERTY_DECREASED_SAVING_THROWS: + case ITEM_PROPERTY_SAVING_THROW_BONUS: + return sTypeName + ": " + _prc_inc_SavingThrowElementTypeString(nSubType) + " " + _prc_inc_CostTableEntryString(nCostTable, nCostTableValue); + } + return sTypeName + " (" + sResult + ")"; +} + +string _prc_inc_EffectString(effect eEffect) +{ + int nType = GetEffectType(eEffect); + int nSubType = GetEffectSubType(eEffect); + int nDurationType = GetEffectDurationType(eEffect); + int nSpellId = GetEffectSpellId(eEffect); + string sType = IntToString(nType); + string sSubType = IntToString(nSubType); + string sDurationType = IntToString(nDurationType); + string sSpellId = IntToString(nSpellId); + + //Decode type if possible + //TODO: look up in 2da (which one?) instead of having a big switch statement + switch (nType) + { + case EFFECT_TYPE_INVALIDEFFECT : sType = "EFFECT_TYPE_INVALIDEFFECT"; break; + case EFFECT_TYPE_DAMAGE_RESISTANCE : sType = "EFFECT_TYPE_DAMAGE_RESISTANCE"; break; + //case EFFECT_TYPE_ABILITY_BONUS : sType = "EFFECT_TYPE_ABILITY_BONUS"; break; + case EFFECT_TYPE_REGENERATE : sType = "EFFECT_TYPE_REGENERATE"; break; + //case EFFECT_TYPE_SAVING_THROW_BONUS : sType = "EFFECT_TYPE_SAVING_THROW_BONUS"; break; + //case EFFECT_TYPE_MODIFY_AC : sType = "EFFECT_TYPE_MODIFY_AC"; break; + //case EFFECT_TYPE_ATTACK_BONUS : sType = "EFFECT_TYPE_ATTACK_BONUS"; break; + case EFFECT_TYPE_DAMAGE_REDUCTION : sType = "EFFECT_TYPE_DAMAGE_REDUCTION"; break; + //case EFFECT_TYPE_DAMAGE_BONUS : sType = "EFFECT_TYPE_DAMAGE_BONUS"; break; + case EFFECT_TYPE_TEMPORARY_HITPOINTS : sType = "EFFECT_TYPE_TEMPORARY_HITPOINTS"; break; + //case EFFECT_TYPE_DAMAGE_IMMUNITY : sType = "EFFECT_TYPE_DAMAGE_IMMUNITY"; break; + case EFFECT_TYPE_ENTANGLE : sType = "EFFECT_TYPE_ENTANGLE"; break; + case EFFECT_TYPE_INVULNERABLE : sType = "EFFECT_TYPE_INVULNERABLE"; break; + case EFFECT_TYPE_DEAF : sType = "EFFECT_TYPE_DEAF"; break; + case EFFECT_TYPE_RESURRECTION : sType = "EFFECT_TYPE_RESURRECTION"; break; + case EFFECT_TYPE_IMMUNITY : sType = "EFFECT_TYPE_IMMUNITY"; break; + //case EFFECT_TYPE_BLIND : sType = "EFFECT_TYPE_BLIND"; break; + case EFFECT_TYPE_ENEMY_ATTACK_BONUS : sType = "EFFECT_TYPE_ENEMY_ATTACK_BONUS"; break; + case EFFECT_TYPE_ARCANE_SPELL_FAILURE : sType = "EFFECT_TYPE_ARCANE_SPELL_FAILURE"; break; + //case EFFECT_TYPE_MOVEMENT_SPEED : sType = "EFFECT_TYPE_MOVEMENT_SPEED"; break; + case EFFECT_TYPE_AREA_OF_EFFECT : sType = "EFFECT_TYPE_AREA_OF_EFFECT"; break; + case EFFECT_TYPE_BEAM : sType = "EFFECT_TYPE_BEAM"; break; + //case EFFECT_TYPE_SPELL_RESISTANCE : sType = "EFFECT_TYPE_SPELL_RESISTANCE"; break; + case EFFECT_TYPE_CHARMED : sType = "EFFECT_TYPE_CHARMED"; break; + case EFFECT_TYPE_CONFUSED : sType = "EFFECT_TYPE_CONFUSED"; break; + case EFFECT_TYPE_FRIGHTENED : sType = "EFFECT_TYPE_FRIGHTENED"; break; + case EFFECT_TYPE_DOMINATED : sType = "EFFECT_TYPE_DOMINATED"; break; + case EFFECT_TYPE_PARALYZE : sType = "EFFECT_TYPE_PARALYZE"; break; + case EFFECT_TYPE_DAZED : sType = "EFFECT_TYPE_DAZED"; break; + case EFFECT_TYPE_STUNNED : sType = "EFFECT_TYPE_STUNNED"; break; + case EFFECT_TYPE_SLEEP : sType = "EFFECT_TYPE_SLEEP"; break; + case EFFECT_TYPE_POISON : sType = "EFFECT_TYPE_POISON"; break; + case EFFECT_TYPE_DISEASE : sType = "EFFECT_TYPE_DISEASE"; break; + case EFFECT_TYPE_CURSE : sType = "EFFECT_TYPE_CURSE"; break; + case EFFECT_TYPE_SILENCE : sType = "EFFECT_TYPE_SILENCE"; break; + case EFFECT_TYPE_TURNED : sType = "EFFECT_TYPE_TURNED"; break; + case EFFECT_TYPE_HASTE : sType = "EFFECT_TYPE_HASTE"; break; + case EFFECT_TYPE_SLOW : sType = "EFFECT_TYPE_SLOW"; break; + case EFFECT_TYPE_ABILITY_INCREASE : sType = "EFFECT_TYPE_ABILITY_INCREASE"; break; + case EFFECT_TYPE_ABILITY_DECREASE : sType = "EFFECT_TYPE_ABILITY_DECREASE"; break; + case EFFECT_TYPE_ATTACK_INCREASE : sType = "EFFECT_TYPE_ATTACK_INCREASE"; break; + case EFFECT_TYPE_ATTACK_DECREASE : sType = "EFFECT_TYPE_ATTACK_DECREASE"; break; + case EFFECT_TYPE_DAMAGE_INCREASE : sType = "EFFECT_TYPE_DAMAGE_INCREASE"; break; + case EFFECT_TYPE_DAMAGE_DECREASE : sType = "EFFECT_TYPE_DAMAGE_DECREASE"; break; + case EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE : sType = "EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE"; break; + case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE : sType = "EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE"; break; + case EFFECT_TYPE_AC_INCREASE : sType = "EFFECT_TYPE_AC_INCREASE"; break; + case EFFECT_TYPE_AC_DECREASE : sType = "EFFECT_TYPE_AC_DECREASE"; break; + case EFFECT_TYPE_MOVEMENT_SPEED_INCREASE : sType = "EFFECT_TYPE_MOVEMENT_SPEED_INCREASE"; break; + case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE : sType = "EFFECT_TYPE_MOVEMENT_SPEED_DECREASE"; break; + case EFFECT_TYPE_SAVING_THROW_INCREASE : sType = "EFFECT_TYPE_SAVING_THROW_INCREASE"; break; + case EFFECT_TYPE_SAVING_THROW_DECREASE : sType = "EFFECT_TYPE_SAVING_THROW_DECREASE"; break; + case EFFECT_TYPE_SPELL_RESISTANCE_INCREASE : sType = "EFFECT_TYPE_SPELL_RESISTANCE_INCREASE"; break; + case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE : sType = "EFFECT_TYPE_SPELL_RESISTANCE_DECREASE"; break; + case EFFECT_TYPE_SKILL_INCREASE : sType = "EFFECT_TYPE_SKILL_INCREASE"; break; + case EFFECT_TYPE_SKILL_DECREASE : sType = "EFFECT_TYPE_SKILL_DECREASE"; break; + case EFFECT_TYPE_INVISIBILITY : sType = "EFFECT_TYPE_INVISIBILITY"; break; + case EFFECT_TYPE_IMPROVEDINVISIBILITY : sType = "EFFECT_TYPE_IMPROVEDINVISIBILITY"; break; + case EFFECT_TYPE_DARKNESS : sType = "EFFECT_TYPE_DARKNESS"; break; + case EFFECT_TYPE_DISPELMAGICALL : sType = "EFFECT_TYPE_DISPELMAGICALL"; break; + case EFFECT_TYPE_ELEMENTALSHIELD : sType = "EFFECT_TYPE_ELEMENTALSHIELD"; break; + case EFFECT_TYPE_NEGATIVELEVEL : sType = "EFFECT_TYPE_NEGATIVELEVEL"; break; + case EFFECT_TYPE_POLYMORPH : sType = "EFFECT_TYPE_POLYMORPH"; break; + case EFFECT_TYPE_SANCTUARY : sType = "EFFECT_TYPE_SANCTUARY"; break; + case EFFECT_TYPE_TRUESEEING : sType = "EFFECT_TYPE_TRUESEEING"; break; + case EFFECT_TYPE_SEEINVISIBLE : sType = "EFFECT_TYPE_SEEINVISIBLE"; break; + case EFFECT_TYPE_TIMESTOP : sType = "EFFECT_TYPE_TIMESTOP"; break; + case EFFECT_TYPE_BLINDNESS : sType = "EFFECT_TYPE_BLINDNESS"; break; + case EFFECT_TYPE_SPELLLEVELABSORPTION : sType = "EFFECT_TYPE_SPELLLEVELABSORPTION"; break; + case EFFECT_TYPE_DISPELMAGICBEST : sType = "EFFECT_TYPE_DISPELMAGICBEST"; break; + case EFFECT_TYPE_ULTRAVISION : sType = "EFFECT_TYPE_ULTRAVISION"; break; + case EFFECT_TYPE_MISS_CHANCE : sType = "EFFECT_TYPE_MISS_CHANCE"; break; + case EFFECT_TYPE_CONCEALMENT : sType = "EFFECT_TYPE_CONCEALMENT"; break; + case EFFECT_TYPE_SPELL_IMMUNITY : sType = "EFFECT_TYPE_SPELL_IMMUNITY"; break; + case EFFECT_TYPE_VISUALEFFECT : sType = "EFFECT_TYPE_VISUALEFFECT"; break; + case EFFECT_TYPE_DISAPPEARAPPEAR : sType = "EFFECT_TYPE_DISAPPEARAPPEAR"; break; + case EFFECT_TYPE_SWARM : sType = "EFFECT_TYPE_SWARM"; break; + case EFFECT_TYPE_TURN_RESISTANCE_DECREASE : sType = "EFFECT_TYPE_TURN_RESISTANCE_DECREASE"; break; + case EFFECT_TYPE_TURN_RESISTANCE_INCREASE : sType = "EFFECT_TYPE_TURN_RESISTANCE_INCREASE"; break; + case EFFECT_TYPE_PETRIFY : sType = "EFFECT_TYPE_PETRIFY"; break; + case EFFECT_TYPE_CUTSCENE_PARALYZE : sType = "EFFECT_TYPE_CUTSCENE_PARALYZE"; break; + case EFFECT_TYPE_ETHEREAL : sType = "EFFECT_TYPE_ETHEREAL"; break; + case EFFECT_TYPE_SPELL_FAILURE : sType = "EFFECT_TYPE_SPELL_FAILURE"; break; + case EFFECT_TYPE_CUTSCENEGHOST : sType = "EFFECT_TYPE_CUTSCENEGHOST"; break; + case EFFECT_TYPE_CUTSCENEIMMOBILIZE : sType = "EFFECT_TYPE_CUTSCENEIMMOBILIZE"; break; + } + + //Decode subtype if possible + //TODO: look up in 2da (which one?) instead of having a switch statement + switch (nSubType) + { + case SUBTYPE_MAGICAL : sSubType = "SUBTYPE_MAGICAL"; break; + case SUBTYPE_SUPERNATURAL : sSubType = "SUBTYPE_SUPERNATURAL"; break; + case SUBTYPE_EXTRAORDINARY : sSubType = "SUBTYPE_EXTRAORDINARY"; break; + } + + //Decode duration type if possible + //TODO: look up in 2da (which one?) instead of having a switch statement + switch (nDurationType) + { + case DURATION_TYPE_INSTANT : sDurationType = "DURATION_TYPE_INSTANT"; break; + case DURATION_TYPE_TEMPORARY : sDurationType = "DURATION_TYPE_TEMPORARY"; break; + case DURATION_TYPE_PERMANENT : sDurationType = "DURATION_TYPE_PERMANENT"; break; + } + + string sResult = + "EFFECT Type: " + sType + "; " + + "SubType: " + sSubType + "; " + + "DurationType: " + sDurationType + "; " + + "SpellId: " + sSpellId; + + return sResult; +} + +void _prc_inc_PrintShapeInfo(object oPC, string sMessage) +{ + if(!GetLocalInt(oPC, "PRC_SuppressChatPrint")) + SendMessageToPC(oPC, sMessage); //Send to chat window in game + if(GetLocalInt(oPC, "PRC_EnableLogPrint")) + PrintString(sMessage); //Write to log file for reference +} + +void _prc_inc_PrintClassInfo(string sPrefix, object oPC, object oTemplate, int nClassType) +{ + if (nClassType != CLASS_TYPE_INVALID) + { + int nLevel = GetLevelByClass(nClassType, oTemplate); + string sClassName = _prc_inc_ClassTypeString(nClassType); + _prc_inc_PrintShapeInfo(oPC, sPrefix + sClassName + " (" + IntToString(nLevel) + ")"); + } +} + +void _prc_inc_PrintItemProperty(string sPrefix, object oPC, itemproperty iProp, int bIncludeTemp = FALSE) +{ + int nDurationType = GetItemPropertyDurationType(iProp); + if(nDurationType == DURATION_TYPE_PERMANENT || (bIncludeTemp && nDurationType == DURATION_TYPE_TEMPORARY)) + { + string sPropString = _prc_inc_ItemPropertyString(iProp); + if(sPropString != "") + { + if (nDurationType == DURATION_TYPE_TEMPORARY) + sPropString = GetStringByStrRef(57473+0x01000000) + sPropString; //"TEMPORARY: " + _prc_inc_PrintShapeInfo(oPC, sPrefix + sPropString); + } + } +} + +void _prc_inc_PrintAllItemProperties(string sPrefix, object oPC, object oItem, int bIncludeTemp = FALSE) +{ + itemproperty iProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(iProp)) + { + _prc_inc_PrintItemProperty(sPrefix, oPC, iProp, bIncludeTemp); + iProp = GetNextItemProperty(oItem); + } +} + +void _prc_inc_PrintEffect(string sPrefix, object oPC, effect eEffect) +{ + if (GetEffectType(eEffect) == EFFECT_TYPE_INVALIDEFFECT) + { + //An effect with type EFFECT_TYPE_INVALID is added for each item property + //They are also added for a couple of other things (Knockdown, summons, etc.) + //Just skip these + } + else + { + string sEffectString = _prc_inc_EffectString(eEffect); + if(sEffectString != "") + _prc_inc_PrintShapeInfo(oPC, sPrefix + sEffectString); + } +} + +void _prc_inc_PrintAllEffects(string sPrefix, object oPC, object oItem) +{ + effect eEffect = GetFirstEffect(oItem); + while(GetIsEffectValid(eEffect)) + { + _prc_inc_PrintEffect(sPrefix, oPC, eEffect); + eEffect = GetNextEffect(oItem); + } +} + +//NOTE: THIS FUNCTION HAS A LOT OF CODE IN COMMON WITH _prc_inc_shifting_CreateShifterActiveAbilitiesItem +//TODO: PUT SOME OF IT IN A SHARED FUNCTION THAT THEY BOTH CALL +void _prc_inc_shifting_PrintShifterActiveAbilities(object oPC, object oTemplate) +{ + string sPrefix = GetStringByStrRef(57437+0x01000000); //"Epic Wildshape Spell-Like Abilities:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + + int bPrinted = FALSE; + + object oTemplateHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate); + itemproperty iProp = GetFirstItemProperty(oTemplateHide); + while(GetIsItemPropertyValid(iProp)) + { + if(GetItemPropertyDurationType(iProp) == DURATION_TYPE_PERMANENT && GetItemPropertyType(iProp) == ITEM_PROPERTY_CAST_SPELL) + { + _prc_inc_PrintItemProperty("=== ", oPC, iProp); + bPrinted = TRUE; + } + iProp = GetNextItemProperty(oTemplateHide); + } + + // Loop over shifter_abilitie.2da + string sNumUses; + int nSpell, nNumUses, nProps; + int i = 0; + while(nSpell = StringToInt(Get2DACache("shifter_abilitie", "Spell", i))) + { + // See if the template has this spell + if(GetHasSpell(nSpell, oTemplate)) + { + // Determine the number of uses from the 2da + sNumUses = Get2DACache("shifter_abilitie", "IPCSpellNumUses", i); + if(sNumUses == "1_USE_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_1_USE_PER_DAY; + else if(sNumUses == "2_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_2_USES_PER_DAY; + else if(sNumUses == "3_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_3_USES_PER_DAY; + else if(sNumUses == "4_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_4_USES_PER_DAY; + else if(sNumUses == "5_USES_PER_DAY") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_5_USES_PER_DAY; + else if(sNumUses == "UNLIMITED_USE") + nNumUses = IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE; + else{ + if(DEBUG) DoDebug("prc_inc_shifting: _prc_inc_shifting_PrintShifterActiveAbilities(): Unknown IPCSpellNumUses in shifter_abilitie.2da line " + IntToString(i) + ": " + sNumUses); + nNumUses = -1; + } + + // Create the itemproperty and print it + iProp = ItemPropertyCastSpell(StringToInt(Get2DACache("shifter_abilitie", "IPSpell", i)), nNumUses); + _prc_inc_PrintItemProperty("=== ", oPC, iProp); + bPrinted = TRUE; + //TODO: DESTROY iProp? + + // Increment property counter + nProps += 1; + } + + // Increment loop counter + i += 1; + } + + if(!bPrinted) + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57481+0x01000000)); //"None" +} + +void _prc_inc_shifting_PrintFeats(object oPC, object oTemplate, int nStartIndex, int nLimitIndex) +{ + //Loop over shifter_feats.2da + string sFeat; + int i = nStartIndex; + while((i < nLimitIndex) && (sFeat = Get2DACache("shifter_feats", "Feat", i)) != "") + { + if (_prc_inc_GetHasFeat(oTemplate, StringToInt(sFeat))) + { + string sFeatName = GetStringByStrRef(StringToInt(Get2DACache("feat", "Feat", StringToInt(sFeat)))); + _prc_inc_PrintShapeInfo(oPC, "=== " + sFeatName); + } + i += 1; + } +} + +void _prc_inc_PrintNaturalAC(object oPC, object oTemplate) +{ + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57435+0x01000000) + " " + IntToString(_prc_inc_CreatureNaturalAC(oTemplate))); //Natural AC of form +} + +//TODO: TLK Entries +void _prc_inc_PrintDebugItem(object oPC, string oItemType, object oItem) +{ + if (GetIsObjectValid(oItem)) + { + _prc_inc_PrintShapeInfo(oPC, "===================================="); + _prc_inc_PrintShapeInfo(oPC, "====== " + oItemType); + _prc_inc_PrintShapeInfo(oPC, "====== NAME: " + GetName(oItem)); + _prc_inc_PrintShapeInfo(oPC, "====== RESREF: " + GetResRef(oItem)); + _prc_inc_PrintShapeInfo(oPC, "====== ITEM PROPERTIES ======"); + _prc_inc_PrintAllItemProperties("=== ", oPC, oItem, TRUE); + _prc_inc_PrintShapeInfo(oPC, "====== EFFECTS ======"); + _prc_inc_PrintAllEffects("=== ", oPC, oItem); + _prc_inc_PrintShapeInfo(oPC, "====== OTHER ======"); + if (GetObjectType(oItem) == OBJECT_TYPE_CREATURE) + { + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(1, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(2, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(3, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(4, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(5, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(6, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(7, oItem)); + _prc_inc_PrintClassInfo("=== ", oPC, oItem, GetClassByPosition(8, oItem)); + + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== Main hand weapon: " + (GetIsWeaponEffective(oItem, FALSE) ? "Effective" : "Ineffective")); + _prc_inc_PrintShapeInfo(oPC, "====== Off hand weapon: " + (GetIsWeaponEffective(oItem, TRUE) ? "Effective" : "Ineffective")); + _prc_inc_PrintShapeInfo(oPC, "====== Immortal: " + (GetImmortal(oItem) ? "Yes" : "No")); + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== Level: " + IntToString(GetHitDice(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== CR: " + FloatToString(GetChallengeRating(oItem), 4, 1)); + _prc_inc_PrintShapeInfo(oPC, "====== Caster Level: " + IntToString(GetCasterLevel(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== XP: " + IntToString(GetXP(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== Alignment: " + IntToString(GetLawChaosValue(oItem)) + " / " + IntToString(GetGoodEvilValue(oItem))); + //TODO: + // int GetAlignmentLawChaos(object oCreature); + // int GetAlignmentGoodEvil(object oCreature); + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== BAB: " + IntToString(GetBaseAttackBonus(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== HP: " + IntToString(GetCurrentHitPoints(oItem)) + " / " + IntToString(GetMaxHitPoints(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== AC: " + IntToString(GetAC(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== SR: " + IntToString(GetSpellResistance(oItem))); + _prc_inc_PrintShapeInfo(oPC, "------"); + + //TODO: look up names in 2da/TLK? + _prc_inc_PrintShapeInfo(oPC, "====== STR: " + IntToString(GetAbilityScore(oItem, ABILITY_STRENGTH)) + " (" + IntToString(GetAbilityModifier(ABILITY_STRENGTH, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== DEX: " + IntToString(GetAbilityScore(oItem, ABILITY_DEXTERITY)) + " (" + IntToString(GetAbilityModifier(ABILITY_DEXTERITY, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== CON: " + IntToString(GetAbilityScore(oItem, ABILITY_CONSTITUTION)) + " (" + IntToString(GetAbilityModifier(ABILITY_CONSTITUTION, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== INT: " + IntToString(GetAbilityScore(oItem, ABILITY_INTELLIGENCE)) + " (" + IntToString(GetAbilityModifier(ABILITY_INTELLIGENCE, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== WIS: " + IntToString(GetAbilityScore(oItem, ABILITY_WISDOM)) + " (" + IntToString(GetAbilityModifier(ABILITY_WISDOM, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "====== CHA: " + IntToString(GetAbilityScore(oItem, ABILITY_CHARISMA)) + " (" + IntToString(GetAbilityModifier(ABILITY_CHARISMA, oItem)) + ")"); + _prc_inc_PrintShapeInfo(oPC, "------"); + + //TODO: look up names in 2da/TLK? + _prc_inc_PrintShapeInfo(oPC, "====== Fortitude: " + IntToString(GetFortitudeSavingThrow(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== Will: " + IntToString(GetWillSavingThrow(oItem))); + _prc_inc_PrintShapeInfo(oPC, "====== Reflex: " + IntToString(GetReflexSavingThrow(oItem))); + _prc_inc_PrintShapeInfo(oPC, "------"); + + int i = 0; + string sSkillName; + while((sSkillName = Get2DACache("skills", "Name", i)) != "") + { + sSkillName = GetStringByStrRef(StringToInt(sSkillName)); + _prc_inc_PrintShapeInfo(oPC, "====== " + sSkillName + ": " + IntToString(GetSkillRank(i, oItem))); + i += 1; + } + _prc_inc_PrintShapeInfo(oPC, "------"); + + _prc_inc_PrintShapeInfo(oPC, "====== Gender: " + IntToString(GetGender(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Size: " + IntToString(GetCreatureSize(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Race: " + IntToString(GetRacialType(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Speed: " + IntToString(GetMovementRate(oItem))); //TODO: look up values in 2da? + _prc_inc_PrintShapeInfo(oPC, "====== Dead: " + (GetIsDead(oItem) ? "Yes" : "No")); + _prc_inc_PrintShapeInfo(oPC, "====== Tag: " + GetTag(oItem)); + _prc_inc_PrintShapeInfo(oPC, "====== Object Type: " + IntToString(GetObjectType(oItem))); //TODO: look up values in 2da? + + //TODO?: + //int GetGold(object oTarget=OBJECT_SELF); + //location GetLocalLocation(object oObject, string sVarName); + // vector GetPositionFromLocation(location lLocation); + // object GetAreaFromLocation(location lLocation); + // float GetFacingFromLocation(location lLocation); + //int GetCommandable(object oTarget=OBJECT_SELF); + //int GetIsListening(object oObject); + //int GetReputation(object oSource, object oTarget); + //location GetLocation(object oObject); + //int GetIsPC(object oCreature); + // int GetIsEnemy(object oTarget, object oSource=OBJECT_SELF); + // int GetIsFriend(object oTarget, object oSource=OBJECT_SELF); + // int GetIsNeutral(object oTarget, object oSource=OBJECT_SELF); + // int GetStealthMode(object oCreature); + // int GetDetectMode(object oCreature); + // int GetDefensiveCastingMode(object oCreature); + // int GetAppearanceType(object oCreature); + // int GetWeight(object oTarget=OBJECT_SELF); //Gets the weight of an item, or the total carried weight of a creature in tenths of pounds (as per the baseitems.2da). + // int GetAILevel(object oTarget=OBJECT_SELF); + // int GetActionMode(object oCreature, int nMode); + // int GetArcaneSpellFailure(object oCreature); + // int GetLootable( object oCreature ); + // int GetIsCreatureDisarmable(object oCreature); + // string GetDeity(object oCreature); + // string GetSubRace(object oTarget); + // int GetAge(object oCreature); + //int GetPlotFlag(object oTarget=OBJECT_SELF); + } + else + _prc_inc_PrintShapeInfo(oPC, "====== AC: " + IntToString(GetItemACValue(oItem))); + _prc_inc_PrintShapeInfo(oPC, "===================================="); + } +} + +//TODO: TLK Entries +void _prc_inc_ShapePrintDebug(object oPC, object oTarget, int bForceLogPrint) +{ + int nSaveValue = GetLocalInt(oPC, "PRC_EnableLogPrint"); + if (bForceLogPrint) + SetLocalInt(oPC, "PRC_EnableLogPrint", TRUE); + + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "CREATURE", oTarget)); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "CREATURE SKIN", GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTarget))); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "RIGHT CREATURE WEAPON", GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget))); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "LEFT CREATURE WEAPON", GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget))); + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "SPECIAL CREATURE WEAPON", GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget))); + + int nSlot; + for(nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++) + { + switch (nSlot) + { + case INVENTORY_SLOT_CARMOUR: + case INVENTORY_SLOT_CWEAPON_R: + case INVENTORY_SLOT_CWEAPON_L: + case INVENTORY_SLOT_CWEAPON_B: + break; + + default: + DelayCommand(0.0, _prc_inc_PrintDebugItem(oPC, "INVENTORY ITEM " + IntToString(nSlot) + ": ", GetItemInSlot(nSlot, oTarget))); + } + } + + if (bForceLogPrint) + DelayCommand(0.1, SetLocalInt(oPC, "PRC_EnableLogPrint", nSaveValue)); +} + +//TODO: Add nShifterType parameter so that only applicable information is printed? +void _prc_inc_PrintShape(object oPC, object oTemplate, int bForceLogPrint) +{ + int nSaveValue = GetLocalInt(oPC, "PRC_EnableLogPrint"); + if (bForceLogPrint) + SetLocalInt(oPC, "PRC_EnableLogPrint", TRUE); + + _prc_inc_PrintShapeInfo(oPC, "================================================="); + + //Basic information + + _prc_inc_PrintShapeInfo(oPC, "=== " + GetName(oTemplate)); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetResRef(oTemplate)); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef((StringToInt(Get2DACache("racialtypes", "Name", MyPRCGetRacialType(oTemplate)))))); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57420+0x01000000) + IntToString(_prc_inc_shifting_ShifterLevelRequirement(oTemplate))); //"Required Shifter Level: " + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57421+0x01000000) + IntToString(_prc_inc_shifting_CharacterLevelRequirement(oTemplate))); //"Required Character Level: " + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57436+0x01000000) + FloatToString(GetChallengeRating(oTemplate), 4, 1)); //"Challenge Rating: " + + _prc_inc_PrintShapeInfo(oPC, "=========="); + + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(1, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(2, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(3, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(4, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(5, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(6, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(7, oTemplate)); + _prc_inc_PrintClassInfo("=== ", oPC, oTemplate, GetClassByPosition(8, oTemplate)); + + _prc_inc_PrintShapeInfo(oPC, "=========="); + + //Harmlessly invisible? + + if(_prc_inc_shifting_GetIsCreatureHarmless(oTemplate)) + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57424+0x01000000)); //"Harmlessly Invisible" + + //Able to cast spells without Natural Spell? + + if(!_prc_inc_shifting_GetCanFormCast(oTemplate) && !GetHasFeat(FEAT_PRESTIGE_SHIFTER_NATURALSPELL, oTemplate)) + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57477+0x01000000)); //"Cannot cast spells" + else + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57476+0x01000000)); //"Can cast spells" + + //Natural AC + + _prc_inc_PrintNaturalAC(oPC, oTemplate); + + //STR, DEX, CON + + struct _prc_inc_ability_info_struct rInfoStruct = _prc_inc_shifter_GetAbilityInfo(oTemplate, oPC); + + //Extra information related to STR, DEX, CON + + string sExtra, sBonusOrPenalty = GetStringByStrRef(57427+0x01000000); + sExtra = " (" + sBonusOrPenalty + (rInfoStruct.nDeltaSTR>=0?"+":"") + IntToString(rInfoStruct.nDeltaSTR) + ")"; + _prc_inc_PrintShapeInfo(oPC, "=== " + _prc_inc_AbilityTypeString(0) + " " + IntToString(rInfoStruct.nTemplateSTR) + sExtra); + sExtra = " (" + sBonusOrPenalty + (rInfoStruct.nDeltaDEX>=0?"+":"") + IntToString(rInfoStruct.nDeltaDEX) + ")"; + _prc_inc_PrintShapeInfo(oPC, "=== " + _prc_inc_AbilityTypeString(1) + " " + IntToString(rInfoStruct.nTemplateDEX) + sExtra); + sExtra = " (" + sBonusOrPenalty + (rInfoStruct.nDeltaCON>=0?"+":"") + IntToString(rInfoStruct.nDeltaCON) + ")"; + _prc_inc_PrintShapeInfo(oPC, "=== " + _prc_inc_AbilityTypeString(2) + " " + IntToString(rInfoStruct.nTemplateCON) + sExtra); + + _prc_inc_PrintShapeInfo(oPC, "------"); + + int i = 0; + string sSkillName; + string sSTRBasedSkills, sDEXBasedSkills, sCONBasedSkills; + while((sSkillName = Get2DACache("skills", "Name", i)) != "") + { + sSkillName = GetStringByStrRef(StringToInt(sSkillName)); + string sSkillKeyAbility = Get2DACache("skills", "KeyAbility", i); + if (sSkillKeyAbility == "STR") + sSTRBasedSkills += sSkillName + ", "; + else if (sSkillKeyAbility == "DEX") + sDEXBasedSkills += sSkillName + ", "; + else if (sSkillKeyAbility == "CON") + sCONBasedSkills += sSkillName + ", "; + i += 1; + } + if (GetStringLength(sSTRBasedSkills)) + sSTRBasedSkills = GetStringLeft(sSTRBasedSkills, GetStringLength(sSTRBasedSkills) - 2); //Remove the final ", " + if (GetStringLength(sDEXBasedSkills)) + sDEXBasedSkills = GetStringLeft(sDEXBasedSkills, GetStringLength(sDEXBasedSkills) - 2); //Remove the final ", " + if (GetStringLength(sCONBasedSkills)) + sCONBasedSkills = GetStringLeft(sCONBasedSkills, GetStringLength(sCONBasedSkills) - 2); //Remove the final ", " + + int nSTRBonus = rInfoStruct.nExtraSTR / 2; + if (nSTRBonus > 0) + { + //TODO: cap AB bonus + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60544+0x01000000) + " +" + IntToString(nSTRBonus)); //Attack increase from STR increase + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60546+0x01000000) + " +" + IntToString(nSTRBonus)); //Damage increase from STR increase + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60528+0x01000000), "%(SKILLS)", sSTRBasedSkills) + " +" + IntToString(nSTRBonus)); //Skill bonus from STR increase + } + else if (nSTRBonus < 0) + { + //TODO: cap AB penalty--at what? + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60545+0x01000000) + " " + IntToString(nSTRBonus)); //Attack decrease from STR decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60547+0x01000000) + " " + IntToString(nSTRBonus)); //Damage decrease from STR decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60529+0x01000000), "%(SKILLS)", sSTRBasedSkills) + " " + IntToString(nSTRBonus)); //Skill penalty from STR decrease + } + + int nDEXBonus = rInfoStruct.nExtraDEX / 2; + if (nDEXBonus > 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60548+0x01000000) + " +" + IntToString(nDEXBonus)); //AC increase from DEX increase + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60530+0x01000000), "%(SKILLS)", sDEXBasedSkills) + " +" + IntToString(nDEXBonus)); //Skill bonus from DEX increase + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60531+0x01000000) + " +" + IntToString(nDEXBonus)); //Saving throw bonus from DEX increase + } + else if (nDEXBonus < 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60549+0x01000000) + " " + IntToString(nDEXBonus)); //AC decrease from DEX increase + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60532+0x01000000), "%(SKILLS)", sDEXBasedSkills) + " " + IntToString(nDEXBonus)); //Skill penalty from DEX decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60533+0x01000000) + " " + IntToString(nDEXBonus)); //Saving throw penalty from DEX decrease + } + + int nCONBonus = rInfoStruct.nExtraCON / 2; + if (nCONBonus > 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60534+0x01000000), "%(SKILLS)", sCONBasedSkills) + " +" + IntToString(nCONBonus)); //Skill bonus from CON increase + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60535+0x01000000) + " +" + IntToString(nCONBonus)); //Saving throw bonus from CON increase + int tempHP = rInfoStruct.nExtraCON * GetHitDice(oPC); + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(57431+0x01000000) + " " + IntToString(tempHP)); //Temporary HP from CON increase + } + else if (nCONBonus < 0) + { + _prc_inc_PrintShapeInfo(oPC, "=== " + ReplaceString(GetStringByStrRef(60536+0x01000000), "%(SKILLS)", sCONBasedSkills) + " " + IntToString(nCONBonus)); //Skill penalty from CON decrease + _prc_inc_PrintShapeInfo(oPC, "=== " + GetStringByStrRef(60537+0x01000000) + " " + IntToString(nCONBonus)); //Saving throw penalty from CON decrease + } + + _prc_inc_PrintShapeInfo(oPC, "=========="); + + //Hide and creature weapon properties + + object oTemplateCWpR = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTemplate); + object oTemplateCWpL = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTemplate); + object oTemplateCWpB = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTemplate); + object oTemplateHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTemplate); + + if(GetIsObjectValid(oTemplateCWpR)) + { + string sPrefix = GetStringByStrRef(57432+0x01000000); //"Right Creature Weapon:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateCWpR); + } + + if(GetIsObjectValid(oTemplateCWpL)) + { + string sPrefix = GetStringByStrRef(57433+0x01000000); //"Left Creature Weapon:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateCWpL); + } + + if(GetIsObjectValid(oTemplateCWpB)) + { + string sPrefix = GetStringByStrRef(57434+0x01000000); //"Special Attack Creature Weapon:" + _prc_inc_PrintShapeInfo(oPC, "=== " + sPrefix); + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateCWpB); + } + + if(GetIsObjectValid(oTemplateHide)) + _prc_inc_PrintAllItemProperties("=== ", oPC, oTemplateHide); + + //Spell-like abilities + + _prc_inc_shifting_PrintShifterActiveAbilities(oPC, oTemplate); + + //Feats + + i = 0; + string sFeat; + int CHUNK_SIZE = 25; //50 was too big, so use 25 + + while((sFeat = Get2DACache("shifter_feats", "Feat", i)) != "") + { + DelayCommand(0.0f, _prc_inc_shifting_PrintFeats(oPC, oTemplate, i, i+CHUNK_SIZE)); + i += CHUNK_SIZE; + } + DelayCommand(0.0f, _prc_inc_PrintShapeInfo(oPC, "=================================================")); + + if (bForceLogPrint) + DelayCommand(0.1, SetLocalInt(oPC, "PRC_EnableLogPrint", nSaveValue)); + + if (GetLocalInt(oPC, "prc_shift_debug")) + DelayCommand(0.2f, _prc_inc_ShapePrintDebug(oPC, oTemplate, bForceLogPrint)); +} diff --git a/src/include/prc_sp_func.nss b/src/include/prc_sp_func.nss new file mode 100644 index 0000000..233090f --- /dev/null +++ b/src/include/prc_sp_func.nss @@ -0,0 +1,341 @@ +/* + prc_sp_func + + Additional spell functions for holding the + charge (also works for psionics) + + By: Flaming_Sword + Created: January 31, 2006 *complete rewrite + Modified: May 27, 2006 + + From Player's Handbook 3.5ed p141: + + Holding the Charge: If you don't discharge the + spell in the round when you cast the sepll, + you can hold the discharge of the spell + (hold the charge) indefinitely. You can + continue to make touch attacks round after + round. You can touch one friend as a standard + action or up to six friends as a full-round + action. If you touch anything or anyone while + holding a charge, even unintentionally, the + spell discharges. If you cast another spell, + the touch spell dissipates. Alternatively, you + may make a normal unarmed attack (or an attack + with a natural weapon) while holding a charge. + In this case you aren't considered armed and you + provoke attacks of opportunity as normal for + the attack. (If your unarmed attacks or natural + weapon attack doesn't provoke attacks of + opportunity, neither dies this attack. If the + attack hits, you deal normal damage for your + unarmed attack or natural weapon and the spell + discharges. If the attack misses, you are still + holding the charge. + + Conclusion: + Holy crap! Charges stay if your touch attacks miss! + +*/ + +// need declaration for this here +int IsTouchSpell(int nSpellID); +void CleanSpellVariables(object oPC); +void SetLocalSpellVariables(object oCaster, int nCharges = 1, int nSpellIDOverride = -1); +void DecrementSpellCharges(object oPC); +void RunSpellScript(object oPC, int nSpellID, int nEventType); +void RunImpactScript(object oPC, int nSpellID, int nEventType); +int IsCure(int nSpellID); +int IsMassCure(int nSpellID); +int IsMassInflict(int nSpellID); +int IsHeal(int nSpellID); +int IsMassHealHarm(int nSpellID); +int CheckRemoveEffects(int nSpellID, int nEffectType); +int IsRaySpell(int nSpell); + +//constant declarations in case they change +const string PRC_SPELL_CHARGE_COUNT = "PRC_SPELL_CHARGE_COUNT"; +const string PRC_SPELL_CHARGE_SPELLID = "PRC_SPELL_CHARGE_SPELLID"; +const string PRC_SPELL_CHARGE_LEVEL = "PRC_SPELL_CHARGE_LEVEL"; +const string PRC_SPELL_CONC_TARGET = "PRC_SPELL_CONC_TARGET"; +const string PRC_SPELL_METAMAGIC = "PRC_SPELL_METAMAGIC"; + +const string PRC_POWER_HOLD_MANIFESTATION = "PRC_POWER_HOLD_MANIFESTATION"; + +const string PRC_SPELL_EVENT = "PRC_SPELL_EVENT"; + +const string PRC_SPELL_HOLD = "PRC_SPELL_HOLD"; + +const int PRC_SPELL_EVENT_NONE = 0; +const int PRC_SPELL_EVENT_ATTACK = 1; +const int PRC_SPELL_EVENT_CONCENTRATION = 2; + +const int FEAT_SPELLS_TOUCH_ATTACK = 4092; +const int FEAT_SPELLS_RANGED_ATTACK = 4093; +const int FEAT_SPELLS_CONCENTRATION_TARGET = 4094; +const int FEAT_SPELLS_HOLD_CHARGE_TOGGLE = 4095; + +const int SPELLS_SPELLS_TOUCH_ATTACK = 3042; +const int SPELLS_SPELLS_RANGED_ATTACK = 3043; +const int SPELLS_SPELLS_CONCENTRATION_TARGET = 3044; +const int SPELLS_SPELLS_HOLD_CHARGE_TOGGLE = 3045; + +#include "prc_alterations" +#include "prc_inc_unarmed" +//#include "psi_inc_manifest" +//#include "spinc_common" +//#include "prc_spell_const" +//#include "prc_inc_combat" + +//Deletes local variables used for spell functions +// To be called (or copied) on rest, from spellhook +void CleanSpellVariables(object oPC) +{ + DeleteLocalInt(oPC, PRC_SPELL_CHARGE_COUNT); + DeleteLocalInt(oPC, PRC_SPELL_CHARGE_SPELLID); + DeleteLocalInt(oPC, PRC_SPELL_CHARGE_LEVEL); + DeleteLocalObject(oPC, PRC_SPELL_CONC_TARGET); + DeleteLocalInt(oPC, PRC_SPELL_HOLD); + DeleteLocalInt(oPC, PRC_SPELL_METAMAGIC); + DeleteLocalManifestation(oPC, PRC_POWER_HOLD_MANIFESTATION); + DeleteLocalMystery(oPC, "MYST_HOLD_MYST"); +} + +//Returns whether a spell is a touch spell +// nSpellID, the spell id (row in spells.2da) of the spell +int IsTouchSpell(int nSpellID) +{ + return (Get2DACache("spells", "Range", nSpellID) == "T"); +} + +//Sets local variables for the new spell functions +// object oCaster, the caster of the spell +// int nCharges, the number of charges for a spell +// int nSpellIDOverride, for overriding a SpellID +void SetLocalSpellVariables(object oCaster, int nCharges = 1, int nSpellIDOverride = -1) +{ + int nTemp = (nSpellIDOverride == -1) ? PRCGetSpellId() : nSpellIDOverride; + SetLocalInt(oCaster, PRC_SPELL_CHARGE_SPELLID, nTemp); + SetLocalInt(oCaster, PRC_SPELL_CHARGE_COUNT, nCharges); + SetLocalInt(oCaster, PRC_SPELL_CHARGE_LEVEL, PRCGetCasterLevel(oCaster)); + SetLocalInt(oCaster, PRC_SPELL_METAMAGIC, PRCGetMetaMagicFeat()); + FloatingTextStringOnCreature("*Holding the Charge*", oCaster); + + object oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCaster); + if (GetIsPRCCreatureWeapon(oItem)) { + RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "prc_sp_event", TRUE, FALSE); + // Add eventhook to the item + AddEventScript(oItem, EVENT_ITEM_ONHIT, "prc_sp_event", TRUE, FALSE); + + // Add the OnHitCastSpell: Unique needed to trigger the event + IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 91234.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } +} + +//Decrements the number of spell charges +// object oPC, the subject losing a charge +void DecrementSpellCharges(object oPC) +{ + int nCharges= GetLocalInt(oPC, PRC_SPELL_CHARGE_COUNT); + SetLocalInt(oPC, PRC_SPELL_CHARGE_COUNT, nCharges - 1); + FloatingTextStringOnCreature("Charges Remaining: " + IntToString(nCharges - 1), oPC); +} + +//Called from scripts, will run a spell script with local int for flags +// NOTE: Flags must be interpreted by the spell script +// This lets spell scripts handle events the way they want +// +// object oPC, the subject running the script +// int nSpellID, the spellid of the spell to run +// int nEventType, an integer containing flags defining events to use +void RunSpellScript(object oPC, int nSpellID, int nEventType) +{ + ClearAllActions(); //clears queue to prevent exploits + SetLocalInt(oPC, PRC_SPELL_EVENT, nEventType); + ActionCastSpell(nSpellID, 0, 0, 0, GetLocalInt(oPC, PRC_SPELL_METAMAGIC)); + DelayCommand(3.0, DeleteLocalInt(oPC, PRC_SPELL_EVENT)); +} + +//Called from scripts, will run a spell script with local int for flags +// NOTE: This directly executes the impactscript of the spell, which mean +// the spellid of any effect that get applied will be wrong. +// Only use this when you are sure no effect will be apllied, +// or if you really need to bypass the action queue. +void RunImpactScript(object oPC, int nSpellID, int nEventType) +{ + // we dont want to be running a 'fake' spellscript, as that will + // use ActionCastSpell, which is exactly what we are trying to avoid + int nRealSpell = GetPowerFromSpellID(nSpellID); + if (nRealSpell == -1) + nRealSpell = nSpellID; + + SetLocalInt(oPC, PRC_SPELL_EVENT, nEventType); + SetLocalInt(oPC, PRC_SPELLID_OVERRIDE, nRealSpell); + + string sScript = Get2DACache("spells", "ImpactScript", nRealSpell); + + ExecuteScript(sScript, oPC); + + DeleteLocalInt(oPC, PRC_SPELL_EVENT); + DeleteLocalInt(oPC, PRC_SPELLID_OVERRIDE); +} + +//Returns true if the spell is one of the cure spells +int IsCure(int nSpellID) +{ + return ((nSpellID >= SPELL_CURE_CRITICAL_WOUNDS) && (nSpellID <= SPELL_CURE_SERIOUS_WOUNDS)); +} + +//Returns true if the spell is one of the mass cure spells +int IsMassCure(int nSpellID) +{ + return ((nSpellID >= SPELL_MASS_CURE_LIGHT) && (nSpellID <= SPELL_MASS_CURE_CRITICAL)); +} + +//Returns true if the spell is one of the mass inflict spells +int IsMassInflict(int nSpellID) +{ + return ((nSpellID >= SPELL_MASS_INFLICT_LIGHT) && (nSpellID <= SPELL_MASS_INFLICT_CRITICAL)); +} + +//Returns true for the spell Heal +int IsHeal(int nSpellID) +{ + return nSpellID == SPELL_HEAL + || nSpellID == SPELL_MASS_HEAL; +} + +//Returns true for the Mass Heal spell (Mass Harm if it ever gets made) +int IsMassHealHarm(int nSpellID) +{ + return nSpellID == SPELL_MASS_HEAL + || nSpellID == SPELL_MASS_HARM; +} + +//Returns whether an effect should be removed +int CheckRemoveEffects(int nSpellID, int nEffectType) +{ + switch(nSpellID) + { + case SPELL_GREATER_RESTORATION: + { + return(nEffectType == EFFECT_TYPE_ABILITY_DECREASE || + nEffectType == EFFECT_TYPE_AC_DECREASE || + nEffectType == EFFECT_TYPE_ATTACK_DECREASE || + nEffectType == EFFECT_TYPE_DAMAGE_DECREASE || + nEffectType == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE || + nEffectType == EFFECT_TYPE_SAVING_THROW_DECREASE || + nEffectType == EFFECT_TYPE_SPELL_RESISTANCE_DECREASE || + nEffectType == EFFECT_TYPE_SKILL_DECREASE || + nEffectType == EFFECT_TYPE_BLINDNESS || + nEffectType == EFFECT_TYPE_DEAF || + nEffectType == EFFECT_TYPE_CURSE || + nEffectType == EFFECT_TYPE_DISEASE || + nEffectType == EFFECT_TYPE_POISON || + nEffectType == EFFECT_TYPE_PARALYZE || + nEffectType == EFFECT_TYPE_CHARMED || + nEffectType == EFFECT_TYPE_DOMINATED || + nEffectType == EFFECT_TYPE_DAZED || + nEffectType == EFFECT_TYPE_CONFUSED || + nEffectType == EFFECT_TYPE_FRIGHTENED || + nEffectType == EFFECT_TYPE_NEGATIVELEVEL || + nEffectType == EFFECT_TYPE_SLOW || + nEffectType == EFFECT_TYPE_STUNNED); + } + case SPELL_RESTORATION: + { + return(nEffectType == EFFECT_TYPE_ABILITY_DECREASE || + nEffectType == EFFECT_TYPE_AC_DECREASE || + nEffectType == EFFECT_TYPE_ATTACK_DECREASE || + nEffectType == EFFECT_TYPE_DAMAGE_DECREASE || + nEffectType == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE || + nEffectType == EFFECT_TYPE_SAVING_THROW_DECREASE || + nEffectType == EFFECT_TYPE_SPELL_RESISTANCE_DECREASE || + nEffectType == EFFECT_TYPE_SKILL_DECREASE || + nEffectType == EFFECT_TYPE_BLINDNESS || + nEffectType == EFFECT_TYPE_DEAF || + nEffectType == EFFECT_TYPE_PARALYZE || + nEffectType == EFFECT_TYPE_NEGATIVELEVEL); + break; + } + case SPELL_LESSER_RESTORATION: + { + return(nEffectType == EFFECT_TYPE_ABILITY_DECREASE || + nEffectType == EFFECT_TYPE_AC_DECREASE || + nEffectType == EFFECT_TYPE_ATTACK_DECREASE || + nEffectType == EFFECT_TYPE_DAMAGE_DECREASE || + nEffectType == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE || + nEffectType == EFFECT_TYPE_SAVING_THROW_DECREASE || + nEffectType == EFFECT_TYPE_SPELL_RESISTANCE_DECREASE || + nEffectType == EFFECT_TYPE_SKILL_DECREASE); + break; + } + case SPELL_REMOVE_BLINDNESS_AND_DEAFNESS: + { + return nEffectType == EFFECT_TYPE_BLINDNESS + || nEffectType == EFFECT_TYPE_DEAF; + } + case SPELL_REMOVE_CURSE: + { + return nEffectType == EFFECT_TYPE_CURSE; + } + case SPELL_CALM_EMOTIONS: + { + return nEffectType == EFFECT_TYPE_FRIGHTENED + || nEffectType == EFFECT_TYPE_CONFUSED; + } + case SPELLABILITY_REMOVE_DISEASE: + case SPELL_REMOVE_DISEASE: + { + return (nEffectType == EFFECT_TYPE_DISEASE || + (GetPRCSwitch(PRC_BIOWARE_REMOVE_DISEASE) && + nEffectType == EFFECT_TYPE_ABILITY_DECREASE)); + } + case SPELL_NEUTRALIZE_POISON: + { + return (nEffectType == EFFECT_TYPE_POISON || + nEffectType == EFFECT_TYPE_DISEASE || + (GetPRCSwitch(PRC_BIOWARE_NEUTRALIZE_POISON) && + nEffectType == EFFECT_TYPE_ABILITY_DECREASE)); + } + case SPELL_PANACEA: + { + return (EFFECT_TYPE_BLINDNESS == nEffectType || + EFFECT_TYPE_CONFUSED == nEffectType || + EFFECT_TYPE_DAZED == nEffectType || + EFFECT_TYPE_DEAF == nEffectType || + EFFECT_TYPE_DISEASE == nEffectType || + EFFECT_TYPE_FRIGHTENED == nEffectType || + EFFECT_TYPE_PARALYZE == nEffectType || + EFFECT_TYPE_POISON == nEffectType || + EFFECT_TYPE_SLEEP == nEffectType || + EFFECT_TYPE_STUNNED == nEffectType); + } + } + return FALSE; +} + +int IsRaySpell(int nSpell) +{ + int nRealSpell = GetPowerFromSpellID(nSpell); + if (nRealSpell != -1) + nSpell = nRealSpell; + return nSpell == SPELL_AVASCULAR_MASS // avascular mass + || nSpell == SPELL_AVASCULATE // avasculate + || nSpell == SPELL_DIMENSIONAL_ANCHOR // dimensional anchor + || nSpell == SPELL_DISINTEGRATE // disintegrate + || nSpell == SPELL_POLAR_RAY // polar ray + || nSpell == SPELL_SLASHING_DARKNESS // slashing darkness + || nSpell == SPELL_RAY_OF_FROST // ray of frost + || nSpell == SPELL_ENERGY_DRAIN // energy drain + || nSpell == SPELL_ENERVATION // enervate + || nSpell == SPELL_RAY_OF_ENFEEBLEMENT // ray of enfeeblement + || nSpell == SPELL_NEGATIVE_ENERGY_RAY // negative energy ray + || nSpell == SPELL_SEARING_LIGHT // searing light + || nSpell == SPELL_SEEKING_RAY // seeking ray + || nSpell == SPELL_SCORCHING_RAY // scorching ray + || nSpell == SPELL_PRISMATIC_RAY // prismatic ray + || nSpell == SPELL_ELECTRIC_JOLT // Electric Jolt +// || nSpell == 0) // + ; +} diff --git a/src/include/prc_spell_const.nss b/src/include/prc_spell_const.nss new file mode 100644 index 0000000..bb8b1cd --- /dev/null +++ b/src/include/prc_spell_const.nss @@ -0,0 +1,1594 @@ +//const int FOOBAR = -1; // Comment or uncomment whenever the compiler croaks on an error code 5. + +//:: NUI Spell constants +const int SPELL_NUI_POWER_ATTACK = 2691; + +//spell +const int SPELL_MAJOR_MAGIC_MISSILE = 2247; +const int SPELL_GREAT_WALL_OF_DISPEL = 2096; +const int SPELL_SOL_CONSECRATE = 2108; +const int SPELL_BLACKLIGHT = 2091; + +// bard +const int SPELL_BARD_SONG = 411; +const int SPELL_BARD_CURSE_SONG = 644; + +//:: Epic Level Handbook +const int SPELL_EPIC_SWARM_OF_ARROWS = 17996; + +//:: Bloodclaw Master +const int SPELL_BCM_RENDING_CLAWS = 17997; + +//:: Complete Warrior +const int SPELL_RANGED_DISARM = 3493; + +//marshal +const int SPELL_MINAUR_DEMFORT = 3500; +const int SPELL_MINAUR_FORCEWILL = 3501; +const int SPELL_MINAUR_WATCHEYE = 3502; +const int SPELL_MINAUR_BOOSTCHA = 3503; +const int SPELL_MINAUR_BOOSTCON = 3504; +const int SPELL_MINAUR_BOOSTDEX = 3505; +const int SPELL_MINAUR_BOOSTINT = 3506; +const int SPELL_MINAUR_BOOSTSTR = 3507; +const int SPELL_MINAUR_BOOSTWIS = 3508; +const int SPELL_MINAUR_DETCAST = 3509; +const int SPELL_MINAUR_ARTOFWAR = 3510; +const int SPELL_MAJAUR_MOT_ARDOR = 3511; +const int SPELL_MAJAUR_MOT_CARE = 3512; +const int SPELL_MAJAUR_RES_TROOPS = 3513; +const int SPELL_MAJAUR_MOT_URGE = 3514; +const int SPELL_MAJAUR_HARD_SOLDIER = 3515; +const int SPELL_MAJAUR_MOT_ATTACK = 3516; +const int SPELL_MAJAUR_STEAD_HAND = 3517; +const int SPELL_GRANT_MOVE_ACTION = 3518; +const int SPELL_MAJAUR_MOT_CHA = 3519; +const int SPELL_MAJAUR_MOT_CON = 3520; +const int SPELL_MAJAUR_MOT_DEX = 3521; +const int SPELL_MAJAUR_MOT_INT = 3522; +const int SPELL_MAJAUR_MOT_STR = 3523; +const int SPELL_MAJAUR_MOT_WIS = 3524; + +//oozemaster +const int SPELL_SLIME_WAVE = 2023; + +//damage increase spells for ActionCastSpellOnSelf +const int SPELL_SET_COMPOSITE_ATTACK_BONUS = 2732; +const int SPELL_UNARMED_ATTACK_PEN = 2734; +const int SPELL_BATTLERAGER_DAMAGE = 2728; +const int SPELL_HEXTOR_DAMAGE = 2729; +const int SPELL_HEXTOR_MODE = 3859; +const int SPELL_THAYANKNIGHT_DAMAGE = 2730; +const int SPELL_MANATARMS_DAMAGE = 2731; +const int SPELL_KNIGHTCHALICE_DAMAGE = 2733; +const int SPELL_SWASH_DAMAGE = 3210; +const int SPELL_COC_DAMAGE = 3211; +const int SPELL_OOTBI_GREATER_WEAPON_FOCUS = 2436; +const int SPELL_INTUITIVE_ATK = 2090; +const int SPELL_SHIFTING_EFFECTS = 1918; + +//heartwarder +const int SPELL_LIPS_RAPTUR = 2210; +const int SPELL_TEARS_EVERGOLD = 2211; +const int SPELL_ELE_CONF_FIRE = 2213; +const int SPELL_ELE_CONF_WATER = 2214; +const int SPELL_ELE_CONF_EARTH = 2215; +const int SPELL_ELE_CONF_AIR = 2216; +const int IP_CONST_CASTSPELL_LIPS_RAPTURE_1 =550; + +// Frenzied Berserker Spells +const int SPELL_FRENZY = 2700; +const int SPELL_INSPIRE_FRENZY = 2701; +const int SPELL_SUPREME_POWER_ATTACK = 2702; +const int SPELL_REMOVE_FRENZY = 2703; + +// Eye of Gruumsh +const int SPELL_COMMAND_THE_HORDE = 2708; +const int SPELL_BLINDING_SPITTLE = 2709; +const int SPELL_BLINDSIGHT_5_FT = 2710; +const int SPELL_BLINDSIGHT_10_FT = 2711; + +// Orc Warlord +const int SPELL_OW_INSPIRE_COURAGE = 2712; +const int SPELL_FINAL_RAGE = 2713; +const int SPELL_RELEASE_HORDE = 2714; +const int SPELL_GATHER_HORDE_I = 2715; +const int SPELL_GATHER_HORDE_II = 2716; +const int SPELL_BLOOD_OF_THE_WARLORD = 2721; + +// Tempest Spells +const int SPELL_T_TWO_WEAPON_FIGHTING = 2705; + +// Runecaster Spells +const int SPELL_RUNE_CHANT = 1623; + +// Duelist +const int SPELL_ELABORATE_PARRY = 2722; +const int SPELL_ELABORATE_PARRY_P = 2723; +const int SPELL_ELABORATE_PARRY_FD = 2724; +const int SPELL_ELABORATE_PARRY_E = 2725; +const int SPELL_ELABORATE_PARRY_IE = 2726; + +// Master Harper +const int SPELLABILITY_AURA_LLIIRA = 3991; +const int SPELLABILITY_LYCANBANE = 3993; +const int SPELLABILITY_MIELIKKI = 3992; + +//Drow Judicator +const int SPELL_COMMAND_SPIDERS = 1728; +const int SPELL_SUMMON_MYRLOCHAR = 1616; +const int SPELL_SUMMON_MONSTROUS = 1614; +const int SPELL_SUMMON_SWORDSPID = 1615; +const int SPELL_SUMMON_PHASESPID = 1617; +const int SPELL_SELVETARMS_WRATH = 1612; + +// Foe Hunter Spells +const int SPELL_RANCOR = 2706; +const int SPELL_FH_DEATH_ATTACK = 2707; + +//Legendary Dread Spell +const int SPELL_UNMOVABLE = 2750; //CONSTANT + +//Iaijutsu Master Spell +const int SPELL_ONE_STRIKE_TWO_CUTS = 2761; //CONSTANT + +// Fist Raziel Spell +const int SPELL_SMITE_RAZIEL = 2217; +const int SPELL_SU_CIRCEVIL = 2218; + +// bonded +const int SPELL_POLYMORPH_ELEM = 2235 ; + +// Initiate of Draconic +const int SPELL_SHAPEDRAGONGOLD = 2239; +const int SPELL_SHAPEDRAGONRED = 2240; +const int SPELL_SHAPEDRAGONPRYS = 2241; +const int SPELL_SHAPE_INCREASE_DAMAGE = 2258; + +// Bladesinger +const int SPELL_SONG_OF_FURY = 2200; + +//True Necromancer +const int SPELL_DES_20 = 3988; +const int SPELL_DES_100 = 3987; + +//Thrall of Orcus +const int SPELL_TO_CARRION = 1558; + +//Frostrager +const int SPELL_ONETWO_PUNCH = 2705; + +//Serene Guardian +const int SPELL_SERENE_PAIN = 1569; +const int SPELL_SERENE_DAMAGE = 1570; +const int SPELL_SERENE_STAGGER = 1571; +const int SPELL_SERENE_CONFOUND = 1572; +const int SPELL_SERENE_SOUL = 1573; + +//Shou Disciple +const int SPELL_MARTIAL_FLURRY_LIGHT = 2725; +const int SPELL_MARTIAL_FLURRY_ALL = 2726; + +// Sacred Fist +const int SPELL_SACREDSPEED = 2123; +const int SPELL_INNERARMOR = 2124; +const int SPELL_SACREDFLAME = 2125; + +// Shadow Adept +const int SPELL_SHADOWSHIELD = 2126; +const int SPELL_SHADOWWALK = 2127; +const int SPELL_SHADOWDOUBLE = 2128; + +// Nightshade +const int SPELL_NS_WEB = 2130; + +// Shadowlord +const int SHAD_INVISIBILITY_SPHERE = 2230; +const int SHAD_DARKNESS = 2226; + +// Brawler +const int SPELL_BRAWLER_EXTRA_ATT = 2161; + +// Minstrel of the Edge +const int SPELL_MINSTREL_SONG_SLEEP = 2157; +const int SPELL_MINSTREL_SONG_SILENCE = 2158; +const int SPELL_MINSTREL_SONG_HASTE = 2159; +const int SPELL_MINSTREL_SONG_SLOW = 2160; +const int SPELL_MINSTREL_SONG_CHARM = 2145; +const int SPELL_MINSTREL_SONG_IMM_FEAR = 2147; +const int SPELL_MINSTREL_SONG_SHIELD_AC = 2148; +const int SPELL_MINSTREL_SONG_SONIC_WEAP = 2149; +const int SPELL_MINSTREL_SONG_STRENGTH = 2150; +const int SPELL_MINSTREL_SONG_CONSTITUTION = 2151; +const int SPELL_MINSTREL_SONG_DEXTERITY = 2152; +const int SPELL_MINSTREL_SONG_INTELLIGENCE = 2153; +const int SPELL_MINSTREL_SONG_WISDOM = 2154; +const int SPELL_MINSTREL_SONG_CHARISMA = 2155; +const int SPELL_MINSTREL_SONG_WOUND_WHISP = 2156; + +// Ultimate Ranger +const int SPELL_UR_FAVORITE_ENEMY = 2134; +const int SPELL_UR_HAWK_TOTEM = 2140; +const int SPELL_UR_HIPS = 2143; + +// Dragonsong +const int SPELL_DSL_SONG_STRENGTH = 2135; +const int SPELL_DSL_SONG_COMPULSION = 2136; +const int SPELL_DSL_SONG_SPEED = 2137; +const int SPELL_DSL_SONG_FEAR = 2138; +const int SPELL_DSL_SONG_HEALING = 2139; + +//Anti-Paladin +const int SPELL_ANTIPAL_BULLS_STRENGTH = 2041; +const int SPELL_ANTIPAL_DARKNESS = 2042; +const int SPELL_ANTIPAL_DESECRATE = 2043; +const int SPELL_ANTIPAL_INFLICT_LIGHT_WOUNDS = 2037; +const int SPELL_ANTIPAL_INFLICT_MODERATE_WOUNDS = 2048; +const int SPELL_ANTIPAL_INFLICT_SERIOUS_WOUNDS = 2054; +const int SPELL_ANTIPAL_MAGIC_CIRCLE_AGAINST_GOOD = 2049; +const int SPELL_ANTIPAL_AURAFEAR = 2089; +const int SPELL_APAL_MOUNT = 2052; + +// Complete Adv Ninja +const int SPELL_GHOST_STEP = 2742; + +//:: Forest Master (Faiths & Pantheons, pg. 193) +const int SPELL_FM_FORESTDOMINION = 2472; +const int SPELL_FM_GREATMALLETRADIALMASTER = 2473; +const int SPELL_FM_ICYMALLET = 2474; +const int SPELL_FM_SHOCKMALLET = 2475; +const int SPELL_FM_DEEPROOTS = 2476; + +// Spell Feat Generic +const int SPELL_WANDER_UNSEEN = 4071; +const int SPELL_MANYSHOT2 = 2246; +const int SPELL_MANYSHOT3 = 2248; +const int SPELL_MANYSHOT4 = 2249; +const int SPELL_MANYSHOT5 = 2208; +const int SPELL_MANYSHOT6 = 2209; +const int SPELL_PINPOINTACCURACY2 = 2204; +const int SPELL_PINPOINTACCURACY4 = 2205; +const int SPELL_PINPOINTACCURACY6 = 2206; +const int SPELL_EXTRASHOT = 2201; +const int NimbusOfLight = 2032; +const int SPELL_HOLYRADIANCE = 2196; +const int SPELL_RAVAGEGOLDENICE = 2190; +const int ServanOfHeaven = 2033; +const int SPELL_STIGMATA2 = 2193; +const int SPELL_STIGMATA3 = 2194; +const int SPELL_STIGMATA4 = 2195; +const int SPELL_STIGMATA5 = 2192; +const int Ranged_Smite = 2034; + +const int SPELL_POWER_ATTACK1 = 2171; +const int SPELL_POWER_ATTACK2 = 2172; +const int SPELL_POWER_ATTACK3 = 2173; +const int SPELL_POWER_ATTACK4 = 2174; +const int SPELL_POWER_ATTACK5 = 2175; +const int SPELL_POWER_ATTACK6 = 2177; +const int SPELL_POWER_ATTACK7 = 2178; +const int SPELL_POWER_ATTACK8 = 2179; +const int SPELL_POWER_ATTACK9 = 2180; +const int SPELL_POWER_ATTACK10 = 2181; +const int SPELL_FURIOUS_ASSAULT = 2182; +const int SPELL_DIVINE_RESISTANCE = 2166; +const int SPELL_DIVINE_CLEANSING = 2165; +const int SPELL_DIVINE_VIGOR = 2164; +const int SPELL_EPIC_DIVINE_VIGOR = 2162; + +// VFX Persistant +const int VFX_MOB_CIRCEVIL_NODIS = 110 ; + + +// Custom Spell ID's +const int SPELL_ANIMATE_OBJECT = 1790; +const int SPELL_ANIMAL_TRANCE = 1574; +const int SPELL_BLINK = 1575; +const int SPELL_DETECT_MAGIC = 1576; +const int SPELL_ENTHRALL = 2063; +const int SPELL_ACID_ORB = 3100; +const int SPELL_COLD_ORB = 3101; +const int SPELL_ELECTRIC_ORB = 3102; +const int SPELL_FIRE_ORB = 3103; +const int SPELL_SONIC_ORB = 3104; +const int SPELL_ACID_STORM = 3106; +const int SPELL_BLAST_OF_FLAME = 3110; +const int SPELL_BURNING_BOLT = 3109; +const int SPELL_DISINTEGRATE = 3111; +const int SPELL_ENERGY_IMMUNITY = 3113; +const int SPELL_ICE_BURST = 3105; +const int SPELL_LOWER_SR = 3126; +const int SPELL_MANTLE_OF_EGREG_MIGHT = 3112; +const int SPELL_MASS_BULLS_STRENGTH = 3119; +const int SPELL_MASS_CATS_GRACE = 3120; +const int SPELL_MASS_EAGLES_SPLENDOR = 3121; +const int SPELL_MASS_ENDURANCE = 3122; +const int SPELL_MASS_FOXS_CUNNING = 3123; +const int SPELL_MASS_HOLD_MONSTER = 3108; +const int SPELL_MASS_HOLD_PERSON = 3107; +const int SPELL_MASS_OWLS_WISDOM = 3124; +const int SPELL_MASS_ULTRAVISION = 3125; +const int SPELL_SERPENTS_SIGH = 3127; +const int SPELL_SERPENTS_SIGH_BOLT_ACID = 3128; +const int SPELL_SERPENTS_SIGH_BOLT_LIGHTNING = 3129; +const int SPELL_SERPENTS_SIGH_CONE_ACID = 3130; +const int SPELL_SERPENTS_SIGH_CONE_COLD = 3131; +const int SPELL_SERPENTS_SIGH_CONE_FIRE = 3132; +const int SPELL_HEROISM = 3133; +const int SPELL_GREATER_HEROISM = 3134; +const int SPELL_MASS_CURE_LIGHT = 3135; +const int SPELL_MASS_CURE_MODERATE = 3136; +const int SPELL_MASS_CURE_SERIOUS = 3137; +const int SPELL_MASS_CURE_CRITICAL = 3138; +const int SPELL_RIGHTEOUS_MIGHT = 3139; +const int SPELL_RECITATION = 3140; +const int SPELL_FORCEBLAST = 3141; +const int SPELL_GLITTERDUST = 3142; +const int SPELL_MORDENKAINENS_MAGNIFICENT_MANSION = 3143; +const int SPELL_LESSER_ACID_ORB = 3144; +const int SPELL_LESSER_COLD_ORB = 3145; +const int SPELL_LESSER_ELECTRIC_ORB = 3146; +const int SPELL_LESSER_FIRE_ORB = 3147; +const int SPELL_LESSER_SONIC_ORB = 3148; +const int SPELL_BALEFUL_TRANSPOSITION = 3149; +const int SPELL_BENIGN_TRANSPOSITION = 3150; +const int SPELL_CONVICTION = 3151; +const int SPELL_LEGIONS_CONVICTION = 3152; +const int SPELL_CURSE_OF_IMPENDING_BLADES = 3153; +const int SPELL_LEGIONS_CURSE_OF_IMPENDING_BLADES = 3154; +const int SPELL_CURSE_OF_PETTY_FAILING = 3155; +const int SPELL_LEGIONS_CURSE_OF_PETTY_FAILING = 3156; +const int SPELL_DIVINE_PROTECTION = 3157; +const int SPELL_FIREBURST = 3158; +const int SPELL_GREATER_FIREBURST = 3159; +const int SPELL_LIONHEART = 3160; +const int SPELL_LIVING_UNDEATH = 3161; +const int SPELL_PANACEA = 3162; +const int SPELL_LEGIONS_SHIELD_OF_FAITH = 3163; +const int SPELL_SLASHING_DARKNESS = 3164; +const int SPELL_ANGAZZARS_SCORCHER = 3165; +const int SPELL_CREATE_MAGIC_TATTOO = 3166; +const int SPELL_FLASHBURST = 3168; +const int SPELL_FLENSING = 3169; +const int SPELL_SHADOW_SPRAY = 3170; +const int SPELL_SNILLOCS_SNOWBALL_SWARM = 3171; +const int SPELL_BELTYNS_BURNING_BLOOD = 3172; +const int SPELL_ILYYKURS_MANTLE = 3173; +const int SPELL_IMPROVED_MAGE_ARMOR = 3174; +const int SPELL_NYBORS_GENTLE_REMINDER = 3175; +const int SPELL_NYBORS_STERN_REPROOF = 3176; +const int SPELL_SINSABURS_BALEFUL_BOLT = 3177; +const int SPELL_SNILLOCS_SNOWBALL = 3178; +const int SPELL_SOULSCOUR = 3179; +const int SPELL_SPHERE_OF_ULTIMATE_DESTRUCTION = 3180; +const int SPELL_CLARITY_OF_MIND = 3181; +const int SPELL_MASS_DROWN = 3182; +const int SPELL_HAIL_OF_STONE = 3183; +const int SPELL_SPIDERSKIN = 3184; +const int SPELL_VISCID_GLOB = 3185; +const int SPELL_WORD_OF_BALANCE = 3186; +const int SPELL_GREENFIRE = 3187; +const int SPELL_CHILL_TOUCH = 1791; +const int SPELL_POX = 2886; +const int SPELL_PESTILENCE = 2887; +const int SPELL_DETECT_EVIL = 3201; +const int SPELL_DETECT_GOOD = 3202; +const int SPELL_DETECT_LAW = 3203; +const int SPELL_DETECT_CHAOS = 3204; +const int SPELL_UNDETECTABLE_ALINGMENT = 3205; +const int SPELL_UNHOLYSWORD = 3206; +const int SPELL_DEEPER_DARKNESS = 3207; +const int SPELL_HOLY_WORD = 3220; +const int SPELL_BLASPHEMY = 3221; +const int SPELL_DICTUM = 3222; +const int SPELL_WORD_OF_CHAOS = 3223; +const int SPELL_BLUR = 3208; +const int SPELL_MIRROR_IMAGE = 3209; +const int SPELL_INSANITY = 3212; +//const int SPELL_IRON_BODY = 3213; +const int SPELL_DIMENSIONAL_ANCHOR = 2889; +const int SPELL_DIMENSION_DOOR = 2890; +const int SPELL_DIMENSION_DOOR_SELF = 2891; +const int SPELL_DIMENSION_DOOR_PARTY = 2892; +const int SPELL_DIMENSION_DOOR_DIRDIST_SELF = 2896; +const int SPELL_DIMENSION_DOOR_DIRDIST_PARTY = 2897; +const int SPELL_MAZE = 2888; +const int SPELL_GREATER_TELEPORT = 2893; +const int SPELL_GREATER_TELEPORT_SELF = 2894; +const int SPELL_GREATER_TELEPORT_PARTY = 2895; +const int SPELL_TELEPORTATION_CIRCLE = 2877; +const int SPELL_DIMENSIONAL_LOCK = 2898; +const int SPELL_TELEPORT = 2874; +const int SPELL_TELEPORT_SELF = 2875; +const int SPELL_TELEPORT_PARTY = 2876; +const int SPELL_BLOOD_OF_THE_MARTYR = 3099; +const int SPELL_POLAR_RAY = 3098; +const int SPELL_COMMAND_APPROACH = 3093; +const int SPELL_COMMAND_DROP = 3094; +const int SPELL_COMMAND_FALL = 3095; +const int SPELL_COMMAND_FLEE = 3096; +const int SPELL_COMMAND_HALT = 3097; +const int SPELL_GREATER_COMMAND_APPROACH = 3087; +const int SPELL_GREATER_COMMAND_DROP = 3088; +const int SPELL_GREATER_COMMAND_FALL = 3089; +const int SPELL_GREATER_COMMAND_FLEE = 3090; +const int SPELL_GREATER_COMMAND_HALT = 3091; +const int SPELL_VISION_OF_HEAVEN = 3083; +const int SPELL_SWORD_OF_CONSCIENCE = 3084; +const int SPELL_OBSCURING_MIST = 3085; +const int SPELL_SHOUT = 1953; +const int SPELL_SHOUT_GREATER = 1954; +const int SPELL_SPELL_JUMP = 2070; +const int SPELL_IRON_BODY = 2071; +const int SPELL_ENLARGE_PERSON = 2072; +const int SPELL_ENLARGE_PERSON_MASS = 2073; +const int SPELL_REDUCE_PERSON = 2074; +const int SPELL_REDUCE_PERSON_MASS = 2075; +const int SPELL_ANTIMAGIC_FIELD = 2076; +const int SPELL_NONDETECTION = 2077; +const int SPELL_RAINBOW_PATTERN = 2078; +const int SPELL_MAGE_HAND = 2079; +const int SPELL_DETECT_UNDEAD = 2080; +const int SPELL_PNP_SCARE = 1789; +const int SPELL_CONTAGIOUS_TOUCH = 2227; +const int SPELL_SUDDEN_STALAGMITE = 3827; +const int SPELL_BONES_OF_THE_EARTH = 3828; +const int SPELL_PHANTOM_STEED = 2347; +const int SPELL_GASEOUS_FORM = 2348; + +//:: Racial spell additions +const int SPIRETOP_FOG_CLOUD_BREATH = 1487; //:: Spiretop Dragon +const int MEPHLING_BREATH_WEAPON = 1488; //:: Mephlings +const int SPELL_ARANEA_ALTER = 1489; //:: Aranea +const int SPELL_ARANEA_ALTER_HUMANOID = 1490; +const int SPELL_ARANEA_ALTER_HYBRID = 1491; +const int SPELL_ARANEA_ALTER_SPIDER = 1492; +const int SPELL_ARANEA_WEB = 1493; +const int SPELL_MUCK_SQUIRT = 1494; //:: Muckdweller +const int SPELL_RAKSHASA_DISGUISE = 1951; +const int SPELL_FEYRI_ALTER = 1955; +const int SPELL_NIXIE_WATERBREATHING = 1956; +const int SPELL_URDINNIR_STONESKIN = 1957; +const int SPELL_RACE_DARKNESS = 1958; +const int SPELL_RACE_DAZE = 1959; +const int SPELL_RACE_LIGHT = 1960; +const int SPELL_SVIRF_BLINDDEAF = 1961; +const int SPELL_DUERGAR_INVIS = 1962; +const int SPELL_ILLITHID_CHARM_MONSTER = 1963; +const int SPELL_RACE_CHARM_PERSON = 1964; +const int SPELL_AVARIEL_DIVE = 1966; +const int SPELL_MINOTAUR_CHARGE = 1967; +const int SPELL_RACE_BLUR = 1968; +const int SPELL_FEYRI_ENERVATION = 1969; +const int SPELL_RACE_ENTANGLE = 1970; +const int SPELL_RACE_FEAR = 1971; +const int SPELL_RACE_CLAIR = 1972; +const int SPELL_RACE_NEUTRALIZE_POISON = 1973; +const int SPELL_PIXIE_CONFUSION = 1974; +const int SPELL_PIXIE_IMPINVIS = 1975; +const int SPELL_PIXIE_DISPEL = 1976; +const int SPELL_RACE_CHILLTOUCH = 1977; +const int SPELL_RACE_SILENCE = 1989; +const int SPELL_RACE_MAGE_HAND = 1990; +const int SPELL_HOUND_AURAMENACE = 2905; +const int SPELL_HOUND_AID = 2930; +const int SPELL_HOUND_CONTFLAME = 2932; +const int SPELL_HOUND_DETECTEVIL = 2933; +const int SPELL_HOUND_DISGUISE = 2936; +const int SPELL_HOUND_MAGICCIRCLE = 2934; +const int SPELL_HOUND_TELEPORT = 2935; +const int SPELL_ZENYTH_TRUE_STRIKE = 2937; +const int SPELL_RACIAL_CIRCLE_VS_GOOD = 2938; +const int SPELL_RACIAL_CIRCLE_VS_EVIL = 2939; +const int SPELL_RACIAL_CIRCLE_VS_LAW = 2940; +const int SPELL_RACIAL_CIRCLE_VS_CHAOS = 2941; +const int SPELL_NATHRI_EXPEDITIOUS_RETREAT = 2943; +const int SPELL_BLADELING_RAZOR_STORM = 2944; +const int SPELL_DRIDER_DETECTGOOD = 2945; +const int SPELL_DRIDER_DETECTLAW = 2946; +const int SPELL_KAPAK_APPLY_SALIVA = 2947; +const int SPELL_KAPAK_ONHIT_POISON = 2948; +const int SPELL_IRDA_FLARE = 2922; +const int SPELL_ZAKYA_VAMPIRIC_TOUCH = 1996; +const int SPELL_MINOR_SHAPE_CHANGE = 2907; +const int SPELL_CHANGLING_CHANGE_SHAPE_LEARN = 1943; +const int SPELL_CHANGLING_CHANGE_SHAPE_OPTIONS = 1944; +const int SPELL_CHANGLING_CHANGE_SHAPE_TRUE = 1945; +const int SPELL_CHANGLING_CHANGE_SHAPE_QS1 = 1946; +const int SPELL_CHANGLING_CHANGE_SHAPE_QS2 = 1947; +const int SPELL_IRDA_CHANGE_SHAPE_LEARN = 1937; +const int SPELL_IRDA_CHANGE_SHAPE_OPTIONS = 1938; +const int SPELL_IRDA_CHANGE_SHAPE_TRUE = 1939; +const int SPELL_IRDA_CHANGE_SHAPE_QS1 = 1940; +const int SPELL_IRDA_CHANGE_SHAPE_QS2 = 1941; +const int SPELL_QUICK_CHANGE_SHAPE_LEARN = 2909; +const int SPELL_QUICK_CHANGE_SHAPE_OPTIONS = 2910; +const int SPELL_QUICK_CHANGE_SHAPE_TRUE = 2911; +const int SPELL_QUICK_CHANGE_SHAPE_QS1 = 2912; +const int SPELL_QUICK_CHANGE_SHAPE_QS2 = 2913; +const int SPELL_FEYRI_CHANGE_SHAPE_LEARN = 2951; +const int SPELL_FEYRI_CHANGE_SHAPE_OPTIONS = 2952; +const int SPELL_FEYRI_CHANGE_SHAPE_TRUE = 2953; +const int SPELL_FEYRI_CHANGE_SHAPE_QS1 = 2954; +const int SPELL_FEYRI_CHANGE_SHAPE_QS2 = 2955; +const int SPELL_NYMPH_DIMDOOR_SELF = 2957; +const int SPELL_NYMPH_DIMDOOR_PARTY = 2958; +const int SPELL_NYMPH_DIMDOOR_DIST_SELF = 2959; +const int SPELL_NYMPH_DIMDOOR_DIST_PARTY = 2960; +const int SPELL_NYMPH_STUNNING_GLANCE = 1948; +const int SPELL_NYMPH_BLINDING_BEAUTY = 1949; +const int SPELL_GRIG_PYROTECHNICS_FIREWORKS = 2915; +const int SPELL_GRIG_PYROTECHNICS_SMOKE = 2916; +const int SPELL_BRALANI_LIGHTNING_BOLT = 2917; +const int SPELL_BRALANI_CURE_SERIOUS_WOUNDS = 2918; +const int SPELL_BRALANI_MIRROR_IMAGE = 2920; +const int SPELL_BRALANI_GUST_OF_WIND = 2919; +const int SPELL_RAKSHASA_CHANGE_SHAPE_LEARN = 2962; +const int SPELL_RAKSHASA_CHANGE_SHAPE_OPTIONS = 2963; +const int SPELL_RAKSHASA_CHANGE_SHAPE_TRUE = 2964; +const int SPELL_RAKSHASA_CHANGE_SHAPE_QS1 = 2965; +const int SPELL_RAKSHASA_CHANGE_SHAPE_QS2 = 2966; +const int SPELL_FORESTLORD_TREEWALK_SELECTED = 2967; +const int SPELL_FORESTLORD_TREEWALK_DIRDIST = 2968; +const int SPELL_ASHRATI_BODYLAMP = 2289; +const int SPELL_ASHRATI_DAZZLE = 2332; +const int SPELL_HYBSIL_MIRROR_IMAGE = 1578; +const int SPELL_HYBSIL_DANCLIGHTS = 1579; +const int SPELL_HYBSIL_JUMP = 1580; +const int SPELL_ARKAMOI_STRENGTH = 19011; +const int SPELL_LASHEMOI_STRENGTH = 19012; +const int SPELL_TURLEMOI_STRENGTH = 19013; +const int SPELL_HADRIMOI_STRENGTH = 19014; +const int SPELL_GLOURA_GRACE = 19015; + +// Poison system spells +const int SPELL_POISONED_WEAPON = 2880; +const int SPELL_GRENADE_POISONVIAL = 2881; +const int SPELL_POISON_WEAPON = 2882; +const int SPELL_POISON_ITEM = 2883; +const int SPELL_POISON_FOOD = 2884; +const int SPELL_CLEAN_POISON_OFF = 2885; + + +// Psionic feat spells +const int SPELL_FEAT_SPEED_OF_THOUGHT_BONUS = 2820; + +// Active feat spells +const int SPELL_JUMP = 2727; + +// Havoc Mage +const int SPELL_BATTLECAST = 2025; + +// Lasher +const int SPELL_LASHER_THIRD_HAND = 1910; +const int SPELL_LASHER_CRACK_FATE = 1911; +const int SPELL_LASHER_STUN_SNAP = 1912; +const int SPELL_LASHER_DEATH_SPIRAL = 1913; +const int SPELL_LASHER_LASHW = 1914; +const int SPELL_LASHER_CRACK_DOOM = 1917; + +// Warchief +const int SPELL_TRIBAL_FRENZY = 1597; + + +//Baelnorn +const int SPELL_END_PROJECTION = 2899; +const int SPELL_BAELNORN_PROJECTION = 2902; +const int SPELL_BAELNORN_TOUCH = 2903; +const int SPELL_BAELN_EYES = 2027; + +//Peerless Archer Feats +const int SPELL_PA_FLETCH_1 = 1515; +const int SPELL_PA_FLETCH_2 = 1516; +const int SPELL_PA_FLETCH_3 = 1517; +const int SPELL_PA_FLETCH_4 = 1518; +const int SPELL_PA_FLETCH_5 = 1519; +const int SPELL_PA_POWERSHOT = 1520; +const int SPELL_PA_IMP_POWERSHOT = 1521; +const int SPELL_PA_SUP_POWERSHOT = 1522; + +//Tenjac's spells +const int SPELL_AVASCULATE = 2851; +const int SPELL_AVASCULAR_MASS = 2852; +const int SPELL_OTILUKES_RS = 2853; +const int SPELL_NECROTIC_AWARENESS = 2854; +const int SPELL_NECROTIC_CYST = 2855; +const int SPELL_NECROTIC_BLOAT = 2856; +const int SPELL_NECROTIC_DOMINATION = 2857; +const int SPELL_NECROTIC_BURST = 2858; +const int SPELL_NECROTIC_ERUPTION = 2859; +const int SPELL_NECROTIC_EMPOWERMENT = 2860; +const int SPELL_NECROTIC_TERMINATION = 2861; +const int SPELL_GHOUL_GAUNTLET = 2862; +const int SPELL_ENERGY_EBB = 2863; +const int SPELL_NIGHTS_CARESS = 2864; + +const int SPELL_ENERGIZE_POTION_ACID = 2489; +const int SPELL_ENERGIZE_POTION_COLD = 2490; +const int SPELL_ENERGIZE_POTION_ELECTRICITY = 2491; +const int SPELL_ENERGIZE_POTION_FIRE = 2492; +const int SPELL_ENERGIZE_POTION_SONIC = 2493; +const int SPELL_DIVINE_SACRIFICE_2 = 2494; +const int SPELL_DIVINE_SACRIFICE_4 = 2495; +const int SPELL_DIVINE_SACRIFICE_6 = 2496; +const int SPELL_DIVINE_SACRIFICE_8 = 2497; +const int SPELL_DIVINE_SACRIFICE_10 = 2498; +const int SPELL_COMMAND_UNDEAD = 2499; + +const int SPELL_ABERRATE = 2512; +const int SPELL_ABSORB_STRENGTH = 2513; +const int SPELL_ABYSSAL_MIGHT = 2514; +const int SPELL_ANGRY_ACHE = 2515; +const int SPELL_APOCALYPSE_FROM_THE_SKY = 2516; +const int SPELL_APOCALYPSE_FROM_THE_SKY_FIRE = 2517; +const int SPELL_APOCALYPSE_FROM_THE_SKY_ACID = 2518; +const int SPELL_APOCALYPSE_FROM_THE_SKY_SONIC = 2519; +const int SPELL_BESTOW_WOUND = 2520; +const int SPELL_BODAK_BIRTH = 2521; +const int SPELL_BONEBLADE = 2522; +const int SPELL_BONEBLADE_GREATSWORD = 2523; +const int SPELL_BONEBLADE_LONGSWORD = 2524; +const int SPELL_BONEBLADE_SHORTSWORD = 2525; +const int SPELL_BONEBLAST = 2526; +const int SPELL_CALL_DRETCH_HORDE = 2527; +const int SPELL_CALL_LEMURE_HORDE = 2528; +const int SPELL_CLAWS_OF_THE_BEBILITH = 2529; +const int SPELL_CLAWS_OF_THE_SAVAGE = 2530; +const int SPELL_CLOUD_OF_THE_ACHAIERAI = 2531; +const int SPELL_CLUTCH_OF_ORCUS = 2532; +const int SPELL_CRUSHING_FIST_OF_SPITE = 2533; +const int SPELL_CURSE_OF_THE_PUTRID_HUSK = 2534; +const int SPELL_DAMNING_DARKNESS = 2535; +const int SPELL_DEATH_BY_THORNS = 2536; +const int SPELL_DEMONCALL = 2537; +const int SPELL_DEMONFLESH = 2538; +const int SPELL_DESPOIL = 2539; +const int SPELL_DEVILS_EGO = 2540; +const int SPELL_DEVILS_EYE = 2541; +const int SPELL_DEVILS_TAIL = 2542; +const int SPELL_DREAD_WORD = 2543; +const int SPELL_DRUG_RESISTANCE = 2544; +const int SPELL_ECTOPLASMIC_ENCHANCEMENT = 2545; +const int SPELL_ETERNITY_OF_TORTURE = 2546; +const int SPELL_EVIL_EYE = 2547; +const int SPELL_EYE_OF_THE_BEHOLDER = 2548; +const int SPELL_EVIL_WEATHER = 2549; +const int SPELL_EVIL_WEATHER_RAIN_OF_BLOOD = 2550; +const int SPELL_EVIL_WEATHER_VIOLET_RAIN = 2551; +const int SPELL_EVIL_WEATHER_GREEN_FOG = 2552; +const int SPELL_EVIL_WEATHER_RAIN_OF_FISH = 2553; +const int SPELL_EXTRACT_DRUG = 2554; +const int SPELL_EXTRACT_BACCARAN = 2555; +const int SPELL_EXTRACT_VODARE = 2556; +const int SPELL_EXTRACT_SANNISH = 2557; +const int SPELL_EXTRACT_MUSHROOM_POWDER = 2558; +const int SPELL_FANGS_OF_THE_VAMPIRE_KING = 2559; +const int SPELL_FIENDISH_CLARITY = 2560; +const int SPELL_FLESH_ARMOR = 2561; +const int SPELL_FLESH_RIPPER = 2562; +const int SPELL_GRIM_REVENGE = 2563; +const int SPELL_GUTWRENCH = 2564; +const int SPELL_HEARTACHE = 2565; +const int SPELL_HEARTCLUTCH = 2566; +const int SPELL_HELLFIRE = 2567; +const int SPELL_HELLFIRE_STORM = 2568; +const int SPELL_HELLS_POWER = 2569; +const int SPELL_LAHMS_FINGER_DARTS = 2570; +const int SPELL_LIQUID_PAIN = 2571; +const int SPELL_MORALITY_UNDONE = 2572; +const int SPELL_POWER_LEECH = 2573; +const int SPELL_RAPTURE_OF_RUPTURE = 2574; +const int SPELL_REALITY_BLIND = 2575; +const int SPELL_RED_FESTER = 2576; +const int SPELL_RESONATING_RESISTANCE = 2577; +const int SPELL_ROTTING_CURSE_OF_URFESTRA = 2578; +const int SPELL_SEETHING_EYEBANE = 2579; +const int SPELL_SHRIVELING = 2580; +const int SPELL_SONG_OF_FESTERING_DEATH = 2581; +const int SPELL_SORROW = 2582; +const int SPELL_EXALTED_RAIMENT = 2583; +const int SPELL_SPORES_OF_THE_VROCK = 2584; +const int SPELL_STUNNING_SCREECH = 2585; +const int SPELL_STOP_HEART = 2586; +const int SPELL_THOUSAND_NEEDLES = 2587; +const int SPELL_TONGUE_OF_BAALZEBUL = 2588; +const int SPELL_TOUCH_OF_JUIBLEX = 2589; +const int SPELL_UNHEAVENED = 2590; +const int SPELL_UNLIVING_WEAPON = 2591; +const int SPELL_UTTERDARK = 2592; +const int SPELL_WAVE_OF_GRIEF = 2593; +const int SPELL_WAVE_OF_PAIN = 2594; +const int SPELL_WRACK = 2595; +const int SPELL_WRETCHED_BLIGHT = 2596; +const int SPELL_AGONY = 2597; +const int SPELL_BACCARAN = 2598; +const int SPELL_DEVILWEED = 2599; +const int SPELL_LUHIX = 2600; +const int SPELL_MUSHROOM_POWDER = 2601; +const int SPELL_SANNISH = 2602; +const int SPELL_TERRAN_BRANDY = 2603; +const int SPELL_VODARE = 2604; +const int SPELL_LESSER_SHIVERING_TOUCH = 2605; +const int SPELL_SHIVERING_TOUCH = 2606; +const int SPELL_AYAILLAS_RADIANT_BURST = 2607; +const int SPELL_BRILLIANT_EMANATION = 2608; +const int SPELL_DIVINE_INSPIRATION = 2609; +const int SPELL_DIAMOND_SPRAY = 2610; +const int SPELL_DRAGON_CLOUD = 2611; +const int SPELL_EXALTED_FURY = 2612; +const int SPELL_HAMMER_OF_RIGHTEOUSNESS = 2613; +const int SPELL_PHIERANS_RESOLVE = 2614; +const int SPELL_PHOENIX_FIRE = 2615; +const int SPELL_RAIN_OF_EMBERS = 2616; +const int SPELL_SICKEN_EVIL = 2617; +const int SPELL_STORM_OF_SHARDS = 2618; +const int SPELL_SUNMANTLE = 2619; +const int SPELL_TWILIGHT_LUCK = 2620; +const int SPELL_AMBER_SARCOPHAGUS = 2621; +const int SPELL_BLINDING_GLORY = 2622; +const int SPELL_BOLT_OF_GLORY = 2623; +const int SPELL_CALL_FAITHFUL_SERVANTS = 2624; +const int SPELL_CELESTIAL_BLOOD = 2625; +const int SPELL_CHAAVS_LAUGH = 2626; +const int SPELL_CONVERT_WAND = 2627; +const int SPELL_DANCING_WEB = 2628; +const int SPELL_DIVINE_SACRIFICE = 2629; +const int SPELL_ELATION = 2630; +const int SPELL_ENERGIZE_POTION = 2631; +const int SPELL_EYES_OF_THE_AVORAL = 2632; +const int SPELL_INSPIRED_AIM = 2633; +const int SPELL_LANTERN_LIGHT = 2634; +const int SPELL_LAST_JUDGEMENT = 2635; +const int SPELL_LUMINOUS_ARMOR = 2636; +const int SPELL_GREATER_LUMINOUS_ARMOR = 2637; +const int SPELL_RAIN_OF_BLACK_TULIPS = 2638; +const int SPELL_RAIN_OF_ROSES = 2639; +const int SPELL_RAY_OF_HOPE = 2640; +const int SPELL_RIGHTEOUS_SMITE = 2641; +const int SPELL_STARMANTLE = 2642; +const int SPELL_TOMB_OF_LIGHT = 2643; +const int SPELL_MASOCHISM = 2644; +const int SPELL_ADDICTION = 2645; +const int SPELL_ADDICTION_TERRAN_BRANDY = 2646; +const int SPELL_ADDICTION_MUSHROOM_POWDER = 2647; +const int SPELL_ADDICTION_VODARE = 2648; +const int SPELL_ADDICTION_AGONY = 2649; + +const int SPELL_ANIMALISTIC_POWER = 1741; +const int SPELL_BIGBYS_STRIKING_FIST = 1742; +const int SPELL_BIGBYS_TRIPPING_HAND = 1743; +const int SPELL_BLADE_OF_BLOOD = 1744; +const int SPELL_BLADE_OF_BLOOD_NRM = 1745; +const int SPELL_BLADE_OF_BLOOD_EMP = 1746; +const int SPELL_AUGMENT_FAMILIAR = 1796; +const int SPELL_FALSE_LIFE = 1799; +const int SPELL_PROTECTION_FROM_ARROWS = 1800; +const int SPELL_TOUCH_OF_IDIOCY = 1801; +const int SPELL_DEEP_SLUMBER = 1802; +const int SPELL_HOUND_OF_DOOM = 1803; +const int SPELL_REPEL_VERMIN = 1804; +const int SPELL_BALEFUL_POLYMORPH = 1805; +const int SPELL_BREAK_ENCHANTMENT = 1806; +const int SPELL_CURSED_BLADE = 1807; +const int SPELL_SOLID_FOG = 1808; +const int SPELL_DETECT_SCRYING = 1809; + +const int SPELL_CHANNELED_PYROBURST = 1810; +const int SPELL_CHANNELED_PYROBURST_1 = 1811; +const int SPELL_CHANNELED_PYROBURST_2 = 1812; +const int SPELL_CHANNELED_PYROBURST_3 = 1813; +const int SPELL_CHANNELED_PYROBURST_4 = 1814; +const int SPELL_CROWN_OF_MIGHT = 1815; +const int SPELL_CROWN_OF_PROTECTION = 1816; +const int SPELL_LESSER_DEFLECT = 1817; +const int SPELL_DEFLECT = 1797; +const int SPELL_DIMENSION_HOP = 1818; +const int SPELL_DISPELLING_TOUCH = 1819; +const int SPELL_DOOM_SCARABS = 1820; +const int SPELL_ENERGY_AEGIS = 1821; +const int SPELL_ENERGY_AEGIS_ACID = 1822; +const int SPELL_ENERGY_AEGIS_COLD = 1823; +const int SPELL_ENERGY_AEGIS_ELEC = 1824; +const int SPELL_ENERGY_AEGIS_FIRE = 1825; +const int SPELL_ENERGY_AEGIS_SONIC = 1826; +const int SPELL_ENERGY_SURGE = 1827; +const int SPELL_ENERGY_SURGE_ACID = 1828; +const int SPELL_ENERGY_SURGE_COLD = 1829; +const int SPELL_ENERGY_SURGE_ELEC = 1830; +const int SPELL_ENERGY_SURGE_FIRE = 1831; +const int SPELL_ENERGY_SURGE_SONIC = 1832; +const int SPELL_HALT = 1833; +const int SPELL_KELGORES_FIRE_ORB = 1834; +const int SPELL_REGROUP = 1835; +const int SPELL_ROUSE = 1836; +const int SPELL_SEEKING_RAY = 1837; +const int SPELL_SLASHING_DISPEL = 1838; +const int SPELL_SONIC_SHIELD = 1839; +const int SPELL_SURE_STRIKE = 1840; +const int SPELL_TRUE_RESURRECTION = 1855; +const int SPELL_CALM_EMOTIONS = 1852; +const int SPELL_FROGS_AND_FISH = 2646; +const int SPELL_REPULSION = 1853; + +const int SPELL_SARCOPHAGUS_OF_STONE = 3662; +const int SPELL_ARROW_OF_BONE = 3663; +const int SPELL_DRACONIC_MIGHT = 3664; +const int SPELL_EXTRACT_WATER_ELEMENTAL = 3665; +const int SPELL_HEART_RIPPER = 3666; +const int SPELL_LIFE_BOLT = 3667; +const int SPELL_RAINBOW_BLAST = 3668; +const int SPELL_TOWERING_OAK = 3669; +const int SPELL_LIFE_BOLT_1_BOLT = 3670; +const int SPELL_LIFE_BOLT_2_BOLTS = 3671; +const int SPELL_LIFE_BOLT_3_BOLTS = 3672; +const int SPELL_LIFE_BOLT_4_BOLTS = 3673; +const int SPELL_LIFE_BOLT_5_BOLTS = 3674; + +const int SPELL_DISRUPT_UNDEAD = 3675; +const int SPELL_OTILUKES_RESILIENT_SPHERE = 3676; +const int SPELL_FIST_OF_STONE = 3677; +const int SPELL_BLADES_OF_FIRE = 3678; +const int SPELL_FIRE_TRAP = 3679; +const int SPELL_PYROTECHNICS = 3680; +const int SPELL_ICE_KNIFE = 3681; +const int SPELL_MASS_FIRE_SHIELD_RED = 3682; +const int SPELL_WHIRLING_BLADE = 3683; +const int SPELL_PNP_FIRE_SHIELD = 3684; +const int SPELL_RING_OF_BLADES = 3685; +const int SPELL_SLEET_STORM = 3686; +const int SPELL_ORB_OF_FORCE = 3687; +const int SPELL_MASS_FIRE_SHIELD = 3688; +const int SPELL_PRISMATIC_RAY = 3689; +const int SPELL_FIRE_SEEDS = 3690; +const int SPELL_OTILUKES_FREEZING_SPHERE = 3691; +const int SPELL_WAVES_OF_EXHAUSTION = 3692; +const int SPELL_PRISMATIC_WALL = 3693; +const int SPELL_SCINTILLATING_PATTERN = 3694; +const int SPELL_PRISMATIC_SPHERE = 3695; +const int SPELL_PYROTECHNICS_FIREWORKS = 3696; +const int SPELL_PYROTECHNICS_SMOKE = 3697; +const int SPELL_GREATER_DISRUPT_UNDEAD = 3698; +const int SPELL_PNP_FIRE_SHIELD_RED = 3699; +const int SPELL_PNP_FIRE_SHIELD_BLUE = 3700; +const int SPELL_MASS_FIRE_SHIELD_BLUE = 3701; +const int SPELL_SHOCKING_GRASP = 3055; +const int SPELL_GLIBNESS = 2081; + +const int SPELL_ARROW_STORM = 3702; +const int SPELL_ARROW_SPLIT = 3703; +const int SPELL_BLOODFREEZE_ARROW = 3704; +const int SPELL_DOUBLESTRIKE_ARROW = 3706; +const int SPELL_DETECT_FAVORED_ENEMIES = 3707; +const int SPELL_DARKFLAME_ARROW = 3708; +const int SPELL_HEAL_ANIMAL_COMPANION = 3709; +const int SPELL_SHADOW_ARROW = 3710; +const int SPELL_SNARE = 3711; +const int SPELL_SPELLSLAYER_ARROW = 3712; + +const int SPELL_DAYLIGHT = 2971; +const int SPELL_CLOSE_WOUNDS = 1849; +const int SPELL_REVIVIFY = 1850; +const int SPELL_BLESS_WATER = 1851; +const int SPELL_PRC_HOLY_AURA = 1854; +const int SPELL_FORESIGHT = 1856; + +// Spells added for the Dread Necro +const int SPELL_HIDE_FROM_UNDEAD = 3432; +const int SPELL_CRUSHING_DESPAIR = 3433; +const int SPELL_HALT_UNDEAD = 3434; +const int SPELL_EYEBITE = 3435; +const int SPELL_SONG_OF_DISCORD = 3436; +const int SPELL_FIRE_IN_THE_BLOOD = 3437; +const int SPELL_VILE_DEATH = 3438; +const int SPELL_PLAGUE_OF_UNDEAD = 3439; +const int SPELL_DREADNECRO_FEARAURA = 3444; + +const int SPELL_BRILLIANT_ENERGY_ARROW = 3394; +const int SPELL_HIDE_FROM_ANIMALS = 3395; +const int SPELL_LONGSTRIDER = 3396; +const int SPELL_SERPENT_ARROW = 3397; +const int SPELL_TREESHAPE = 3398; +const int SPELL_WATER_BREATHING = 3399; +const int SPELL_FOG_CLOUD = 1998; + +const int SPELL_ACIDIC_FIRE = 3713; +const int SPELL_ALCHEMISTS_FROST = 3714; +const int SPELL_ALCHEMICAL_SLEEPING_GAS = 3715; +const int SPELL_ALCHEMISTS_SPARK = 3716; +const int SPELL_BLEND_CREAM = 3717; +const int SPELL_BRITTLEBONE = 3718; +const int SPELL_CRACKLEPOWDER = 3719; +const int SPELL_EMBALMING_FIRE = 3720; +const int SPELL_FAREYE_OIL = 3721; +const int SPELL_FESTERING_BOMB = 3722; +const int SPELL_FLASH_PELLET = 3723; +const int SPELL_HEALEARS_BALM = 3724; +const int SPELL_KEENEAR_POWDER = 3725; +const int SPELL_LOCKSLIP_GREASE = 3726; +const int SPELL_NATURES_DRAUGHT = 3727; +const int SPELL_NERV = 3728; +const int SPELL_SCREAMING_FLASK = 3729; +const int SPELL_SOFTFOOT = 3730; +const int SPELL_WEEPING_STONE = 3731; +const int SPELL_BILE_DROPPINGS = 3732; +const int SPELL_SHEDDEN = 3733; +const int SPELL_SHEDDEN2 = 3734; +const int SPELL_SHEDDEN3 = 3735; +const int SPELL_SHEDDEN4 = 3736; +const int SPELL_SHEDDEN5 = 3737; + +//3.3 Tenjac feats +const int SPELL_DARK_SPEECH = 3837; +const int SPELL_DARK_SPEECH_DREAD = 3838; +const int SPELL_DARK_SPEECH_POWER = 3839; +const int SPELL_CHOSEN_EVIL_ATTACK = 3845; +const int SPELL_CHOSEN_EVIL_SKILL = 3846; +const int SPELL_CHOSEN_EVIL_SAVE = 3847; + +//Disguise self +const int SPELL_DISGUISE_SELF_LEARN = 2840; +const int SPELL_DISGUISE_SELF_OPTIONS = 2841; +const int SPELL_DISGUISE_SELF_QS1 = 2842; +const int SPELL_DISGUISE_SELF_QS2 = 2843; +const int SPELL_DISGUISE_SELF_QS3 = 2844; +const int SPELL_ALTER_SELF_LEARN = 2846; +const int SPELL_ALTER_SELF_OPTIONS = 2847; +const int SPELL_ALTER_SELF_QS1 = 2848; +const int SPELL_ALTER_SELF_QS2 = 2849; +const int SPELL_ALTER_SELF_QS3 = 2850; + +// Stuff with previously missing constants +const int SPELL_SCORCHING_RAY = 3056; + +// Passive feats +const int SPELL_FORCE_PERSONALITY = 1915; +const int SPELL_INSIGHTFUL_REFLEXES = 1916; + +// Spellfire +const int SPELL_SPELLFIRE_WIELDER = 3013; +const int SPELL_SPELLFIRE_INCREASE = 3014; +const int SPELL_SPELLFIRE_DECREASE = 3015; +const int SPELL_SPELLFIRE_QUICKSELECT = 3016; +const int SPELL_SPELLFIRE_ATTACK = 3017; +const int SPELL_SPELLFIRE_HEAL = 3018; +const int SPELL_SPELLFIRE_CHECK = 3019; +const int SPELL_SPELLFIRE_PLUS_ONE = 3020; +const int SPELL_SPELLFIRE_PLUS_FIVE = 3021; +const int SPELL_SPELLFIRE_PLUS_TEN = 3022; +const int SPELL_SPELLFIRE_PLUS_TWENTY = 3023; +const int SPELL_SPELLFIRE_MINUS_ONE = 3024; +const int SPELL_SPELLFIRE_MINUS_FIVE = 3025; +const int SPELL_SPELLFIRE_MINUS_TEN = 3026; +const int SPELL_SPELLFIRE_MINUS_TWENTY = 3027; +const int SPELL_SPELLFIRE_CHECK_EXP = 3028; +const int SPELL_SPELLFIRE_QUICKSELECT_CHANGE = 3029; +const int SPELL_SPELLFIRE_QUICKSELECT_1 = 3030; +const int SPELL_SPELLFIRE_QUICKSELECT_2 = 3031; +const int SPELL_SPELLFIRE_QUICKSELECT_3 = 3032; +const int SPELL_SPELLFIRE_DRAIN_CHARGED = 3033; +const int SPELL_SPELLFIRE_RAPID_BLAST = 3034; +const int SPELL_SPELLFIRE_RAPID_BLAST_TWO = 3035; +const int SPELL_SPELLFIRE_RAPID_BLAST_THREE = 3036; +const int SPELL_SPELLFIRE_DRAIN_PERMANENT = 3037; +const int SPELL_SPELLFIRE_CHARGE_ITEM = 3038; +const int SPELL_SPELLFIRE_CROWN = 3039; +const int SPELL_SPELLFIRE_MAELSTROM = 3040; +const int SPELL_SPELLFIRE_ABSORB = 3041; + +////////////////////////////////////////////////// +/* Turning Constants */ +////////////////////////////////////////////////// + +const int SPELL_TURN_UNDEAD = 308; +const int SPELL_TURN_REPTILE = 1726; +const int SPELL_TURN_OOZE = 1727; +const int SPELL_TURN_SPIDER = 1728; +const int SPELL_TURN_PLANT = 1729; +const int SPELL_TURN_AIR = 1730; +const int SPELL_TURN_EARTH = 1731; +const int SPELL_TURN_FIRE = 1732; +const int SPELL_TURN_WATER = 1733; +const int SPELL_TURN_BLIGHTSPAWNED = 1734; +const int SPELL_TURN_OUTSIDER = 2259; +const int SPELL_TURN_COLD = 3497; + +// Slayer of Domiel +const int SPELL_SLAYER_DOMIEL_DIVINE_GRACE = 2748; + +// Disciple of Asmodeus +const int SPELL_DISCIPLE_ASMODEUS_DREAD_MIGHT = 3059; +const int SPELL_DISCIPLE_ASMODEUS_DEVIL_CORN = 3060; +const int SPELL_DISCIPLE_ASMODEUS_DEVIL_GEL = 3061; +const int SPELL_DISCIPLE_ASMODEUS_DEVIL_GLAB = 3062; +const int SPELL_DISCIPLE_ASMODEUS_DEVIL_HAM = 3063; +const int SPELL_DISCIPLE_ASMODEUS_DEVIL_OSY = 3064; +const int SPELL_DOA_COMMAND_APPROACH = 3078; +const int SPELL_DOA_COMMAND_DROP = 3079; +const int SPELL_DOA_COMMAND_FALL = 3080; +const int SPELL_DOA_COMMAND_FLEE = 3081; +const int SPELL_DOA_COMMAND_HALT = 3082; +const int SPELL_DOA_GREATER_COMMAND_APPROACH = 3072; +const int SPELL_DOA_GREATER_COMMAND_DROP = 3073; +const int SPELL_DOA_GREATER_COMMAND_FALL = 3074; +const int SPELL_DOA_GREATER_COMMAND_FLEE = 3075; +const int SPELL_DOA_GREATER_COMMAND_HALT = 3076; + +// Soulknife +const int SPELL_MINDBLADE_LUCKY = 2429; + +// More Spell IDs (new spells) +const int SPELL_MASS_INFLICT_LIGHT = 3213; +const int SPELL_MASS_INFLICT_MODERATE = 3214; +const int SPELL_MASS_INFLICT_SERIOUS = 3215; +const int SPELL_MASS_INFLICT_CRITICAL = 3216; +const int SPELL_GREATER_HARM = 3217; +const int SPELL_MASS_HARM = 3218; + +// Virtuoso + const int SPELL_VIRTUOSO_SUSTAINING_SONG = 3046; + const int SPELL_VIRTUOSO_CALUMNY = 3047; + const int SPELL_VIRTUOSO_JARRING_SONG = 3048; + const int SPELL_VIRTUOSO_SHARP_NOTE = 3049; + const int SPELL_VIRTUOSO_MINDBENDING_MELODY = 3050; + const int SPELL_VIRTUOSO_GREATER_CALUMNY = 3051; + const int SPELL_VIRTUOSO_MAGICAL_MELODY = 3052; + const int SPELL_VIRTUOSO_SONG_OF_FURY = 3053; + const int SPELL_VIRTUOSO_REVEALING_MELODY = 3054; + + // PRC Extra Stunning Feat + const int SPELL_PRC_EXTRA_STUNNING = 2500; + + // Enlightened Fist + const int SPELL_EF_FIST_OF_ENERGY = 2501; + const int SPELL_EF_FIST_OF_ENERGY_FIRE = 2502; + const int SPELL_EF_FIST_OF_ENERGY_ELECTRICITY = 2503; + const int SPELL_EF_SPELL_SELECT = 2504; + const int SPELL_EF_SPELL_SELECT_CONVO = 2505; + const int SPELL_EF_SPELL_SELECT_QUICK1 = 2506; + const int SPELL_EF_SPELL_SELECT_QUICK2 = 2507; + const int SPELL_EF_SPELL_SELECT_QUICK3 = 2508; + const int SPELL_EF_SPELL_SELECT_QUICK4 = 2509; + const int SPELL_EF_ARCANE_FIST = 2510; + const int SPELL_EF_ARCANE_REJUVENATION = 2511; + +// Scrying Spells +const int SPELL_SCRY = 3219; +const int SPELL_GREATER_SCRYING = 3655; +const int SPELL_DISCERN_LOCATION = 3656; +const int SPELL_LOCATE_CREATURE = 3657; +const int SPELL_LOCATE_OBJECT = 3658; +const int SPELL_ARCANE_EYE = 3659; +const int SPELL_OBSCURE_OBJECT = 3660; +const int SPELL_SEQUESTER = 3661; +const int SPELL_END_SCRY = 1798; +const int SPELL_PNP_SCRY_FAMILIAR = 2699; + +// Knight Spells +const int SPELL_VIGILANT_DEFENDER = 1778; +const int SPELL_BULWARK_DEFENSE = 1777; +const int SPELL_FIGHT_CHALLENGE = 1770; + +//Draconic Feat Spells +const int SPELL_DRACONIC_GRACE_1 = 1862; +const int SPELL_DRACONIC_GRACE_2 = 1863; +const int SPELL_DRACONIC_GRACE_3 = 1864; +const int SPELL_DRACONIC_GRACE_4 = 1865; +const int SPELL_DRACONIC_GRACE_5 = 1866; +const int SPELL_DRACONIC_GRACE_6 = 1867; +const int SPELL_DRACONIC_GRACE_7 = 1868; +const int SPELL_DRACONIC_GRACE_8 = 1869; +const int SPELL_DRACONIC_GRACE_9 = 1870; +const int SPELL_DRACONIC_BREATH_1 = 1871; +const int SPELL_DRACONIC_BREATH_2 = 1872; +const int SPELL_DRACONIC_BREATH_3 = 1873; +const int SPELL_DRACONIC_BREATH_4 = 1874; +const int SPELL_DRACONIC_BREATH_5 = 1875; +const int SPELL_DRACONIC_BREATH_6 = 1876; +const int SPELL_DRACONIC_BREATH_7 = 1877; +const int SPELL_DRACONIC_BREATH_8 = 1878; +const int SPELL_DRACONIC_BREATH_9 = 1879; +const int SPELL_DRAGONFIRE_STRIKE_TOGGLE = 1890; + +//Swift Wing Abilities +const int SPELL_BREATH_OF_LIFE = 1880; +const int SPELL_SWIFT_WING_WINGS = 1881; + +//Talon of Tiamat Abilities +const int SPELL_TOT_FRIGHTFUL_PRESENCE = 1882; +const int SPELL_TOT_DOMINATE_DRAGON = 1883; +const int SPELL_TOT_COLD_CONE = 1885; +const int SPELL_TOT_ACID_LINE = 1886; +const int SPELL_TOT_ACID_CONE = 1887; +const int SPELL_TOT_ELEC_LINE = 1888; +const int SPELL_TOT_FIRE_CONE = 1889; + +//Hand of the Winged Masters Abilites +const int SPELL_TRUE_STEALTH = 1891; + +//Dragon Shaman Abilities +const int SPELL_DRAGON_SHAMAN_TOUCH = 3767; +const int SPELL_DRAGON_SHAMAN_TOUCH_MINOR = 3768; +const int SPELL_DRAGON_SHAMAN_TOUCH_RESTORE = 3769; +const int SPELL_DRAGON_SHAMAN_TOUCH_MAJOR = 3770; +const int SPELL_DRACONIC_AURA_PRESENCE = 3760; +const int SPELL_DRACONIC_AURA_VIGOR = 3761; +const int SPELL_DRACONIC_AURA_TOUGHNESS = 3762; +const int SPELL_DRACONIC_AURA_ENERGY_SHIELD = 3763; +const int SPELL_DRACONIC_AURA_RESISTANCE = 3764; +const int SPELL_DRACONIC_AURA_POWER = 3765; +const int SPELL_DRACONIC_AURA_SENSES = 3766; +const int SPELL_DRACONIC_AURA_INSIGHT = 3772; +const int SPELL_DRACONIC_AURA_RESOLVE = 3773; +const int SPELL_DRACONIC_AURA_STAMINA = 3774; +const int SPELL_DRACONIC_AURA_SWIFTNESS = 3775; +const int SPELL_DRACONIC_AURA_MAGICPOWER = 3813; +const int SPELL_DRACONIC_AURA_ENERGY = 3817; +const int SPELL_DRAGON_SHAMAN_BREATH = 3771; +const int SPELL_DRAGON_SHAMAN_BREATH_FIRE = 19282; +const int SPELL_DRAGON_SHAMAN_BREATH_COLD = 19283; +const int SPELL_DRAGON_SHAMAN_BREATH_ELEC = 19284; +const int SPELL_DRAGON_SHAMAN_BREATH_ACID = 19285; + + +//Other Draconic Auras +const int SPELL_MARSHAL_AURA_SENSES = 3787; +const int SPELL_MARSHAL_AURA_PRESENCE = 3781; +const int SPELL_MARSHAL_AURA_RESISTACID = 3782; +const int SPELL_MARSHAL_AURA_RESISTCOLD = 3783; +const int SPELL_MARSHAL_AURA_RESISTELEC = 3784; +const int SPELL_MARSHAL_AURA_RESISTFIRE = 3785; +const int SPELL_MARSHAL_AURA_TOUGHNESS = 3790; +const int SPELL_MARSHAL_AURA_INSIGHT = 3780; +const int SPELL_MARSHAL_AURA_RESOLVE = 3786; +const int SPELL_MARSHAL_AURA_STAMINA = 3788; +const int SPELL_MARSHAL_AURA_SWIFTNESS = 3789; +const int SPELL_MARSHAL_AURA_MAGICPOWER = 3814; +const int SPELL_MARSHAL_AURA_ENERGYACID = 3818; +const int SPELL_MARSHAL_AURA_ENERGYCOLD = 3819; +const int SPELL_MARSHAL_AURA_ENERGYELEC = 3820; +const int SPELL_MARSHAL_AURA_ENERGYFIRE = 3821; +const int SPELL_SECOND_AURA_PRESENCE = 3791; +const int SPELL_SECOND_AURA_VIGOR = 3792; +const int SPELL_SECOND_AURA_TOUGHNESS = 3793; +const int SPELL_SECOND_AURA_ENERGY_SHIELD = 3794; +const int SPELL_SECOND_AURA_RESISTANCE = 3795; +const int SPELL_SECOND_AURA_POWER = 3796; +const int SPELL_SECOND_AURA_SENSES = 3797; +const int SPELL_SECOND_AURA_INSIGHT = 3798; +const int SPELL_SECOND_AURA_RESOLVE = 3799; +const int SPELL_SECOND_AURA_STAMINA = 3800; +const int SPELL_SECOND_AURA_SWIFTNESS = 3801; +const int SPELL_SECOND_AURA_RESISTACID = 3802; +const int SPELL_SECOND_AURA_RESISTCOLD = 3803; +const int SPELL_SECOND_AURA_RESISTELEC = 3804; +const int SPELL_SECOND_AURA_RESISTFIRE = 3805; +const int SPELL_SECOND_AURA_MAGICPOWER = 3816; +const int SPELL_SECOND_AURA_ENERGYACID = 3827; +const int SPELL_SECOND_AURA_ENERGYCOLD = 3828; +const int SPELL_SECOND_AURA_ENERGYELEC = 3829; +const int SPELL_SECOND_AURA_ENERGYFIRE = 3830; +const int SPELL_SECOND_AURA_ENERGY = 3826; +const int SPELL_BONUS_AURA_RESISTACID = 3776; +const int SPELL_BONUS_AURA_RESISTCOLD = 3777; +const int SPELL_BONUS_AURA_RESISTELEC = 3778; +const int SPELL_BONUS_AURA_RESISTFIRE = 3779; +const int SPELL_BONUS_AURA_INSIGHT = 3806; +const int SPELL_BONUS_AURA_PRESENCE = 3807; +const int SPELL_BONUS_AURA_RESOLVE = 3808; +const int SPELL_BONUS_AURA_SENSES = 3809; +const int SPELL_BONUS_AURA_STAMINA = 3810; +const int SPELL_BONUS_AURA_SWIFTNESS = 3811; +const int SPELL_BONUS_AURA_TOUGHNESS = 3812; +const int SPELL_BONUS_AURA_MAGICPOWER = 3815; +const int SPELL_BONUS_AURA_ENERGYACID = 3822; +const int SPELL_BONUS_AURA_ENERGYCOLD = 3823; +const int SPELL_BONUS_AURA_ENERGYELEC = 3824; +const int SPELL_BONUS_AURA_ENERGYFIRE = 3825; + +// Dragon Magic Spells +const int SPELL_DRAGON_ALLY = 3388; + +//Metabreath +const int SPELL_HEIGHTEN_CONV = 2985; +const int SPELL_HEIGHTEN_OFF = 2986; +const int SPELL_HEIGHTEN_QS1 = 2987; +const int SPELL_HEIGHTEN_QS2 = 2988; +const int SPELL_CLINGING_CONV = 2973; +const int SPELL_CLINGING_OFF = 2974; +const int SPELL_CLINGING_QS1 = 2975; +const int SPELL_CLINGING_QS2 = 2976; +const int SPELL_CLINGING_QS3 = 2977; +const int SPELL_LINGERING_CONV = 2979; +const int SPELL_LINGERING_OFF = 2980; +const int SPELL_LINGERING_QS1 = 2981; +const int SPELL_LINGERING_QS2 = 2982; +const int SPELL_LINGERING_QS3 = 2983; + +//Breath Effects +const int BREATH_FIRE_CONE = 2102; +const int BREATH_FIRE_LINE = 2103; +const int BREATH_FROST_CONE = 2104; +const int BREATH_ELECTRIC_LINE = 2105; +const int BREATH_SICKENING_CONE = 2106; +const int BREATH_ACID_CONE = 2108; +const int BREATH_ACID_LINE = 2109; +const int BREATH_SHAPED_BREATH = 2110; +const int BREATH_SLOW_CONE = 2111; +const int BREATH_WEAKENING_CONE = 2112; +const int BREATH_CLOUD_BREATH = 2113; +const int BREATH_ENDURING_BREATH = 2114; +const int BREATH_SLEEP_CONE = 2115; +const int BREATH_THUNDER_CONE = 2116; +const int BREATH_BAHAMUT_LINE = 2117; +const int BREATH_FORCE_LINE = 2118; +const int BREATH_PARALYZE_CONE = 2119; +const int BREATH_FIVEFOLD_TIAMAT = 2120; + +// Pyrokineticist +const int SPELL_FIRE_LASH = 1892; +const int SPELL_BOLT_OF_FIRE = 1893; +const int SPELL_NIMBUS = 1894; +const int SPELL_NIMBUS_TOUCH_ATTACK = 1895; +const int SPELL_FIREWALK = 1896; +const int SPELL_HEAT_DEATH = 1897; +const int SPELL_PYRO_CONFLAGRATION = 1898; + +// Heroes of Horror +const int SPELL_SUMMON_UNDEAD_1 = 1784; +const int SPELL_SUMMON_UNDEAD_2 = 1785; +const int SPELL_SUMMON_UNDEAD_3 = 1786; +const int SPELL_SUMMON_UNDEAD_4 = 1787; +const int SPELL_SUMMON_UNDEAD_5 = 1788; + +// Archivist +const int SPELL_DK_TACTICS = 19004; +const int SPELL_DK_PUISSANCE = 19005; +const int SPELL_DK_FOE = 19006; +const int SPELL_DK_DREADSECRET = 19007; +const int SPELL_DK_FOREKNOWLEDGE = 19008; + +//Rage Mage +const int SPELL_SPELL_RAGE = 19001; +const int SPELL_WARRIOR_CRY = 19002; + +// Metamagic Feat Abilities +const int SPELL_EXTEND_SPELL_ABILITY = 3449; +const int SPELL_SILENT_SPELL_ABILITY = 3450; +const int SPELL_STILL_SPELL_ABILITY = 3451; +const int SPELL_EMPOWER_SPELL_ABILITY = 3452; +const int SPELL_MAXIMIZE_SPELL_ABILITY = 3453; +const int SPELL_QUICKEN_SPELL_ABILITY = 3454; + +// Flaming_Sword's Spells +const int SPELL_SHIELD_OTHER = 3848; + +//Soul Eater +const int SPELL_SE_ENERGY_DRAIN = 2788; + +//OnHit Cast Spell: Unique Property +const int SPELL_ACTIVATE_ITEM_ONHITSPELLCAST = 700; + +//Runscared Berserker - special version of Dimension Door spell +const int SPELL_RUNE_DIMENSION_DOOR = 2053; +const int SPELL_RUNE_DIMENSION_DOOR_DIRDIST_SELF = 2054; +const int SPELL_RUNE_DIMENSION_DOOR_DIRDIST_PARTY = 2055; + +const int SPELL_SUMMON_CREATURE_VII_AIR = 3189; +const int SPELL_SUMMON_CREATURE_VII_EARTH = 3190; +const int SPELL_SUMMON_CREATURE_VII_FIRE = 3191; +const int SPELL_SUMMON_CREATURE_VII_WATER = 3192; +const int SPELL_SUMMON_CREATURE_VIII_AIR = 3193; +const int SPELL_SUMMON_CREATURE_VIII_EARTH = 3194; +const int SPELL_SUMMON_CREATURE_VIII_FIRE = 3195; +const int SPELL_SUMMON_CREATURE_VIII_WATER = 3196; +const int SPELL_SUMMON_CREATURE_IX_AIR = 3197; +const int SPELL_SUMMON_CREATURE_IX_EARTH = 3198; +const int SPELL_SUMMON_CREATURE_IX_FIRE = 3199; +const int SPELL_SUMMON_CREATURE_IX_WATER = 3200; + +//:: Player's Handbook Spells +const int SPELL_SPIRITUAL_WEAPON = 17249; + +//:: Player's Handbook II Spells +const int SPELL_CHASING_PERFECTION = 2479; + +//:: Spell Compendium Spells +const int SPELL_SPIRIT_WORM = 17248; +const int SPELL_FORCE_MISSILES = 2480; + +//:: Masters of the Wild Spells +const int SPELL_REGEN_LIGHT_WOUNDS = 17243; +const int SPELL_REGEN_MODERATE_WOUNDS = 17244; +const int SPELL_REGEN_SERIOUS_WOUNDS = 17245; +const int SPELL_REGEN_CRITICAL_WOUNDS = 17246; +const int SPELL_SPEED_WIND = 17247; +const int SPELL_TORTISE_SHELL = 17250; + +//x +const int SPELL_TENSERS_FLOATING_DISK = 3849; +const int SPELL_WOLFSKIN = 3850; +const int SPELL_TRUE_CASTING = 3851; +const int SPELL_MASS_SPELL_RESISTANCE = 3852; +const int SPELL_MAGIC_STONE = 3853; +const int SPELL_CW_RAIN = 3855; +const int SPELL_CW_SNOW = 3856; +const int SPELL_CW_CLEAR = 3857; +const int SPELL_GREATER_PLANAR_ALLY = 1505; +const int SPELL_LESSER_PLANAR_ALLY = 1504; +const int SPELL_READ_MAGIC = 1500; +const int SPELL_SOBRIETY = 1501; +const int SPELL_DANCING_LIGHTS = 1499; +const int SPELL_CRAFTERS_BLESSING = 1502; +const int SPELL_CRAFTERS_CURSE = 1503; +const int SPELL_DAZE_MONSTER = 1510; +const int SPELL_ETERNAL_CHARM_MONSTER = 1506; +const int SPELL_ETERNAL_CHARM_PERSON = 1507; +const int SPELL_WEAPON_OF_IMPACT = 1508; +const int SPELL_SHILLELAGH = 1509; +const int SPELL_CURSE_WATER = 1511; +const int SPELL_MISLEAD = 1512; +const int SPELL_POWER_WORD_BLIND = 1513; +const int SPELL_GLYPH_OF_WARDING_ACID = 3858; +const int SPELL_GLYPH_OF_WARDING_COLD = 3859; +const int SPELL_GLYPH_OF_WARDING_ELECTRICITY = 3860; +const int SPELL_GLYPH_OF_WARDING_FIRE = 3861; +const int SPELL_GLYPH_OF_WARDING_SONIC = 3862; +const int SPELL_GREATER_GLYPH_OF_WARDING_ACID = 3864; +const int SPELL_GREATER_GLYPH_OF_WARDING_COLD = 3865; +const int SPELL_GREATER_GLYPH_OF_WARDING_ELECTRICITY= 3866; +const int SPELL_GREATER_GLYPH_OF_WARDING_FIRE = 3867; +const int SPELL_GREATER_GLYPH_OF_WARDING_SONIC = 3868; +const int SPELL_ELDER_GLYPH_OF_WARDING_ACID = 3870; +const int SPELL_ELDER_GLYPH_OF_WARDING_COLD = 3871; +const int SPELL_ELDER_GLYPH_OF_WARDING_ELECTRICITY = 3872; +const int SPELL_ELDER_GLYPH_OF_WARDING_FIRE = 3873; +const int SPELL_ELDER_GLYPH_OF_WARDING_SONIC = 3874; +const int SPELL_SPELL_GLYPH = 0; +const int SPELL_GREATER_SPELL_GLYPH = 0; +const int SPELL_ELDER_SPELL_GLYPH = 0; +const int SPELL_DEATH_KNELL = 1568; +const int SPELL_ELEMENTAL_STRIKE_ACID = 3876; +const int SPELL_ELEMENTAL_STRIKE_COLD = 3877; +const int SPELL_ELEMENTAL_STRIKE_ELECRICITY = 3878; +const int SPELL_ELEMENTAL_STRIKE_FIRE = 3879; +const int SPELL_ELEMENTAL_STRIKE_SONIC = 3880; +const int SPELL_WORD_OF_RECALL_SELF = 3882; +const int SPELL_WORD_OF_RECALL_PARTY = 3883; +const int SPELL_TRANSPORT_VIA_PLANTS_SELF = 3885; +const int SPELL_TRANSPORT_VIA_PLANTS_PARTY = 3886; +const int SPELL_PRC_DARKVISION = 0; +const int SPELL_MASS_INVISIBILITY = 0; +const int SPELL_PRC_UNHOLY_AURA = 0; +const int SPELL_OPEN_CLOSE = 0; +const int SPELL_PRC_ETHEREALNESS = 0; +const int SPELL_ETHEREAL_JAUNT = 0; +const int SPELL_GUIDANCE_SKILL = 0; +const int SPELL_GUIDANCE_SAVE = 0; +const int SPELL_GUIDANCE_ATTACK = 0; +const int SPELL_KNOW_DIRECTION = 0; +const int SPELL_WIZARD_SIGHT = 0; +const int SPELL_HOLY_SMITE = 0; +const int SPELL_UNHOLY_BLIGHT = 0; +const int SPELL_CHAOS_HAMMER = 0; +const int SPELL_ORDERS_WRATH = 0; +const int SPELL_CALM_ANIMALS = 0; +const int SPELL_HAND_OF_SORCERER_KINGS = 0; +const int SPELL_POWER_WORD_SILENCE = 0; +const int SPELL_ETERNAL_SLEEP = 0; +const int SPELL_GREATER_BESTOW_CURSE = 0; +const int SPELL_FAERIE_FIRE = 2924; +const int SPELL_MASS_AID = 2853; +const int SPELL_SYMBOL_OF_DEATH = 2481; +const int SPELL_SYMBOL_OF_FEAR = 2482; +const int SPELL_SYMBOL_OF_STUNING = 2483; +const int SPELL_SYMBOL_OF_INSANITY = 2484; +const int SPELL_SYMBOL_OF_PAIN = 2485; +const int SPELL_SYMBOL_OF_PERSUASION = 2486; +const int SPELL_SYMBOL_OF_SLEEP = 2487; +const int SPELL_SYMBOL_OF_WEAKNESS = 2488; +const int SPELL_VIGOR = 3459; +const int SPELL_LESSER_VIGOR = 3460; +const int SPELL_MASS_LESSER_VIGOR = 3461; +const int SPELL_VIGOROUS_CIRCLE = 3462; +const int SPELL_GREATER_RESISTANCE = 3457; +const int SPELL_SUPERIOR_RESISTANCE = 3458; +const int SPELL_TROGLODYTE_STENCH = 17255; + +// Combat Maneuver SpellIds +const int COMBAT_GRAPPLE = 3475; +const int COMBAT_BULL_RUSH = 3476; +const int COMBAT_OVERRUN = 3477; +const int COMBAT_TRIP = 3478; +const int COMBAT_CHARGE = 3479; +const int COMBAT_CHARGE_BULL_RUSH = 3480; +const int COMBAT_DISARM = 3491; +const int COMBAT_SHIELD_BASH = 2344; +const int COMBAT_SHIELD_CHARGE = 2345; +const int COMBAT_SHIELD_SLAM = 2346; + +// Battlesmith +const int BATTLESMITH_BOOST = 2233; + +// Spelldancer +const int SPELLDANCE_ENTHRALL = 2064; + +// PHB +const int SPELL_CHANGESTAFF = 1600; +const int SPELL_RAGE = 1601; +const int SPELL_TOUCH_FATIGUE = 3803; +const int SPELL_HALLOW = 3830; +const int SPELL_CONSECRATE = 2167; +const int SPELL_DESECRATE = 2342; + +// Factotum +const int SPELL_CUNNING_INSIGHT_ATTACK = 3895; +const int SPELL_CUNNING_INSIGHT_DAMAGE = 3896; +const int SPELL_CUNNING_INSIGHT_SAVES = 3897; +const int SPELL_CUNNING_DEFENSE = 3898; +const int SPELL_CUNNING_STRIKE = 3899; +const int SPELL_CUNNING_SURGE = 3900; +const int SPELL_CUNNING_BREACH = 3901; +const int SPELL_CUNNING_DODGE = 3902; +const int SPELL_CUNNING_KNOWLEDGE = 3903; +const int SPELL_OPPORTUNISTIC_PIETY_HEAL = 3904; +const int SPELL_OPPORTUNISTIC_PIETY_TURN = 3905; + +// Celebrant of Sharess +const int SPELL_SHARESS_FASCINATE = 3907; + +// Unapproachable East +const int SPELL_FORCE_ORB = 3912; + +// Spell Compendium +const int SPELL_BLASTOFFORCE = 2343; + +//Paladin Pack +const int SPELL_ALIGNED_AURA = 3922; +const int SPELL_ANGELSKIN = 3923; +const int SPELL_AXIOMATIC_STORM = 3924; +const int SPELL_BLADEBANE = 3925; +const int SPELL_BLESSED_AIM = 3926; +const int SPELL_BLESSING_OF_THE_RIGHTEOUS = 3927; +const int SPELL_CLEAR_MIND = 3928; +const int SPELL_CLOAK_OF_BRAVERY = 3929; +const int SPELL_DIAMONDSTEEL = 3930; +const int SPELL_LESSER_ASPECT_OF_THE_DIETY = 3931; +const int SPELL_LAWFUL_SWORD = 3932; +const int SPELL_LESSER_AURA_OF_COLD = 3933; +const int SPELL_AURA_OF_THE_SUN = 3934; +const int SPELL_RIGHTEOUS_FURY = 3935; +const int SPELL_SEEK_ETERNAL_REST = 3936; +const int SPELL_STRENGTH_OF_STONE = 3937; +const int SPELL_UNDEAD_BANE_WEAPON = 3938; +const int SPELL_LESSER_VISAGE_OF_THE_DIETY = 3939; +const int SPELL_ALIGNED_AURA_CHAOS = 3940; +const int SPELL_ALIGNED_AURA_EVIL = 3941; +const int SPELL_ALIGNED_AURA_GOOD = 3942; +const int SPELL_ALIGNED_AURA_LAW = 3943; +const int SPELL_AXIOMATIC_WATER = 3944; +const int SPELL_BLAZE_OF_LIGHT = 3945; +const int SPELL_CALL_MOUNT = 3946; +const int SPELL_CASTIGATE = 3947; +const int SPELL_CHECKMATES_LIGHT = 3948; +const int SPELL_CONDUIT_OF_LIGHT = 3949; +const int SPELL_CURSE_OF_THE_BRUTE = 3950; +const int SPELL_DENOUNCE = 3951; +const int SPELL_DEVASTATING_SMITE = 3952; +const int SPELL_ENERGIZED_SHIELD_FIRE = 3953; +const int SPELL_ENERGIZED_SHIELD_COLD = 3954; +const int SPELL_ENERGIZED_SHIELD = 3955; +const int SPELL_LESSER_ENERGIZED_SHIELD = 3956; +const int SPELL_ESTANAS_STEW = 3957; +const int SPELL_FAVOR_OF_THE_MARTYR = 3958; +const int SPELL_FORCEWARD = 3959; +const int SPELL_GLORY_OF_THE_MARTYR = 3960; +const int SPELL_HOLY_FIRE = 3961; +const int SPELL_HOLY_STORM = 3962; +const int SPELL_INVOKE_THE_CERULEAN_SIGN = 3963; +const int SPELL_IRRESISTIBLE_FORCE = 3964; +const int SPELL_LEGIONS_MAGIC_WEAPON = 3965; +const int SPELL_MAJOR_RESISTANCE = 3966; +const int SPELL_METEORIC_STRIKE = 3967; +const int SPELL_QUICK_MARCH = 3968; +const int SPELL_RESIST_ENERGY = 3969; +const int SPELL_REVENANCE = 3970; +const int SPELL_RHINOS_RUSH = 3971; +const int SPELL_RIGHTEOUS_AURA = 3972; +const int SPELL_ENERGIZED_SHIELD_ELEC = 3973; +const int SPELL_SHIELD_OF_WARDING = 3974; +const int SPELL_SMITE_HERETIC = 3975; +const int SPELL_SMITE_OF_SACRED_FIRE = 3976; +const int SPELL_SOLDIERS_OF_SANCTITY = 3977; +const int SPELL_SOUL_OF_LIGHT = 3978; +const int SPELL_SOUL_OF_ORDER = 3979; +const int SPELL_STABILIZE = 3980; +const int SPELL_STRATEGIC_CHARGE = 3981; +const int SPELL_ENERGIZED_SHIELD_ACID = 3982; +const int SPELL_TRUE_PRAYER_OF_THE_CHOSEN = 3983; +const int SPELL_ENERGIZED_SHIELD_SONIC = 3984; +const int SPELL_UNMOVABLE_OBJECT = 3985; +const int SPELL_VISION_OF_GLORY = 2633; +const int SPELL_ALIGNED_AURA_DISCHARGE = 2469; +const int SPELL_LESSER_ENERGIZED_SHIELD_FIRE = 2471; +const int SPELL_LESSER_ENERGIZED_SHIELD_COLD = 2472; +const int SPELL_LESSER_ENERGIZED_SHIELD_ELEC = 2473; +const int SPELL_LESSER_ENERGIZED_SHIELD_ACID = 2474; +const int SPELL_LESSER_ENERGIZED_SHIELD_SONIC = 2475; +const int SPELL_RESIST_ENERGY_FIRE = 2476; +const int SPELL_RESIST_ENERGY_COLD = 2477; +const int SPELL_RESIST_ENERGY_ELEC = 2478; +const int SPELL_RESIST_ENERGY_ACID = 2479; +const int SPELL_RESIST_ENERGY_SONIC = 2480; + + +//Bright Evening Star +const int SPELL_BES_ENTHRALLING_LIGHT = 16581; +const int SPELL_BES_BLINDING_FLASH = 16582; +const int SPELL_BES_SILVER_STARLIGHT = 16583; + +//Shadow Conjuration +const int SPELL_SHADOWMASTER_QS1 = 3745; +const int SPELL_SHADOWMASTER_QS2 = 3746; +const int SPELL_SHADOWMASTER_QS3 = 3747; +const int SPELL_SHADOWMASTER_QS4 = 3748; +const int SPELL_SHADOWMASTER_QS5 = 3749; +const int SPELL_PNP_SHADES = 19341; +const int SPELL_PNP_SHADES_QS1 = 19342; +const int SPELL_PNP_SHADES_QS2 = 19343; +const int SPELL_PNP_SHADES_QS3 = 19344; +const int SPELL_PNP_SHADES_QS4 = 19345; +const int SPELL_PNP_SHADES_QS5 = 19346; +const int SPELL_PNP_SHADOW_CONJURATION = 19347; +const int SPELL_PNP_SHADOW_CONJURATION_QS1 = 19348; +const int SPELL_PNP_SHADOW_CONJURATION_QS2 = 19349; +const int SPELL_PNP_SHADOW_CONJURATION_QS3 = 19350; +const int SPELL_PNP_SHADOW_CONJURATION_QS4 = 19351; +const int SPELL_PNP_SHADOW_CONJURATION_QS5 = 19352; +const int SPELL_PNP_GREATER_SHADOW_CONJURATION = 19353; +const int SPELL_PNP_GREATER_SHADOW_CONJURATION_QS1 = 19354; +const int SPELL_PNP_GREATER_SHADOW_CONJURATION_QS2 = 19355; +const int SPELL_PNP_GREATER_SHADOW_CONJURATION_QS3 = 19356; +const int SPELL_PNP_GREATER_SHADOW_CONJURATION_QS4 = 19357; +const int SPELL_PNP_GREATER_SHADOW_CONJURATION_QS5 = 19358; + +//Incarnum Melds +const int SPELL_MOI_NECROCARNUM_WEAPON = 18756; + +//Reserve Feats +const int SPELL_MYSTIC_BACKLASH = 19359; +const int SPELL_ACIDIC_SPLATTER = 19360; +const int SPELL_FIERY_BURST = 19361; +const int SPELL_STORM_BOLT = 19362; +const int SPELL_WINTERS_BLAST = 19363; +const int SPELL_CLAP_OF_THUNDER = 19364; +const int SPELL_SICKENING_GRASP = 19365; +const int SPELL_TOUCH_OF_HEALING = 19366; +const int SPELL_DIMENSIONAL_JAUNT = 19367; +const int SPELL_CLUTCH_OF_EARTH = 19368; +const int SPELL_BORNE_ALOFT = 19369; +const int SPELL_PROTECTIVE_WARD = 19370; +const int SPELL_SHADOW_VEIL = 19371; +const int SPELL_SUNLIGHT_EYES = 19372; +const int SPELL_TOUCH_OF_DISTRACTION = 19373; +const int SPELL_UMBRAL_SHROUD = 19374; +const int SPELL_CHARNEL_MIASMA = 19375; +const int SPELL_DROWNING_GLANCE = 19376; +const int SPELL_INVISIBLE_NEEDLE = 19377; +const int SPELL_SUMMON_ELEMENTAL_AIR = 19379; +const int SPELL_SUMMON_ELEMENTAL_EARTH = 19380; +const int SPELL_SUMMON_ELEMENTAL_FIRE = 19381; +const int SPELL_SUMMON_ELEMENTAL_WATER = 19382; +const int SPELL_DIMENSIONAL_REACH = 19383; +const int SPELL_HURRICANE_BREATH = 19384; +const int SPELL_MINOR_SHAPESHIFT_MIGHT = 19386; +const int SPELL_MINOR_SHAPESHIFT_MOBILITY = 19387; +const int SPELL_MINOR_SHAPESHIFT_SAVAGERY = 19388; +const int SPELL_MINOR_SHAPESHIFT_SPEED = 19389; +const int SPELL_MINOR_SHAPESHIFT_VIGOR = 19390; +const int SPELL_FACECHANGER_OPTIONS = 19393; +const int SPELL_FACECHANGER_QS1 = 19394; +const int SPELL_FACECHANGER_QS2 = 19395; +const int SPELL_FACECHANGER_QS3 = 19396; + +//:: Saint Template +const int SAINT_PROTECTIVE_AURA = 16386; \ No newline at end of file diff --git a/src/include/prc_spellf_inc.nss b/src/include/prc_spellf_inc.nss new file mode 100644 index 0000000..753680f --- /dev/null +++ b/src/include/prc_spellf_inc.nss @@ -0,0 +1,527 @@ +/* + + prc_spellf_inc.nss - Spellfire functions + + + + By: Flaming_Sword + Created: February 15, 2006 + Modified: February 15, 2006 + + Naming conventions: + nExpend <-> GetPersistantLocalInt(oPC, "SpellfireLevelExpend"); + nStored <-> GetPersistantLocalInt(oPC, "SpellfireLevelStored"); + +*/ + +//#include "inc_utility" +//#include "prc_alterations" +#include "prc_inc_sp_tch" +#include "prcsp_engine" +//int CheckSpellfire(object oCaster, object oTarget, int bFriendly = FALSE); + +//Spellfire Functions + +//Verifies levels to expend, returns new levels to expend +int SpellfireVerifyExpend(object oPC, int nExpend, int nStored) +{ + int nCON = GetAbilityScore(oPC, ABILITY_CONSTITUTION); + + //sanity check, at least 1, capped by CON and stored + if(nExpend < 1) + { + nExpend = 1; + SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend); + } + if(nExpend > nCON) nExpend = nCON; //in case CON has changed + if(nExpend > nStored) nExpend = nStored; //can't spend more than you've got + + return nExpend; +} + +//Adjusts the number of spellfire levels expended +//in the next use of spellfire +void AdjustSpellfire(object oPC, int nAdjust) +{ + int nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend"); + nExpend += nAdjust; + if(nExpend < 1) nExpend = 1; //at least 1 + SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend); + SendMessageToPC(oPC, "Spellfire levels to expend: " + IntToString(nExpend)); +} + +//Sets flag to change quickselects a la psionics +void SpellfireQuickselectChange(object oPC) +{ + SetLocalInt(oPC, "Spellfire_Quickselect_Change", 1); //set flag + SendMessageToPC(oPC, "Select a quickselect to store the current setting"); + DelayCommand(10.0, DeleteLocalInt(oPC, "Spellfire_Quickselect_Change")); //auto-delete +} + +//Sets or changes quickselects +void SpellfireQuickselect(object oPC, int nSpellID) +{ + int nExpend; + if(GetLocalInt(oPC, "Spellfire_Quickselect_Change")) + { + nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend"); + SetPersistantLocalInt(oPC, "Spellfire_Quickselect_" + IntToString(nSpellID), nExpend); + SendMessageToPC(oPC, "Quickselect " + IntToString(nSpellID - SPELL_SPELLFIRE_QUICKSELECT_1 + 1) + " set to " + IntToString(nExpend)); + DeleteLocalInt(oPC, "Spellfire_Quickselect_Change"); + } + else + { + nExpend = GetPersistantLocalInt(oPC, "Spellfire_Quickselect_" + IntToString(nSpellID)); + SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend); + SendMessageToPC(oPC, "Spellfire levels to expend: " + IntToString(nExpend)); + } +} + +//Expends spellfire and returns the new value +int ExpendSpellfire(object oPC) +{ + int nStored = GetPersistantLocalInt(oPC, "SpellfireLevelStored"); + if(!nStored) return 0; + int nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend"); + + nExpend = SpellfireVerifyExpend(oPC, nExpend, nStored); + + nStored -= nExpend; + SetPersistantLocalInt(oPC, "SpellfireLevelStored", nStored); + return nExpend; +} + +//Applies spellfire damage to target +void SpellfireDamage(object oCaster, object oTarget, int nRoll, int nDamage, int bMaelstrom = FALSE) +{ + if(bMaelstrom) + { + //nDamage += ApplySpellBetrayalStrikeDamage(oTarget, oCaster); + ApplyEffectToObject(DURATION_TYPE_INSTANT, PRCEffectDamage(oTarget, nDamage / 2, DAMAGE_TYPE_MAGICAL), oTarget); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage - (nDamage / 2), DAMAGE_TYPE_FIRE), oTarget); + } + else + ApplyTouchAttackDamage(oCaster, oTarget, nRoll, nDamage, DAMAGE_TYPE_MAGICAL, DAMAGE_TYPE_FIRE); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SPELLF_FLAME), oTarget); +} + +//Spellfire attack roll -> damage +void SpellfireAttackRoll(object oCaster, object oTarget, int nExpend, int iMod = 0, int nDC = 20, int bBeam = FALSE, int bMaelstrom = FALSE) +{ + int nRoll, nDamage; + + //Account for Energy Draconic Aura + if (GetLocalInt(oCaster, "FireEnergyAura") > 0) + { + nDC += GetLocalInt(oCaster, "FireEnergyAura"); + } + + //Weapon Focus (spellfire) applies to spellfire only + if(GetHasFeat(FEAT_WEAPON_FOCUS_SPELLFIRE, oCaster)) iMod++; + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_SPELLFIRE, oCaster)) iMod += 2; + + if(oCaster == oTarget) //yeah, like you could miss with a touch attack on yourself + { + nRoll = 1; + nDamage = d6(nExpend); + } + else + { + nRoll = bMaelstrom ? 1 : GetAttackRoll(oTarget, oCaster, GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster), 0, 0, iMod, TRUE, 0.0, TRUE); + nDamage = PRCGetReflexAdjustedDamage(d6(nExpend), oTarget, nDC, SAVING_THROW_TYPE_NONE, oCaster); + } + if(bBeam) + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(VFX_BEAM_SPELLFIRE, oCaster, BODY_NODE_HAND, !nRoll), oTarget, 1.2 /*+ (0.5 * IntToFloat(nAttacks))*/); + if(nRoll && nDamage) + SpellfireDamage(oCaster, oTarget, nRoll, nDamage, bMaelstrom); +} + +//Hits target with spellfire, implements rapid blast +void SpellfireAttack(object oCaster, object oTarget, int bBeam, int nAttacks = 1) +{ + if(!GetPersistantLocalInt(oCaster, "SpellfireLevelStored")) + { + SendMessageToPC(oCaster, "You have no more stored spellfire levels"); + return; + } + int nLevel = GetLevelByClass(CLASS_TYPE_SPELLFIRE, oCaster); + if(nAttacks > ((nLevel / 4) + 1)) + { + SendMessageToPC(oCaster, "You do not have enough Spellfire Channeler levels use this feat"); + return; + } + int iMod = 0; + int i; + //looping attacks + //SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(VFX_BEAM_SPELLFIRE, oCaster, BODY_NODE_HAND, !nRoll), oTarget, 1.2 + (0.5 * IntToFloat(nAttacks))); + SignalEvent(oTarget, EventSpellCastAt(oCaster, SPELL_SPELLFIRE_ATTACK)); + for(i = 0; i < nAttacks; i++) + DelayCommand(0.5 * IntToFloat(i), SpellfireAttackRoll(oCaster, oTarget, ExpendSpellfire(oCaster), iMod - (2 * i), 20, bBeam)); + //longer effect for more blasts + DelayCommand(0.1, SendMessageToPC(oCaster, "Spellfire levels stored: " + IntToString(GetPersistantLocalInt(oCaster, "SpellfireLevelStored")))); +} + +//Heals target +void SpellfireHeal(object oCaster, object oTarget) +{ + if(!GetPersistantLocalInt(oCaster, "SpellfireLevelStored")) + { + SendMessageToPC(oCaster, "You have no more stored spellfire levels"); + return; + } + int nExpend = ExpendSpellfire(oCaster); + int nHeal = 2 * nExpend; + if(GetHasFeat(FEAT_SPELLFIRE_IMPROVED_HEALING, oCaster)) + nHeal = d4(nExpend) + nExpend; + + SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nHeal), oTarget); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_SPELLF_HEAL), oTarget); + DelayCommand(0.1, SendMessageToPC(oCaster, "Spellfire levels stored: " + IntToString(GetPersistantLocalInt(oCaster, "SpellfireLevelStored")))); +} + +//Maelstrom of fire +void SpellfireMaelstrom(object oCaster) +{ + if(!GetPersistantLocalInt(oCaster, "SpellfireLevelStored")) + { + SendMessageToPC(oCaster, "You have no more stored spellfire levels"); + return; + } + int nLevel = GetLevelByClass(CLASS_TYPE_SPELLFIRE, oCaster); + int nCHA = GetAbilityModifier(ABILITY_CHARISMA, oCaster); + int nDC = 10 + nLevel + nCHA; + + //Account for Energy Draconic Aura + if (GetLocalInt(oCaster, "FireEnergyAura") > 0) + { + nDC += GetLocalInt(oCaster, "FireEnergyAura"); + } + + //expend once, hit multiple targets + int nExpend = ExpendSpellfire(oCaster); + float fDelay; + location lTarget = GetLocation(oCaster); + effect eExplode = EffectVisualEffect(VFX_FNF_SPELLF_EXP); + //KABOOM! + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eExplode, lTarget); + object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + while(GetIsObjectValid(oTarget)) + { + if(spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster)) + { //ripped off fireball + SignalEvent(oTarget, EventSpellCastAt(oCaster, SPELL_SPELLFIRE_MAELSTROM)); + //Get the distance between the explosion and the target to calculate delay + fDelay = GetDistanceBetweenLocations(lTarget, GetLocation(oTarget)) / 20; + DelayCommand(fDelay, SpellfireAttackRoll(oCaster, oTarget, nExpend, 0, nDC, FALSE, TRUE)); + } + oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + } +} + +//Returns the caster level of an item with the cast spell property +int GetItemCasterLevelFromCastSpell(object oItem, itemproperty ip) +{ + int nCostTableValue = GetItemPropertyCostTableValue(ip); + int nSubtype = GetItemPropertySubType(ip); + int nCasterLevel = StringToInt(Get2DACache("iprp_spells", "CasterLvl", nSubtype)); + + itemproperty ip2 = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip2)) + { + if(GetItemPropertyType(ip2) == ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL && + //temporary caster level? i think not. + GetItemPropertyDurationType(ip2) == DURATION_TYPE_PERMANENT && + GetItemPropertySubType(ip2) == nSubtype) + { + int nNewCasterLevel = GetItemPropertyCostTableValue(ip2); //caster level property + if(nNewCasterLevel > nCasterLevel) nCasterLevel = nNewCasterLevel; + break; + } + ip2 = GetNextItemProperty(oItem); + } + return nCasterLevel; +} + +//Returns TRUE if an item is drained +int SpellfireDrainItem(object oPC, object oItem, int bCharged = TRUE, int bSingleUse = TRUE) +{ + if(GetIsObjectValid(oItem) && GetIsMagicItem(oItem)) + { //drain charged item + if(bCharged) //because big compound if statements are messy + { + int nBase = GetBaseItemType(oItem); + int nExpend = GetPersistantLocalInt(oPC, "SpellfireLevelExpend"); + if(nExpend < 1) + { + nExpend = 1; + SetPersistantLocalInt(oPC, "SpellfireLevelExpend", nExpend); + } + int nStored = GetPersistantLocalInt(oPC, "SpellfireLevelStored"); + int nCap = SpellfireMax(oPC) - nStored; + itemproperty ip; + int nSubType; + int nStack = GetItemStackSize(oItem); + int nCharges = GetItemCharges(oItem); + if(nCharges) //charged item + { + nExpend = PRCMin(PRCMin(nCharges, nCap), nExpend); //capped by charges and capacity + SetItemCharges(oItem, nCharges - nExpend); //will destroy item if all charges drained + AddSpellfireLevels(oPC, nExpend); //adds 1 level/charge + return TRUE; + } + ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) //single use item + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL && + GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT && + GetItemPropertyCostTableValue(ip) == IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE) + { + if(DEBUG) + { + DoDebug("SpellfireDrainItem(): nBase = " + IntToString(nBase), oPC); + DoDebug("SpellfireDrainItem(): nStack = " + IntToString(nStack), oPC); + DoDebug("SpellfireDrainItem(): nCap = " + IntToString(nCap), oPC); + DoDebug("SpellfireDrainItem(): nExpend = " + IntToString(nExpend), oPC); + } + nSubType = GetItemPropertySubType(ip); + //hardcoded filter + if(!( + (nSubType >= 329 && nSubType <= 344) || + (nSubType == 359) || + (nSubType >= 400 && nSubType <= 409) || + (nSubType >= 411 && nSubType <= 446) || + (nSubType >= 490 && nSubType <= 500) || + (nSubType == 513) || + (nSubType >= 521 && nSubType <= 537) || + (nSubType >= 750 && nSubType <= 851) || + (nSubType >= 1201) + )) + { + + if((nBase == BASE_ITEM_POTIONS) || + (nBase == BASE_ITEM_SCROLL) || + (nBase == BASE_ITEM_SPELLSCROLL) || + (nBase == BASE_ITEM_BLANK_POTION) || + (nBase == BASE_ITEM_BLANK_SCROLL) || + (nBase == BASE_ITEM_ENCHANTED_POTION) || + (nBase == BASE_ITEM_ENCHANTED_SCROLL) + ) + { + nExpend = PRCMin(PRCMin(nStack, nCap), nExpend); //capped by charges and capacity + if(nExpend == nStack) + DestroyObject(oItem); + else + SetItemStackSize(oItem, nStack - nExpend); //modifies stack size as scrolls/potions are used up + AddSpellfireLevels(oPC, nExpend); //adds 1 level/charge + return TRUE; + } + else + { + RemoveItemProperty(oItem, ip); //just removes the cast spell property + AddSpellfireLevels(oPC, 1); //adds 1 level + return TRUE; + } + } + } + ip = GetNextItemProperty(oItem); + } + } + else //drain permanent item + { + itemproperty ip = GetFirstItemProperty(oItem); + int nCostTableValue = GetItemPropertyCostTableValue(ip); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL && + GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT && + nCostTableValue >= IP_CONST_CASTSPELL_NUMUSES_0_CHARGES_PER_USE) + { + nCostTableValue = GetItemPropertyCostTableValue(ip); + int nSubtype = GetItemPropertySubType(ip); + int nLevel = GetItemCasterLevelFromCastSpell(oItem, ip) / 2; + RemoveItemProperty(oItem, ip); //just removes the cast spell property + AddSpellfireLevels(oPC, nLevel); //adds half the caster level + + DelayCommand(HoursToSeconds(24), //reapplying property + IPSafeAddItemProperty(oItem, + ItemPropertyCastSpell(nSubtype, nCostTableValue), + 0.0, + X2_IP_ADDPROP_POLICY_KEEP_EXISTING)); + return 1; + } + ip = GetNextItemProperty(oItem); + } + } + } + return 0; +} + +//Returns "'s" if sName has no s at the end, otherwise "'" +string Name_s(string sName = "") +{ + return (GetStringRight(sName, 1) == "s") ? "'" : "'s"; +} + +//Finds an item to drain, favours equipped items +//WARNING: This will loop through every item property on every item +// until the right conditions are met +void SpellfireDrain(object oPC, object oTarget, int bCharged = TRUE, int bExemptCreatureItems = TRUE, int bSingleUse = TRUE) +{ + object oItem; + int nObjectType = GetObjectType(oTarget); + string sMessage_oPC = ""; + string sMessage_oTarget = ""; + string sName_oPC = GetName(oPC); + string sName_oTarget = GetName(oTarget); + int bFound = 0; + + if(nObjectType == OBJECT_TYPE_ITEM) + { + oItem = oTarget; + int nBase = GetBaseItemType(oItem); + if(GetPRCSwitch(PRC_SPELLFIRE_DISALLOW_DRAIN_SCROLL_POTION) && + ((nBase == BASE_ITEM_POTIONS) || + (nBase == BASE_ITEM_SCROLL) || + (nBase == BASE_ITEM_BLANK_POTION) || + (nBase == BASE_ITEM_BLANK_SCROLL) + ) + ) + { + sMessage_oPC = "Draining charges from scrolls and potions is not allowed"; + } + else if(SpellfireDrainItem(oPC, oItem, bCharged)) + { + bFound = 1; + sMessage_oPC = "You drained " + sName_oTarget; + } + } + else if(nObjectType == OBJECT_TYPE_CREATURE) + { + if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, 10, SAVING_THROW_TYPE_NONE, oPC)) + { + //creature items exempt + int nSlotMax = bExemptCreatureItems ? INVENTORY_SLOT_CWEAPON_L : NUM_INVENTORY_SLOTS; + //int nCharges = 0; + int i; + for(i = 0; i < nSlotMax; i++) + { + oItem = GetItemInSlot(i, oTarget); + if(SpellfireDrainItem(oPC, oItem, bCharged)) + { + bFound = 1; + break; + } + } + oItem = GetFirstItemInInventory(oTarget); + if(bFound != 1) + { + while(GetIsObjectValid(oItem)) + { + if(SpellfireDrainItem(oPC, oItem, bCharged)) + { + bFound = 1; + break; + } + } + } + //search function + if(bFound) + { + sMessage_oPC = "You drained " + GetName(oItem) + " from " + sName_oTarget; + //sMessage_oTarget = sName_oPC + " drained " + GetName(oItem) " from you"; + } + else + { + sMessage_oPC = "You tried to drain one of " + sName_oTarget + Name_s(sName_oTarget) + " items, but failed"; + sMessage_oTarget = sName_oPC + " tried to drain one of your items, but failed"; + } + } + else + { + sMessage_oPC = sName_oTarget + " resisted your attempt to drain one of items"; + sMessage_oTarget = "You resisted " + sName_oPC + Name_s(sName_oPC) + " attempt to drain one of your items"; + } + } + else if(DEBUG) + DoDebug("SpellfireDrain(): Invalid target object", oPC); + + if(bFound && !bCharged) + { + string s24 = " for 24 hours"; + sMessage_oPC += s24; + sMessage_oTarget += s24; + } + + if(GetIsPC(oPC) && sMessage_oPC != "") SendMessageToPC(oPC, sMessage_oPC); + if(GetIsPC(oTarget) && sMessage_oTarget != "") SendMessageToPC(oTarget, sMessage_oTarget); +} + +//Toggles a flag +void SpellfireToggleAbsorbFriendly(object oPC) +{ + if(GetLocalInt(oPC, "SpellfireAbsorbFriendly")) + { + DeleteLocalInt(oPC, "SpellfireAbsorbFriendly"); + FloatingTextStringOnCreature("*Absorb Friendly Spells Off*", oPC); + } + else + { + SetLocalInt(oPC, "SpellfireAbsorbFriendly", 1); + FloatingTextStringOnCreature("*Absorb Friendly Spells On*", oPC); + } +} + +//Charges items +void SpellfireChargeItem(object oPC, object oItem) +{ + int nObjectType = GetObjectType(oItem); + if(nObjectType == OBJECT_TYPE_ITEM) + { + int nExpend = ExpendSpellfire(oPC); + int nCharges = GetItemCharges(oItem); + int nNewCharges = nExpend + nCharges; + if(nNewCharges > 50) + { + AddSpellfireLevels(oPC, nNewCharges - 50); + nNewCharges = 50; + } + SetItemCharges(oItem, nCharges + nExpend); + //Assuming 50 is the maximum + //refunding excess charges + } + else if(DEBUG) + DoDebug("SpellfireChargeItem(): Invalid target object", oPC); +} + +//Applies Crown of Fire effects. Other effects are implemented +//through heartbeat and onhit scripts +void SpellfireCrown(object oPC) +{ + if(GetLocalInt(oPC, "SpellfireCrown")) + { + DeleteLocalInt(oPC, "SpellfireCrown"); + PRCRemoveEffectsFromSpell(oPC, SPELL_SPELLFIRE_CROWN); + FloatingTextStringOnCreature("*Crown of Fire Deactivated*", oPC); + } + else + { + if(GetPersistantLocalInt(oPC, "SpellfireLevelStored") < 10) + { + SendMessageToPC(oPC, "You do not have enough stored spellfire levels"); + return; + } + SetLocalInt(oPC, "SpellfireCrown", 1); + effect eDmgred = EffectDamageReduction(10, DAMAGE_POWER_PLUS_ONE); + effect eResist = EffectSpellResistanceIncrease(32); + effect eCrown = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_POSITIVE); + effect eFlame = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD); + effect eLight = EffectVisualEffect(VFX_DUR_LIGHT_WHITE_20); + effect eLink = EffectLinkEffects(eDmgred, eResist); + eLink = EffectLinkEffects(eLink, eCrown); + eLink = EffectLinkEffects(eLink, eFlame); + eLink = EffectLinkEffects(eLink, eLight); + SPApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oPC); + FloatingTextStringOnCreature("*Crown of Fire Activated*", oPC); + } +} + diff --git a/src/include/prc_spellhook.nss b/src/include/prc_spellhook.nss new file mode 100644 index 0000000..796bdcb --- /dev/null +++ b/src/include/prc_spellhook.nss @@ -0,0 +1,35 @@ +/** + * PRC spellhook code for non-AMS spells. The actual work is done in + * prc_prespell.nss + * @author fluffyamoeba + * @date 2008-4-24 + */ + + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int X2PreSpellCastCode(); + +// this will execute the prespellcastcode, whose full functionality is incoded in X2PreSpellCastCode2(), +// as a script, to save loading time for spells scripts and reduce memory usage of NWN +// the prespellcode takes up roughly 250 kByte compiled code, meaning that every spell script that +// calls it directly as a function (e.g.: X2PreSpellCastCode2) will be between 100 kByte to 250 kByte +// larger, than a spell script calling the prespellcode via ExecuteScript (e.g. X2PreSpellCastCode) +// Although ExecuteScript is slightly slower than a direct function call, quite likely overall performance is +// increased, because for every new spell 100-250 kByte less code need to be loaded into memory +// and NWN has more free memory available to keep more spells scripts (and other crucial scripts) +//in RAM + +int X2PreSpellCastCode() +{ + object oCaster = OBJECT_SELF; + + // SetLocalInt(oCaster, "PSCC_Ret", 0); + ExecuteScript("prc_prespell", oCaster); + + int nReturn = GetLocalInt(oCaster, "PSCC_Ret"); + // DeleteLocalInt(oCaster, "PSCC_Ret"); + + return nReturn; +} diff --git a/src/include/prc_string_inc.nss b/src/include/prc_string_inc.nss new file mode 100644 index 0000000..f590b4c --- /dev/null +++ b/src/include/prc_string_inc.nss @@ -0,0 +1,74 @@ +//:://///////////////////////////////////////////// +//:: String Util +//:: prc_string_inc +//::////////////////////////////////////////////// +/* + A util class for providing useful string functions. +*/ +//::////////////////////////////////////////////// +//:: Created By: Rakiov +//:: Created On: 22.05.2005 +//::////////////////////////////////////////////// + +#include "inc_utility" + +// +// StringSplit +// Takes a string and splits it by " " into a json list of strings +// i.e. "this is a test" returns +// { +// "this", +// "is", +// "a", +// "test" +// } +// +// Parameters: +// string input the string input +// +// Returns: +// json the json list of words +// +json StringSplit(string input); + +json StringSplit(string input) +{ + json retValue = JsonArray(); + + string subString = ""; + //trim any whitespace characters first + string currString = PRCTrimString(input); + + // loop until we process the whole string + while(currString != "") + { + string currChar = GetStringLeft(currString, 1); + if (currChar != "" && currChar != " ") + { + // if the current character isn't nothing or whitespace, then add it + // to the current sub string. + subString += currChar; + } + else + { + // otherwise if the substring is not empty, then add it to the list + // of words to return + if(subString != "") + { + retValue = JsonArrayInsert(retValue, JsonString(subString)); + subString = ""; + } + } + + // pop and move to next character + currString = GetStringRight(currString, GetStringLength(currString)-1); + } + + // if there is any sub string left at the end of the loop, add it to the list + if(subString != "") + { + retValue = JsonArrayInsert(retValue, JsonString(subString)); + } + + return retValue; +} diff --git a/src/include/prc_template_con.nss b/src/include/prc_template_con.nss new file mode 100644 index 0000000..fc15544 --- /dev/null +++ b/src/include/prc_template_con.nss @@ -0,0 +1,468 @@ +//Template and Weapons of Legacy constants + +/*////////////////////////////////////////////////// +//////////////// Templates ///////////////////////// +//////////////////////////////////////////////////*/ + +//:: Outsiders +const int TEMPLATE_FIENDISH = 0; +const int TEMPLATE_CELESTIAL = 1; +const int TEMPLATE_HALF_FIEND = 2; +const int TEMPLATE_HALF_CELESTIAL = 3; + +//:: Dragons +const int TEMPLATE_HALF_DRAGON = 4; + +//:: Undead +const int TEMPLATE_CURST = 26; +const int TEMPLATE_GRAVETOUCHED_GHOUL = 29; +const int TEMPLATE_CRYPTSPAWN = 30; +const int TEMPLATE_ARCHLICH = 99; +const int TEMPLATE_LICH = 101; +const int TEMPLATE_DEMILICH = 102; +const int TEMPLATE_NECROPOLITAN = 105; +const int TEMPLATE_ALHOON = 106; + + +//:: General +const int TEMPLATE_DARK = 25; +const int TEMPLATE_MINERAL_WARRIOR = 27; +const int TEMPLATE_BLOODED_ONE = 28; +const int TEMPLATE_HALF_TROLL = 31; +const int TEMPLATE_SAINT = 32; + +//:: Half-dragon sub-templates +const int TEMPLATE_HDRAGON_CHROMATIC_BLACK = 1; +const int TEMPLATE_HDRAGON_CHROMATIC_BLUE = 2; +const int TEMPLATE_HDRAGON_CHROMATIC_GREEN = 3; +const int TEMPLATE_HDRAGON_CHROMATIC_RED = 4; +const int TEMPLATE_HDRAGON_CHROMATIC_WHITE = 5; +const int TEMPLATE_HDRAGON_GEM_AMETHYST = 6; +const int TEMPLATE_HDRAGON_GEM_CRYSTAL = 7; +const int TEMPLATE_HDRAGON_GEM_EMERALD = 8; +const int TEMPLATE_HDRAGON_GEM_SAPPHIRE = 9; +const int TEMPLATE_HDRAGON_GEM_TOPAZ = 10; +const int TEMPLATE_HDRAGON_LUNG_CHIANGLUNG = 11; +const int TEMPLATE_HDRAGON_LUNG_LILUNG = 12; +const int TEMPLATE_HDRAGON_LUNG_LUNGWANG = 13; +const int TEMPLATE_HDRAGON_LUNG_PANLUNG = 14; +const int TEMPLATE_HDRAGON_LUNG_SHENLUNG = 15; +const int TEMPLATE_HDRAGON_LUNG_TIENLUNG = 16; +const int TEMPLATE_HDRAGON_LUNG_TUNMILUNG = 17; +const int TEMPLATE_HDRAGON_METALLIC_BRASS = 18; +const int TEMPLATE_HDRAGON_METALLIC_BRONZE = 19; +const int TEMPLATE_HDRAGON_METALLIC_COPPER = 20; +const int TEMPLATE_HDRAGON_METALLIC_GOLD = 21; +const int TEMPLATE_HDRAGON_METALLIC_SILVER = 22; +const int TEMPLATE_HDRAGON_OBSCURE_BATTLE = 23; +const int TEMPLATE_HDRAGON_OBSCURE_BROWN = 24; +const int TEMPLATE_HDRAGON_OBSCURE_CHAOS = 25; +const int TEMPLATE_HDRAGON_OBSCURE_DEEP = 26; +const int TEMPLATE_HDRAGON_OBSCURE_ETHEREAL = 27; +const int TEMPLATE_HDRAGON_OBSCURE_FANG = 28; +const int TEMPLATE_HDRAGON_OBSCURE_HOWLING = 29; +const int TEMPLATE_HDRAGON_OBSCURE_OCEANUS = 30; +const int TEMPLATE_HDRAGON_OBSCURE_PYROCLASTIC = 31; +const int TEMPLATE_HDRAGON_OBSCURE_RADIANT = 32; +const int TEMPLATE_HDRAGON_OBSCURE_RUST = 33; +const int TEMPLATE_HDRAGON_OBSCURE_SHADOW = 34; +const int TEMPLATE_HDRAGON_OBSCURE_SONG = 35; +const int TEMPLATE_HDRAGON_OBSCURE_STYX = 36; +const int TEMPLATE_HDRAGON_OBSCURE_TARTERIAN = 37; + +/*////////////////////////////////////////////////// +//////////////// Weapons of Legacy////////////////// +//////////////////////////////////////////////////*/ + +const int WOL_BOW_BLACK_ARCHER = 2; +const int WOL_CAPUT_MORTUUM = 6; +const int WOL_STEADFAST = 7; +const int WOL_FLAY = 8; +const int WOL_CRIMSON_RUINATION = 9; +const int WOL_DEVIOUS = 10; +const int WOL_SIMPLE_BOW = 11; +const int WOL_SACARAB_ARADROS = 12; + +/*////////////////////////////////////////////////// +//////////////// Weapons of Legacy SLAs //////////// +//////////////////////////////////////////////////*/ + +const int WOL_BBB_EYES_SHADOW = 16390; +const int WOL_BBB_LONGSTRIDER = 16391; +const int WOL_BBB_SOLACE = 16392; +const int WOL_BBB_FRIEND_SHADOWS = 16393; +const int WOL_BBB_DENY_DEMONWEB = 16394; +const int WOL_BBB_DROWSEEKER = 16395; +const int WOL_BBB_SHOCKING_SHOT = 16396; +const int WOL_BBB_PIERCE_BLACK_HEART = 16397; +const int WOL_STEADFAST_VIGOR = 16398; +const int WOL_STEADFAST_SLOW = 16399; +const int WOL_FLAY_SNAKE_STING = 16400; +const int WOL_FLAY_WHIP_WRAP = 16401; +const int WOL_CR_FROZEN_FATE = 16402; +const int WOL_DEVIOUS_DETECT_THOUGHTS = 16403; +const int WOL_SIMPLEBOW_TRUE_SEEING = 16404; +const int WOL_SIMPLEBOW_PRESCIENCE = 16405; +const int WOL_SIMPLEBOW_FOCUS = 16406; +const int WOL_ARADROS_EXTEND = 16407; +const int WOL_ARADROS_EXTEND_ACID = 16409; +const int WOL_ARADROS_EXTEND_COLD = 16410; +const int WOL_ARADROS_EXTEND_ELEC = 16411; +const int WOL_ARADROS_EXTEND_FIRE = 16412; +const int WOL_ARADROS_EXTEND_SONIC = 16413; +const int WOL_GUURGAL_FORCE = 16414; +const int WOL_GUURGAL_RAGE = 16415; +const int WOL_DIVSPARK_FEAR = 16416; +const int WOL_DIVSPARK_LIGHT = 16417; +const int WOL_WARGIRDS_HASTE = 16418; +const int WOL_WARGIRDS_STONESKIN = 16419; +const int WOL_DW_FIERY_SLASH = 16420; +const int WOL_DW_HOWLING_WIND = 16421; +const int WOL_DW_FAN_FLAMES = 16422; +const int WOL_DW_DUST_DESERT = 16423; +const int WOL_MS_VIRTUE_DENIED = 16424; +const int WOL_MS_KISS_DEATH = 16425; +const int WOL_MS_BATTLE_SHRIEK = 16426; +const int WOL_MS_RUINOUS_HOWL = 16427; +const int WOL_NS_FISHES = 16428; +const int WOL_NS_KRAKEN = 16429; +const int WOL_NS_SCION = 16430; +const int WOL_NS_SEA_CHILDREN = 16431; +const int WOL_UR_SWIFT = 16432; +const int WOL_UR_HEALING = 16433; +const int WOL_UR_SAVAGE = 16434; +const int WOL_FC_MARK_TARGET = 16435; +const int WOL_FC_MORALE = 16436; +const int WOL_FC_FIREBALL = 16437; +const int WOL_SB_STALK = 16438; +const int WOL_SB_ETHEREAL = 16439; +const int WOL_EXORD_GUIDANCE = 16440; +const int WOL_EXORD_CURE = 16441; +const int WOL_EXORD_DISMISSAL = 16442; +const int WOL_CALAD_IMPRISON = 16443; +const int WOL_WITCHES_DETECT = 16444; +const int WOL_WITCHES_SPELLBREAKER = 16445; +const int WOL_WITCHES_AMF = 16446; +const int WOL_WITCHES_DISPEL = 16447; +const int WOL_WITCHES_MANTLE = 16448; +const int WOL_DIREWIND_STUN = 16449; +const int WOL_DIREWIND_GUST = 16450; +const int WOL_DIREWIND_WALL = 16451; +const int WOL_TREEBRO_SHILL = 16452; +const int WOL_TREEBRO_EMPATHY = 16453; +const int WOL_TREEBRO_ENTANGLE = 16454; +const int WOL_TREEBRO_INSIGHT = 16455; +const int WOL_TREEBRO_CHANGESTAFF = 16456; +const int WOL_FULLMOON_RAGE = 16457; +const int WOL_FULLMOON_INVIS = 16458; +const int WOL_FIEND_DARKVIS = 16459; +const int WOL_FIEND_DETECT = 16460; +const int WOL_SCALES_DETECT = 16461; +const int WOL_SCALES_CURE = 16462; +const int WOL_SCALES_KNELL = 16463; +const int WOL_SCALES_ENERV = 16464; +const int WOL_SCALES_HEAL = 16465; +const int WOL_SCALES_FINGER = 16466; +const int WOL_SHISHIO_CHARM = 16467; +const int WOL_SHISHIO_SUMMON = 16468; +const int WOL_SHISHIO_POLY = 16469; +const int WOL_SHISHIO_SHOUT = 16470; +const int WOL_DYMOND_BOLTS = 16471; +const int WOL_DYMOND_DEFLECT = 16472; +const int WOL_DYMOND_DAYLIGHT = 16473; +const int WOL_DYMOND_CURE = 16474; +const int WOL_DYMOND_BANISH = 16475; +const int WOL_SUNSWORD_DAY = 16476; +const int WOL_SUNSWORD_WARD = 16477; +const int WOL_SUNSWORD_BANISH = 16478; +const int WOL_SUNSWORD_UNDEATH = 16479; +const int WOL_BLACKRAZOR_DETECT = 16480; +const int WOL_BLACKRAZOR_KNELL = 16481; +const int WOL_BLACKRAZOR_HASTE = 16482; +const int WOL_RAMETHENE_DETECT = 16483; +const int WOL_RAMETHENE_SMITE = 16484; +const int WOL_RAMETHENE_RESIST = 16485; +const int WOL_RAMETHENE_CLOUD = 16486; +const int WOL_RAMETHENE_MAX = 16487; +const int WOL_RAMETHENE_WILT = 16488; +const int WOL_WYRMBANE_FEAR = 16489; +const int WOL_WYRMBANE_BOLT = 16490; +const int WOL_WYRMBANE_EMP = 16491; +const int WOL_WYRMBANE_BREATH = 16492; +const int WOL_WHELM_DETECT_GIANT = 16493; +const int WOL_WHELM_LOCATE_OBJECT = 16494; +const int WOL_WHELM_DETECT_GOBLIN = 16495; +const int WOL_RAVENKIND_DANCING_LIGHT = 16496; +const int WOL_RAVENKIND_LIGHT = 16497; +const int WOL_RAVENKIND_FLARE = 16498; +const int WOL_RAVENKIND_DETECT_UNDEAD = 16499; +const int WOL_RAVENKIND_HALT = 16500; +const int WOL_RAVENKIND_CURE = 16501; +const int WOL_RAVENKIND_DAYLIGHT = 16502; +const int WOL_RAVENKIND_WARD = 16503; +const int WOL_RAVENKIND_BREAK = 16504; +const int WOL_RAVENKIND_HEAL = 16505; +const int WOL_LAST_CITADEL_ATTACK = 16506; +const int WOL_LAST_CITADEL_PRAYER = 16507; +const int WOL_LAST_CITADEL_FEAR = 16508; +const int WOL_LAST_CITADEL_CURE = 16509; +const int WOL_LAST_CITADEL_BLADE = 16510; +const int WOL_LAST_CITADEL_HEAL = 16511; +const int WOL_UNFETTERED_MINOTAUR = 16512; +const int WOL_UNFETTERED_ENLARGE = 16513; +const int WOL_UNFETTERED_ETHEREAL = 16514; +const int WOL_UNFETTERED_STONESKIN = 16515; +const int WOL_UNFETTERED_SWORD = 16516; +const int WOL_HILLCRUSHER_EARTHEN_MIGHT = 16517; +const int WOL_HILLCRUSHER_SOFTEN_EARTH = 16518; +const int WOL_HILLCRUSHER_FANGS_OF_STONE = 16519; +const int WOL_HILLCRUSHER_RAISE_THE_EARTH = 16520; +const int WOL_HILLCRUSHER_SHAKE_THE_EARTH = 16521; +const int WOL_DWTOB_BURNING_BLADE = 16522; +const int WOL_DWTOB_FAN_THE_FLAMES = 16523; +const int WOL_DWTOB_WYRMS_FLAME = 16524; +const int WOL_FAITHFUL_STRIKE = 16525; +const int WOL_FAITHFUL_DETECT = 16526; +const int WOL_FAITHFUL_LR = 16527; +const int WOL_FAITHFUL_RESTORE = 16528; +const int WOL_FAITHFUL_RESILIENCY = 16529; +const int WOL_FAITHFUL_IMMORTAL = 16530; +const int WOL_SUPERNAL_NIGHTMARE_BLADE = 16531; +const int WOL_SUPERNAL_PSYCHIC_POISE = 16532; +const int WOL_SUPERNAL_HASTE = 16533; +const int WOL_SUPERNAL_FREEDOM = 16534; +const int WOL_SUPERNAL_TIMESTOP = 16535; +const int WOL_KAMATE_STEEL_WIND = 16536; +const int WOL_KAMATE_SHOCK = 16537; +const int WOL_KAMATE_LIGHTNING = 16538; +const int WOL_KAMATE_CHAIN = 16539; +const int WOL_KAMATE_TRUE_STRIKE = 16540; +const int WOL_EVENTIDE_COMET = 16541; +const int WOL_EVENTIDE_BAFFLE = 16542; +const int WOL_EVENTIDE_INVIS = 16543; +const int WOL_UMBRAL_INVIS = 16544; +const int WOL_UMBRAL_INCORP = 16545; +const int WOL_UMBRAL_SPEED_WEAPON = 16546; +const int WOL_TIGER_FANG_CHARGE = 16547; +const int WOL_TIGER_FANG_BATTLE = 16548; +const int WOL_TIGER_FANG_HASTE = 16549; +const int WOL_BULLY_SILLY = 16550; +const int WOL_BULLY_STONE = 16551; +const int WOL_BULLY_CHAIN = 16552; +const int WOL_BULLY_GIANT = 16553; +const int WOL_LORESTEALER_READ = 16554; +const int WOL_LORESTEALER_DETECT = 16555; +const int WOL_LORESTEALER_AXE = 16556; +const int WOL_DURINDANA_DAYLIGHT = 16557; +const int WOL_DURINDANA_WARD = 16558; +const int WOL_DURINDANA_HALLOW = 16559; +const int WOL_DURINDANA_DAZZLE = 16560; +const int WOL_THAAS_DETECT = 16561; +const int WOL_THAAS_OBSTRUCT = 16562; +const int WOL_THAAS_BANISH = 16563; +const int WOL_THAAS_TELEPORT = 16564; +const int WOL_QUICKSPUR_SHIELD = 16565; +const int WOL_QUICKSPUR_RESIST = 16566; +const int WOL_QUICKSPUR_STEED = 16567; +const int WOL_QUICKSPUR_BLUR = 16568; +const int WOL_QUICKSPUR_STONE = 16569; +const int WOL_BES_FIRE_OF_THE_HEART = 16570; +const int WOL_BES_ENTHRALLING_LIGHT = 16571; +const int WOL_BES_COLOR_SPRAY = 16572; +const int WOL_BES_BLINDING_FLASH = 16573; +const int WOL_BES_SHOOTING_STARS = 16574; +const int WOL_BES_GLITTERING_MOTES = 16575; +const int WOL_BES_TWINKLE = 16576; +const int WOL_BES_SILVER_STARLIGHT = 16577; +const int WOL_BES_STARLIGHT_DISPELLING = 16578; +const int WOL_BES_TALES_IN_THE_SKY = 16579; +const int WOL_BES_CALL_DOWN_A_STAR = 16580; + +/*////////////////////////////////////////////////// +//////////////// Weapons of Legacy iprps /////////// +//////////////////////////////////////////////////*/ + +const int IP_CONST_FEAT_EYES_SHADOW = 1000; +const int IP_CONST_FEAT_LONGSTRIDER = 1001; +const int IP_CONST_FEAT_SOLACE = 1002; +const int IP_CONST_FEAT_FRIEND_SHADOWS = 1003; +const int IP_CONST_FEAT_DENY_DEMONWEB = 1004; +const int IP_CONST_FEAT_DROWSEEKER = 1005; +const int IP_CONST_FEAT_SHOCKING_SHOT = 1006; +const int IP_CONST_FEAT_PIERCE_BLACK_HEART = 1007; +const int IP_CONST_FEAT_STEADFAST_VIGOR = 1008; +const int IP_CONST_FEAT_STEADFAST_SLOW = 1009; +const int IP_CONST_FEAT_FLAY_SNAKE_STING = 1010; +const int IP_CONST_FEAT_FLAY_WHIP_WRAP = 1011; +const int IP_CONST_FEAT_CR_FROZEN_FATE = 1012; +const int IP_CONST_FEAT_DEVIOUS_THOUGHTS = 1013; +const int IP_CONST_FEAT_SIMPLEBOW_TRUE_SEEING = 1014; +const int IP_CONST_FEAT_SIMPLEBOW_PRESCIENCE = 1015; +const int IP_CONST_FEAT_SIMPLEBOW_FOCUS = 1016; +const int IP_CONST_FEAT_ARADROS_EXTEND = 1017; +const int IP_CONST_FEAT_ARADROS_SURVIVE = 1018; +const int IP_CONST_FEAT_GUURGAL_FORCE = 1019; +const int IP_CONST_FEAT_GUURGAL_RAGE = 1020; +const int IP_CONST_FEAT_DIVSPARK_FEAR = 1021; +const int IP_CONST_FEAT_DIVSPARK_LIGHT = 1022; +const int IP_CONST_FEAT_WARGIRDS_HASTE = 1023; +const int IP_CONST_FEAT_WARGIRDS_STONESKIN = 1024; +const int IP_CONST_FEAT_DW_FIERY_SLASH = 1025; +const int IP_CONST_FEAT_DW_HOWLING_WIND = 1026; +const int IP_CONST_FEAT_DW_FAN_FLAMES = 1027; +const int IP_CONST_FEAT_DW_DUST_DESERT = 1028; +const int IP_CONST_FEAT_MS_VIRTUE_DENIED = 1029; +const int IP_CONST_FEAT_MS_KISS_DEATH = 1030; +const int IP_CONST_FEAT_MS_BATTLE_SHRIEK = 1031; +const int IP_CONST_FEAT_MS_RUINOUS_HOWL = 1032; +const int IP_CONST_FEAT_NS_FISHES = 1033; +const int IP_CONST_FEAT_NS_KRAKEN = 1034; +const int IP_CONST_FEAT_NS_SCION = 1035; +const int IP_CONST_FEAT_NS_SEA_CHILDREN = 1036; +const int IP_CONST_FEAT_UR_SWIFT = 1037; +const int IP_CONST_FEAT_UR_HEALING = 1038; +const int IP_CONST_FEAT_UR_SAVAGE = 1039; +const int IP_CONST_FEAT_FC_MARK_TARGET = 1040; +const int IP_CONST_FEAT_FC_MORALE = 1041; +const int IP_CONST_FEAT_FC_FIREBALL = 1042; +const int IP_CONST_FEAT_SB_STALK = 1043; +const int IP_CONST_FEAT_SB_ETHEREAL = 1044; +const int IP_CONST_FEAT_EXORD_GUIDANCE = 1045; +const int IP_CONST_FEAT_EXORD_CURE = 1046; +const int IP_CONST_FEAT_EXORD_DISMISSAL = 1047; +const int IP_CONST_FEAT_CALAD_IMPRISON = 1048; +const int IP_CONST_FEAT_WITCHES_DETECT = 1049; +const int IP_CONST_FEAT_WITCHES_SPELLBREAKER = 1050; +const int IP_CONST_FEAT_WITCHES_AMF = 1051; +const int IP_CONST_FEAT_WITCHES_DISPEL = 1052; +const int IP_CONST_FEAT_WITCHES_MANTLE = 1053; +const int IP_CONST_FEAT_DIREWIND_STUN = 1054; +const int IP_CONST_FEAT_DIREWIND_GUST = 1055; +const int IP_CONST_FEAT_DIREWIND_WALL = 1056; +const int IP_CONST_FEAT_TREEBRO_SHILL = 1057; +const int IP_CONST_FEAT_TREEBRO_EMPATHY = 1058; +const int IP_CONST_FEAT_TREEBRO_ENTANGLE = 1059; +const int IP_CONST_FEAT_TREEBRO_INSIGHT = 1060; +const int IP_CONST_FEAT_TREEBRO_CHANGESTAFF = 1061; +const int IP_CONST_FEAT_FULLMOON_RAGE = 1062; +const int IP_CONST_FEAT_FULLMOON_INVIS = 1063; +const int IP_CONST_FEAT_FIEND_DARKVIS = 1064; +const int IP_CONST_FEAT_FIEND_DETECT = 1065; +const int IP_CONST_FEAT_SCALES_DETECT = 1066; +const int IP_CONST_FEAT_SCALES_CURE = 1067; +const int IP_CONST_FEAT_SCALES_KNELL = 1068; +const int IP_CONST_FEAT_SCALES_ENERV = 1069; +const int IP_CONST_FEAT_SCALES_HEAL = 1070; +const int IP_CONST_FEAT_SCALES_FINGER = 1071; +const int IP_CONST_FEAT_SHISHIO_CHARM = 1072; +const int IP_CONST_FEAT_SHISHIO_SUMMON = 1073; +const int IP_CONST_FEAT_SHISHIO_POLY = 1074; +const int IP_CONST_FEAT_SHISHIO_SHOUT = 1075; +const int IP_CONST_FEAT_DYMOND_BOLTS = 1076; +const int IP_CONST_FEAT_DYMOND_DEFLECT = 1077; +const int IP_CONST_FEAT_DYMOND_DAYLIGHT = 1078; +const int IP_CONST_FEAT_DYMOND_CURE = 1079; +const int IP_CONST_FEAT_DYMOND_BANISH = 1080; +const int IP_CONST_FEAT_SUNSWORD_DAY = 1081; +const int IP_CONST_FEAT_SUNSWORD_WARD = 1082; +const int IP_CONST_FEAT_SUNSWORD_BANISH = 1083; +const int IP_CONST_FEAT_SUNSWORD_UNDEATH = 1084; +const int IP_CONST_FEAT_BLACKRAZOR_DETECT = 1085; +const int IP_CONST_FEAT_BLACKRAZOR_KNELL = 1086; +const int IP_CONST_FEAT_BLACKRAZOR_HASTE = 1087; +const int IP_CONST_FEAT_RAMETHENE_DETECT = 1088; +const int IP_CONST_FEAT_RAMETHENE_SMITE = 1089; +const int IP_CONST_FEAT_RAMETHENE_RESIST = 1090; +const int IP_CONST_FEAT_RAMETHENE_CLOUD = 1091; +const int IP_CONST_FEAT_RAMETHENE_MAX = 1092; +const int IP_CONST_FEAT_RAMETHENE_WILT = 1093; +const int IP_CONST_FEAT_WYRMBANE_FEAR = 1094; +const int IP_CONST_FEAT_WYRMBANE_BOLT = 1095; +const int IP_CONST_FEAT_WYRMBANE_EMP = 1096; +const int IP_CONST_FEAT_WYRMBANE_BREATH = 1097; +const int IP_CONST_FEAT_WHELM_DETECT_GIANT = 1098; +const int IP_CONST_FEAT_WHELM_LOCATE_OBJECT = 1099; +const int IP_CONST_FEAT_WHELM_DETECT_GOBLIN = 1100; +const int IP_CONST_FEAT_RAVENKIND_DANCE = 1101; +const int IP_CONST_FEAT_RAVENKIND_FLARE = 1102; +const int IP_CONST_FEAT_RAVENKIND_LIGHT = 1103; +const int IP_CONST_FEAT_RAVENKIND_DETECT = 1104; +const int IP_CONST_FEAT_RAVENKIND_HALT = 1105; +const int IP_CONST_FEAT_RAVENKIND_CURE = 1106; +const int IP_CONST_FEAT_RAVENKIND_DAYLIGHT = 1107; +const int IP_CONST_FEAT_RAVENKIND_WARD = 1108; +const int IP_CONST_FEAT_RAVENKIND_BREAK = 1109; +const int IP_CONST_FEAT_RAVENKIND_HEAL = 1110; +const int IP_CONST_FEAT_LAST_CITADEL_ATTACK = 1111; +const int IP_CONST_FEAT_LAST_CITADEL_PRAYER = 1112; +const int IP_CONST_FEAT_LAST_CITADEL_FEAR = 1113; +const int IP_CONST_FEAT_LAST_CITADEL_CURE = 1114; +const int IP_CONST_FEAT_LAST_CITADEL_BLADE = 1115; +const int IP_CONST_FEAT_LAST_CITADEL_HEAL = 1116; +const int IP_CONST_FEAT_UNFETTERED_MINOTAUR = 1117; +const int IP_CONST_FEAT_UNFETTERED_ENLARGE = 1118; +const int IP_CONST_FEAT_UNFETTERED_ETHEREAL = 1119; +const int IP_CONST_FEAT_UNFETTERED_STONESKIN = 1120; +const int IP_CONST_FEAT_UNFETTERED_SWORD = 1121; +const int IP_CONST_FEAT_HC_EARTHEN_MIGHT = 1122; +const int IP_CONST_FEAT_HC_SOFTEN_EARTH = 1123; +const int IP_CONST_FEAT_HC_FANGS_OF_STONE = 1124; +const int IP_CONST_FEAT_HC_RAISE_THE_EARTH = 1125; +const int IP_CONST_FEAT_HC_SHAKE_THE_EARTH = 1126; +const int IP_CONST_FEAT_DWTOB_BURNING_BLADE = 1127; +const int IP_CONST_FEAT_DWTOB_FAN_THE_FLAMES = 1128; +const int IP_CONST_FEAT_DWTOB_WYRMS_FLAME = 1129; +const int IP_CONST_FEAT_FAITHFUL_DETECT = 1130; +const int IP_CONST_FEAT_FAITHFUL_LR = 1131; +const int IP_CONST_FEAT_FAITHFUL_RESTORE = 1132; +const int IP_CONST_FEAT_FAITHFUL_RESILIENCY = 1133; +const int IP_CONST_FEAT_FAITHFUL_IMMORTAL = 1134; +const int IP_CONST_FEAT_SUPERNAL_NIGHTMARE = 1135; +const int IP_CONST_FEAT_SUPERNAL_PSYCHIC_POISE = 1136; +const int IP_CONST_FEAT_SUPERNAL_HASTE = 1137; +const int IP_CONST_FEAT_SUPERNAL_FREEDOM = 1138; +const int IP_CONST_FEAT_SUPERNAL_TIMESTOP = 1139; +const int IP_CONST_FEAT_KAMATE_STEEL_WIND = 1140; +const int IP_CONST_FEAT_KAMATE_SHOCK = 1141; +const int IP_CONST_FEAT_KAMATE_LIGHTNING = 1142; +const int IP_CONST_FEAT_KAMATE_CHAIN = 1143; +const int IP_CONST_FEAT_KAMATE_TRUE_STRIKE = 1144; +const int IP_CONST_FEAT_EVENTIDE_COMET = 1145; +const int IP_CONST_FEAT_EVENTIDE_BAFFLE = 1146; +const int IP_CONST_FEAT_EVENTIDE_INVIS = 1147; +const int IP_CONST_FEAT_UMBRAL_INVIS = 1148; +const int IP_CONST_FEAT_UMBRAL_INCORP = 1149; +const int IP_CONST_FEAT_TIGER_FANG_CHARGE = 1150; +const int IP_CONST_FEAT_TIGER_FANG_BATTLE = 1151; +const int IP_CONST_FEAT_TIGER_FANG_HASTE = 1152; +const int IP_CONST_FEAT_BULLY_SILLY = 1153; +const int IP_CONST_FEAT_BULLY_STONE = 1154; +const int IP_CONST_FEAT_BULLY_CHAIN = 1155; +const int IP_CONST_FEAT_BULLY_GIANT = 26000; +const int IP_CONST_FEAT_LORESTEALER_READ = 1156; +const int IP_CONST_FEAT_LORESTEALER_DETECT = 1157; +const int IP_CONST_FEAT_LORESTEALER_AXE = 1158; +const int IP_CONST_FEAT_DURINDANA_DAYLIGHT = 1159; +const int IP_CONST_FEAT_DURINDANA_WARD = 1160; +const int IP_CONST_FEAT_DURINDANA_HALLOW = 1161; +const int IP_CONST_FEAT_DURINDANA_DAZZLE = 1162; +const int IP_CONST_FEAT_THAAS_DETECT = 1163; +const int IP_CONST_FEAT_THAAS_OBSTRUCT = 1164; +const int IP_CONST_FEAT_THAAS_BANISH = 1165; +const int IP_CONST_FEAT_THAAS_TELEPORT = 1166; +const int IP_CONST_FEAT_QUICKSPUR_SHIELD = 1167; +const int IP_CONST_FEAT_QUICKSPUR_RESIST = 1168; +const int IP_CONST_FEAT_QUICKSPUR_STEED = 1169; +const int IP_CONST_FEAT_QUICKSPUR_BLUR = 1170; +const int IP_CONST_FEAT_QUICKSPUR_STONE = 1171; +const int IP_CONST_FEAT_BES_FIRE_OF_THE_HEART = 1172; +const int IP_CONST_FEAT_BES_ENTHRALLING_LIGHT = 1173; +const int IP_CONST_FEAT_BES_COLOR_SPRAY = 1174; +const int IP_CONST_FEAT_BES_BLINDING_FLASH = 1175; +const int IP_CONST_FEAT_BES_SHOOTING_STARS = 1176; +const int IP_CONST_FEAT_BES_GLITTERING_MOTES = 1177; +const int IP_CONST_FEAT_BES_TWINKLE = 1178; +const int IP_CONST_FEAT_BES_SILVER_STARLIGHT = 1179; +const int IP_CONST_FEAT_BES_STARLIGHT_DISPELLING = 1180; +const int IP_CONST_FEAT_BES_TALES_IN_THE_SKY = 1181; +const int IP_CONST_FEAT_BES_CALL_DOWN_A_STAR = 1182; diff --git a/src/include/prc_weap_apt.nss b/src/include/prc_weap_apt.nss new file mode 100644 index 0000000..8fa1043 --- /dev/null +++ b/src/include/prc_weap_apt.nss @@ -0,0 +1,526 @@ +//::////////////////////////////////////////////// +//:: Warblade - Weapon Aptitude +//:: prc_weap_apt +//::////////////////////////////////////////////// +/** @file + Allows Warblade to chose aptitude weapon. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "inc_dynconv" +#include "prc_feat_const" +#include "prc_inc_combat" + +const string OPTION_FOCUS_WEAPON_1 = "PRC_WEAPAPT_FOCUS_1"; +const string OPTION_CRITICAL_WEAPON_1 = "PRC_WEAPAPT_CRITICAL_1"; +const string OPTION_FOCUS_WEAPON_2 = "PRC_WEAPAPT_FOCUS_2"; +const string OPTION_CRITICAL_WEAPON_2 = "PRC_WEAPAPT_CRITICAL_2"; + +const string WEAPON_FILE = "prc_weap_items"; + +//:: Test void +//:: void main (){} + +int GetFeatItemProperty(int nFeatNumber, int nStart, int nEnd) +{ + if (nFeatNumber >= 0) + { + int i; + for(i=nStart; i<=nEnd; i++) + { + string sFeat = Get2DACache("iprp_feats", "FeatIndex", i); + if(sFeat == "") + continue; + int nFeat = StringToInt(sFeat); + if(nFeat == nFeatNumber) + return i; + } + } + return -1; +} + +int GetWeaponFocusFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_CLUB, IP_CONST_FEAT_WEAPON_FOCUS_RAY); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT, IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_LANCE, IP_CONST_FEAT_WEAPON_FOCUS_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WeapFocCreature, IP_CONST_FEAT_WeapFocCreature); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetEpicWeaponFocusFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_TRIDENT, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_CLUB, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_RAY); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_LIGHT_LANCE, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE, IP_CONST_FEAT_EPIC_WEAPON_FOCUS_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WeapEpicFocCreature, IP_CONST_FEAT_WeapEpicFocCreature); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetWeaponSpecializationFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_CLUB, IP_CONST_FEAT_WEAPON_SPECIALIZATION_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_TRIDENT, IP_CONST_FEAT_WEAPON_SPECIALIZATION_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_SPECIALIZATION_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_LANCE, IP_CONST_FEAT_WEAPON_SPECIALIZATION_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE, IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE, IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE, IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WeapSpecCreature, IP_CONST_FEAT_WeapSpecCreature); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetEpicWeaponSpecializationFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_TRIDENT, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_LIGHT_LANCE, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE, IP_CONST_FEAT_EPIC_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WeapEpicSpecCreature, IP_CONST_FEAT_WeapEpicSpecCreature); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetImprovedCriticalFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_DAGGER, IP_CONST_FEAT_IMPROVED_CRITICAL_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_LIGHT_LANCE, IP_CONST_FEAT_IMPROVED_CRITICAL_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE, IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE, IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE, IP_CONST_FEAT_IMPROVED_CRITICAL_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_EAGLE_CLAW, IP_CONST_FEAT_IMPROVED_CRITICAL_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_TRIDENT, IP_CONST_FEAT_IMPROVED_CRITICAL_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_ImpCritCreature, IP_CONST_FEAT_ImpCritCreature); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_IMPROVED_CRITICAL_UNARMED, IP_CONST_FEAT_IMPROVED_CRITICAL_UNARMED); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetOverwhelmingCriticalFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_TRIDENT, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_LIGHT_LANCE, IP_CONST_FEAT_EPIC_OVERWHELMING_CRITICAL_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE, IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_THINBLADE, IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE, IP_CONST_FEAT_OVERWHELMING_CRITICAL_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_OVERCRITICAL_CREATURE, IP_CONST_FEAT_OVERCRITICAL_CREATURE); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetDevastatingCriticalFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_TRIDENT, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_LIGHT_LANCE, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE, IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_THINBLADE, IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_COURTBLADE, IP_CONST_FEAT_DEVASTATING_CRITICAL_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_CLUB, IP_CONST_FEAT_EPIC_DEVASTATING_CRITICAL_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_DEVCRITICAL_CREATURE, IP_CONST_FEAT_DEVCRITICAL_CREATURE); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetWeaponOfChoiceFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_TRIDENT, IP_CONST_FEAT_WEAPON_OF_CHOICE_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_OF_CHOICE_EAGLE_CLAW); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_SICKLE, IP_CONST_FEAT_WEAPON_OF_CHOICE_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_LIGHT_LANCE, IP_CONST_FEAT_WEAPON_OF_CHOICE_GOAD); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE, IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_LIGHTBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE, IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_THINBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE, IP_CONST_FEAT_WEAPON_OF_CHOICE_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + + return -1; +} + +int GetSanctifyMartialStrikeFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_SANCTIFY_MARTIAL_SICKLE, IP_CONST_FEAT_SANCTIFY_MARTIAL_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_SANCTIFY_MARTIAL_EAGLE_CLAW, IP_CONST_FEAT_SANCTIFY_MARTIAL_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +int GetVileMartialStrikeFeatItemProperty(int nFeatNumber) +{ + int nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_VILE_MARTIAL_CLUB, IP_CONST_FEAT_VILE_MARTIAL_TRIDENT); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_VILE_MARTIAL_MINDBLADE, IP_CONST_FEAT_VILE_MARTIAL_MINDBLADE); + if(nItemProperty != -1) return nItemProperty; + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_VILE_MARTIAL_EAGLE_CLAW, IP_CONST_FEAT_VILE_MARTIAL_ELVEN_COURTBLADE); + if(nItemProperty != -1) return nItemProperty; + return -1; +} + +struct WeaponFeat TakeWeaponFeatCensus(object oPC) +{ + struct WeaponFeat rWeaponFeatCensus = InitWeaponFeat(0); + + int i = 0; + while(Get2DACache(WEAPON_FILE, "label", i) != "") //until we hit a nonexistant line + { + int nIndex = StringToInt(Get2DACache(WEAPON_FILE, "BaseItemsIndex", i)); + struct WeaponFeat rWeaponFeats = GetAllFeatsOfWeaponType(nIndex); + + //Weapon Focus line of feats + if(rWeaponFeats.Focus != -1 && GetHasFeat(rWeaponFeats.Focus, oPC)) + { + rWeaponFeatCensus.Focus += 1; + int bEpicFocus = FALSE; + if(rWeaponFeats.EpicFocus != -1 && GetHasFeat(rWeaponFeats.EpicFocus, oPC)) + { + rWeaponFeatCensus.EpicFocus += 1; + bEpicFocus = TRUE; + } + if(rWeaponFeats.Specialization != -1 && GetHasFeat(rWeaponFeats.Specialization, oPC)) + { + rWeaponFeatCensus.Specialization += 1; + if(rWeaponFeats.EpicSpecialization != -1 && bEpicFocus && GetHasFeat(rWeaponFeats.EpicSpecialization, oPC)) + rWeaponFeatCensus.EpicSpecialization += 1; + } + if(rWeaponFeats.WeaponOfChoice != -1 && GetHasFeat(rWeaponFeats.WeaponOfChoice, oPC)) + rWeaponFeatCensus.WeaponOfChoice += 1; + if(rWeaponFeats.SanctifyMartialStrike != -1 && GetHasFeat(rWeaponFeats.SanctifyMartialStrike, oPC)) + rWeaponFeatCensus.SanctifyMartialStrike += 1; + if(rWeaponFeats.VileMartialStrike != -1 && GetHasFeat(rWeaponFeats.VileMartialStrike, oPC)) + rWeaponFeatCensus.VileMartialStrike += 1; + } + + //Improved Critical line of feats + if(rWeaponFeats.ImprovedCritical != -1 && GetHasFeat(rWeaponFeats.ImprovedCritical, oPC)) + { + rWeaponFeatCensus.ImprovedCritical += 1; + if(rWeaponFeats.OverwhelmingCritical != -1 && GetHasFeat(rWeaponFeats.OverwhelmingCritical, oPC)) + { + rWeaponFeatCensus.OverwhelmingCritical += 1; + if(rWeaponFeats.DevastatingCritical != -1 && GetHasFeat(rWeaponFeats.DevastatingCritical, oPC)) + rWeaponFeatCensus.DevastatingCritical += 1; + } + } + + i++; //go to next line + } + + if(GetHasFeat(FEAT_WEAPON_FOCUS_APTITUDE_1, oPC)) + rWeaponFeatCensus.Focus += 1; + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_APTITUDE_1, oPC)) + rWeaponFeatCensus.Specialization += 1; + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_APTITUDE_1, oPC)) + rWeaponFeatCensus.EpicFocus += 1; + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_APTITUDE_1, oPC)) + rWeaponFeatCensus.EpicSpecialization += 1; + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_APTITUDE_1, oPC)) + rWeaponFeatCensus.ImprovedCritical += 1; + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_APTITUDE_1, oPC)) + rWeaponFeatCensus.OverwhelmingCritical += 1; + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_APTITUDE_1, oPC)) + rWeaponFeatCensus.DevastatingCritical += 1; + if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_APTITUDE_1, oPC)) + rWeaponFeatCensus.WeaponOfChoice += 1; + if(GetHasFeat(FEAT_SANCTIFY_MARTIAL_STRIKE_APTITUDE_1, oPC)) + rWeaponFeatCensus.SanctifyMartialStrike += 1; + if(GetHasFeat(FEAT_VILE_MARTIAL_STRIKE_APTITUDE_1, oPC)) + rWeaponFeatCensus.VileMartialStrike += 1; + + if(GetHasFeat(FEAT_WEAPON_FOCUS_APTITUDE_2, oPC)) + rWeaponFeatCensus.Focus += 1; + if(GetHasFeat(FEAT_WEAPON_SPECIALIZATION_APTITUDE_2, oPC)) + rWeaponFeatCensus.Specialization += 1; + if(GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_APTITUDE_2, oPC)) + rWeaponFeatCensus.EpicFocus += 1; + if(GetHasFeat(FEAT_EPIC_WEAPON_SPECIALIZATION_APTITUDE_2, oPC)) + rWeaponFeatCensus.EpicSpecialization += 1; + if(GetHasFeat(FEAT_IMPROVED_CRITICAL_APTITUDE_2, oPC)) + rWeaponFeatCensus.ImprovedCritical += 1; + if(GetHasFeat(FEAT_EPIC_OVERWHELMING_CRITICAL_APTITUDE_2, oPC)) + rWeaponFeatCensus.OverwhelmingCritical += 1; + if(GetHasFeat(FEAT_EPIC_DEVASTATING_CRITICAL_APTITUDE_2, oPC)) + rWeaponFeatCensus.DevastatingCritical += 1; + if(GetHasFeat(FEAT_WEAPON_OF_CHOICE_APTITUDE_2, oPC)) + rWeaponFeatCensus.WeaponOfChoice += 1; + if(GetHasFeat(FEAT_SANCTIFY_MARTIAL_STRIKE_APTITUDE_2, oPC)) + rWeaponFeatCensus.SanctifyMartialStrike += 1; + if(GetHasFeat(FEAT_VILE_MARTIAL_STRIKE_APTITUDE_2, oPC)) + rWeaponFeatCensus.VileMartialStrike += 1; + + return rWeaponFeatCensus; +} + +int WeaponItemType(object oWeapon) +{ + if(oWeapon == OBJECT_INVALID) + return BASE_ITEM_INVALID; //Unarmed strike + else + { + int nWeaponItemType = GetBaseItemType(oWeapon); + if(StringToInt(Get2DACache("baseitems", "WeaponType", nWeaponItemType)) > 0) + return nWeaponItemType; + else + return -1; + } +} + +object MainHandWeapon(object oPC) +{ + return GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); +} + +object OffHandWeapon(object oPC) +{ + return GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); +} + +void DoApplyWeaponAptitude(object oPC, struct WeaponFeat rWeaponFeatCensus, int nWeaponFeatThreshold, string sFocusWeaponOption, string sCriticalWeaponOption) +{ + object oPCHide = GetPCSkin(oPC); + + int nFocusWeaponIndex = GetLocalInt(oPC, sFocusWeaponOption); + if(nFocusWeaponIndex) + { + int nFocusWeaponType = StringToInt(Get2DACache(WEAPON_FILE, "BaseItemsIndex", nFocusWeaponIndex-1)); + if(nFocusWeaponType == BASE_ITEM_INVALID+1) + nFocusWeaponType = WeaponItemType(MainHandWeapon(oPC)); + else if (nFocusWeaponType == BASE_ITEM_INVALID+2) + nFocusWeaponType = WeaponItemType(OffHandWeapon(oPC)); + + if(nFocusWeaponType != -1) + { + struct WeaponFeat rWeaponFeats = GetAllFeatsOfWeaponType(nFocusWeaponType); + + if(rWeaponFeatCensus.Focus >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.Focus; + int nItemProperty = GetWeaponFocusFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Focus: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.EpicFocus >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.EpicFocus; + int nItemProperty = GetEpicWeaponFocusFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Epic Focus: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.Specialization >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.Specialization; + int nItemProperty = GetWeaponSpecializationFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Specialization: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.EpicSpecialization >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.EpicSpecialization; + int nItemProperty = GetEpicWeaponSpecializationFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Epic Specialization: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.WeaponOfChoice >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.WeaponOfChoice; + int nItemProperty = GetWeaponOfChoiceFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Weapon of Choice: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.SanctifyMartialStrike >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.SanctifyMartialStrike; + int nItemProperty = GetSanctifyMartialStrikeFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Sanctify Martial Strike: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.VileMartialStrike >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.VileMartialStrike; + int nItemProperty = GetVileMartialStrikeFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Vile Martial Strike: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.ImprovedCritical >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.ImprovedCritical; + int nItemProperty = GetImprovedCriticalFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Improved Critical: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.OverwhelmingCritical >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.OverwhelmingCritical; + int nItemProperty = GetOverwhelmingCriticalFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Overwhelming Critical: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.DevastatingCritical >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.DevastatingCritical; + int nItemProperty = GetDevastatingCriticalFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Devastating Critical: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + } + } + +/* int nCriticalWeaponIndex = GetLocalInt(oPC, sCriticalWeaponOption); + if(nCriticalWeaponIndex) + { + int nCriticalWeaponType = StringToInt(Get2DACache(WEAPON_FILE, "BaseItemsIndex", nCriticalWeaponIndex-1)); + if(nCriticalWeaponType == BASE_ITEM_INVALID+1) + nCriticalWeaponType = WeaponItemType(MainHandWeapon(oPC)); + else if (nCriticalWeaponType == BASE_ITEM_INVALID+2) + nCriticalWeaponType = WeaponItemType(OffHandWeapon(oPC)); + + if(nCriticalWeaponType != -1) + { + struct WeaponFeat rWeaponFeats = GetAllFeatsOfWeaponType(nCriticalWeaponType); + + if(rWeaponFeatCensus.ImprovedCritical >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.ImprovedCritical; + int nItemProperty = GetImprovedCriticalFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Improved Critical: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.OverwhelmingCritical >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.OverwhelmingCritical; + int nItemProperty = GetOverwhelmingCriticalFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Overwhelming Critical: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + + if(rWeaponFeatCensus.DevastatingCritical >= nWeaponFeatThreshold) + { + int nFeat = rWeaponFeats.DevastatingCritical; + int nItemProperty = GetDevastatingCriticalFeatItemProperty(nFeat); + if(DEBUG) DoDebug("Devastating Critical: " + IntToString(nFeat) + ", " + IntToString(nItemProperty)); + if (nItemProperty != -1) + AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(nItemProperty), oPCHide); + } + } + }*/ +} + +void ApplyWeaponAptitude(object oPC, int bAllowDuringCombat) +{ + if(!bAllowDuringCombat && GetIsInCombat(oPC)) + { + FloatingTextStringOnCreature(GetStringByStrRef(16837722), oPC, FALSE); + IncrementRemainingFeatUses(oPC, FEAT_WEAPON_APTITUDE); + return; + } + + struct WeaponFeat rWeaponFeatCensus = TakeWeaponFeatCensus(oPC); + DoApplyWeaponAptitude(oPC, rWeaponFeatCensus, 1, OPTION_FOCUS_WEAPON_1, OPTION_CRITICAL_WEAPON_1); + //DoApplyWeaponAptitude(oPC, rWeaponFeatCensus, 2, OPTION_FOCUS_WEAPON_2, OPTION_CRITICAL_WEAPON_2); + SetLocalInt(oPC, "PRC_WEAPON_APTITUDE_APPLIED", 1); + SetLocalInt(GetPCSkin(oPC), "PRC_WEAPON_APTITUDE_APPLIED", 1); +} diff --git a/src/include/prc_x2_craft.nss b/src/include/prc_x2_craft.nss new file mode 100644 index 0000000..5834f63 --- /dev/null +++ b/src/include/prc_x2_craft.nss @@ -0,0 +1,2946 @@ +//:://///////////////////////////////////////////// +//:: prc_x2_craft +//:: Copyright (c) 2003 Bioare Corp. +//::////////////////////////////////////////////// +/* + + Central include for crafting feat and + crafting skill system. + +*/ +//::////////////////////////////////////////////// +//:: Created By: Georg Zoeller +//:: Created On: 2003-05-09 +//:: Last Updated On: 2003-10-14 +//::////////////////////////////////////////////// + + +struct craft_struct +{ + int nRow; + string sResRef; + int nDC; + int nCost; + string sLabel; +}; + +struct craft_receipe_struct +{ + int nMode; + object oMajor; + object oMinor; +}; + +struct craft_cost_struct +{ + int nGoldCost; + int nXPCost; + int nTimeCost; +}; + +const string X2_CI_CRAFTSKILL_CONV ="x2_p_craftskills"; + +// Brew Potion related Constants +/* moved to be code switches + +const int X2_CI_BREWPOTION_MAXLEVEL = 3; // Max Level for potions +const int X2_CI_BREWPOTION_COSTMODIFIER = 50; // gp Brew Potion XPCost Modifier + +// Scribe Scroll related constants +const int X2_CI_SCRIBESCROLL_COSTMODIFIER = 25; // Scribescroll Cost Modifier + +// Craft Wand related constants +const int X2_CI_CRAFTWAND_MAXLEVEL = 4; +const int X2_CI_CRAFTWAND_COSTMODIFIER = 750; +*/ +const int X2_CI_BREWPOTION_FEAT_ID = 944; // Brew Potion feat simulation +const int X2_CI_SCRIBESCROLL_FEAT_ID = 945; +const int X2_CI_CRAFTWAND_FEAT_ID = 946; +const int X2_CI_CRAFTROD_FEAT_ID = 2927; +const int X2_CI_CRAFTROD_EPIC_FEAT_ID = 3490; +const int X2_CI_CRAFTSTAFF_FEAT_ID = 2928; +const int X2_CI_CRAFTSTAFF_EPIC_FEAT_ID = 3491; +const string X2_CI_BREWPOTION_NEWITEM_RESREF = "x2_it_pcpotion"; // ResRef for new potion item +const string X2_CI_SCRIBESCROLL_NEWITEM_RESREF = "x2_it_pcscroll"; // ResRef for new scroll item +const string X2_CI_CRAFTWAND_NEWITEM_RESREF = "x2_it_pcwand"; +//const string X2_CI_CRAFTROD_NEWITEM_RESREF = "x2_it_pcwand"; +//const string X2_CI_CRAFTSTAFF_NEWITEM_RESREF = "x2_it_pcwand"; + +// 2da for the craftskills +const string X2_CI_CRAFTING_WP_2DA = "des_crft_weapon" ; +const string X2_CI_CRAFTING_AR_2DA = "des_crft_armor" ; +const string X2_CI_CRAFTING_MAT_2DA = "des_crft_mat"; + + +// 2da for matching spells to properties +const string X2_CI_CRAFTING_SP_2DA = "des_crft_spells" ; +// Base custom token for item modification conversations (do not change unless you want to change the conversation too) +const int X2_CI_CRAFTINGSKILL_CTOKENBASE = 13220; + +// Base custom token for DC item modification conversations (do not change unless you want to change the conversation too) +const int X2_CI_CRAFTINGSKILL_DC_CTOKENBASE = 14220; + +// Base custom token for DC item modification conversations (do not change unless you want to change the conversation too) +const int X2_CI_CRAFTINGSKILL_GP_CTOKENBASE = 14320; + +// Base custom token for DC item modification conversations (do not change unless you want to change the conversation too) +const int X2_CI_MODIFYARMOR_GP_CTOKENBASE = 14420; + +//How many items per 2da row in X2_IP_CRAFTING_2DA, do not change>4 until you want to create more conversation condition scripts as well +const int X2_CI_CRAFTING_ITEMS_PER_ROW = 5; + +// name of the scroll 2da +const string X2_CI_2DA_SCROLLS = "des_crft_scroll"; + +const int X2_CI_CRAFTMODE_INVALID = 0; +const int X2_CI_CRAFTMODE_CONTAINER = 1; // no longer used, but left in for the community to reactivate +const int X2_CI_CRAFTMODE_BASE_ITEM = 2; +const int X2_CI_CRAFTMODE_ASSEMBLE = 3; + +const int X2_CI_MAGICTYPE_INVALID = 0; +const int X2_CI_MAGICTYPE_ARCANE = 1; +const int X2_CI_MAGICTYPE_DIVINE = 2; + +const int X2_CI_MODMODE_INVALID = 0; +const int X2_CI_MODMODE_ARMOR = 1; +const int X2_CI_MODMODE_WEAPON = 2; + +// Runecrafting constants +const int PRC_RUNE_BASECOST = 0; +const int PRC_RUNE_CHARGES = 1; +const int PRC_RUNE_PERDAY = 2; +const int PRC_RUNE_MAXCHARGES = 3; +const int PRC_RUNE_MAXUSESPERDAY = 4; +// Attune Gem constants +const int PRC_GEM_BASECOST = 5; +const int PRC_GEM_PERLEVEL = 6; +// Craft Skull Talisman constants +const int PRC_SKULL_BASECOST = 7; + +// * Returns TRUE if an item is a Craft Base Item +// * to be used in spellscript that can be cast on items - i.e light +int CIGetIsCraftFeatBaseItem( object oItem ); + +// * Checks if the last spell cast was used to brew potion and will do the brewing process. +// * Returns TRUE if the spell was indeed used to brew a potion (regardless of the actual outcome of the brewing process) +// * Meant to be used in spellscripts only +int CICraftCheckBrewPotion(object oSpellTarget, object oCaster, int nID = 0); + +// * Checks if the last spell cast was used to scribe a scroll and handles the scribe scroll process +// * Returns TRUE if the spell was indeed used to scribe a scroll (regardless of the actual outcome) +// * Meant to be used in spellscripts only +int CICraftCheckScribeScroll(object oSpellTarget, object oCaster, int nID = 0); + +// * Create a new potion item based on the spell nSpellID on the creator +object CICraftBrewPotion(object oCreator, int nSpellID ); + +// * Create a new scroll item based on the spell nSpellID on the creator +object CICraftScribeScroll(object oCreator, int nSpellID); + + +// * Checks if the caster intends to use his item creation feats and +// * calls appropriate item creation subroutine if conditions are met (spell cast on correct item, etc). +// * Returns TRUE if the spell was used for an item creation feat +int CIGetSpellWasUsedForItemCreation(object oSpellTarget); + +// This function checks whether Inscribe Rune is turned on +// and if so, deducts the appropriate experience and gold +// then creates the rune in the caster's inventory. +// This will also cause the spell to fail if turned on. +int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0); + +// This function checks whether Attune Gem is turned on +// and if so, deducts the appropriate experience and gold +// then creates the gem in the caster's inventory. +// This will also cause the spell to fail if turned on. +int AttuneGem(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0); + +// Gets the Magical Artisan feat given a particular crafting feat +int GetMagicalArtisanFeat(int nCraftingFeat); + +// Gets the modified gold cost taking cost reduction feats and cost +// scaling switches into account +int GetModifiedGoldCost(int nCost, object oPC, int nCraftingFeat); + +// Gets the modified xp cost taking cost reduction feats and cost +// scaling switches into account +int GetModifiedXPCost(int nCost, object oPC, int nCraftingFeat); + +// Gets the modified time cost taking cost reduction feats and cost +// scaling switches into account +int GetModifiedTimeCost(int nCost, object oPC, int nCraftingFeat); + +// Imbue item check for warlocks, returns TRUE if a spell requirement is met +int CheckImbueItem(object oPC, int nSpell); + +// Gets PnP xp cost given a gold cost and whether the item is epic +int GetPnPItemXPCost(int nCost, int bEpic); + +// Returns a struct containing gold, xp and time costs given the base cost and other arguments +struct craft_cost_struct GetModifiedCostsFromBase(int nCost, object oPC, int nCraftingFeat, int bEpic); + +// Additional checking for emulating spells during crafting +int CheckAlternativeCrafting(object oPC, int nSpell, struct craft_cost_struct costs); + +// Returns the maximum of caster level used and other effective levels from emulating spells +int GetAlternativeCasterLevel(object oPC, int nLevel); + +////////////////////////////////////////////////// +/* Include section */ +////////////////////////////////////////////////// + +//#include "prc_x2_itemprop" +//#include "x2_inc_switches" +#include "prc_inc_newip" +#include "prc_inc_spells" +#include "prc_add_spell_dc" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + + +// * Returns the innate level of a spell. If bDefaultZeroToOne is given +// * Level 0 spell will be returned as level 1 spells +int CIGetSpellInnateLevel(int nSpellID, int bDefaultZeroToOne = FALSE) +{ + int nRet = StringToInt(Get2DACache(X2_CI_CRAFTING_SP_2DA, "Level", nSpellID)); + if (nRet == 0 && bDefaultZeroToOne == TRUE) // Was missing the "bDefaultZeroToOne == TRUE" check, fixed to match specification - Ornedan + nRet = 1; + + return nRet; +} + +// * Makes oPC do a Craft check using nSkill to create the item supplied in sResRe +// * If oContainer is specified, the item will be created there. +// * Throwing weapons are created with stack sizes of 10, ammo with 20 +// * oPC - The player crafting +// * nSkill - SKILL_CRAFT_WEAPON or SKILL_CRAFT_ARMOR, +// * sResRef - ResRef of the item to be crafted +// * nDC - DC to beat to succeed +// * oContainer - if a container is specified, create item inside +object CIUseCraftItemSkill(object oPC, int nSkill, string sResRef, int nDC, object oContainer = OBJECT_INVALID); + +// * Returns TRUE if a spell is prevented from being used with one of the crafting feats +int CIGetIsSpellRestrictedFromCraftFeat(int nSpellID, int nFeatID); + +// * Return craftitemstructdata +struct craft_struct CIGetCraftItemStructFrom2DA(string s2DA, int nRow, int nItemNo); + +// * Return the type of magic as one of the following constants +// * const int X2_CI_MAGICTYPE_INVALID = 0; +// * const int X2_CI_MAGICTYPE_ARCANE = 1; +// * const int X2_CI_MAGICTYPE_DIVINE = 2; +// * Parameters: +// * nClass - CLASS_TYPE_* constant +int CI_GetClassMagicType(int nClass) +{ + if(GetIsArcaneClass(nClass)) + return X2_CI_MAGICTYPE_ARCANE; + else if(GetIsDivineClass(nClass)) + return X2_CI_MAGICTYPE_DIVINE; + + return X2_CI_MAGICTYPE_INVALID; +} + +string GetMaterialComponentTag(int nPropID) +{ + string sRet = Get2DACache("des_matcomp","comp_tag",nPropID); + return sRet; +} + + +// ----------------------------------------------------------------------------- +// Return true if oItem is a crafting target item +// ----------------------------------------------------------------------------- +int CIGetIsCraftFeatBaseItem(object oItem) +{ + int nBt = GetBaseItemType(oItem); + // blank scroll, empty potion, wand + if (nBt == BASE_ITEM_BLANK_POTION || + nBt == BASE_ITEM_BLANK_SCROLL || + nBt == BASE_ITEM_BLANK_WAND || + nBt == BASE_ITEM_CRAFTED_ROD || + nBt == BASE_ITEM_CRAFTED_STAFF) + return TRUE; + else + return FALSE; +} + + +// ----------------------------------------------------------------------------- +// Georg, 2003-06-12 +// Create a new playermade potion object with properties matching nSpellID and return it +// ----------------------------------------------------------------------------- +object CICraftBrewPotion(object oCreator, int nSpellID ) +{ + + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + + object oTarget; + // * GZ 2003-09-11: If the current spell cast is not acid fog, and + // * returned property ID is 0, bail out to prevent + // * creation of acid fog items. + if (nPropID == 0 && nSpellID != 0) + { + FloatingTextStrRefOnCreature(84544,oCreator); + return OBJECT_INVALID; + } + + /* //just a tad retarded, don't you think? other crafting feats are not similarly restricted + //Uses per day + int nUsesAllowed; + + if(GetHasFeat(FEAT_BREW_4PERDAY,oCreator)) nUsesAllowed = 4; + + else if(GetHasFeat(FEAT_BREW_3PERDAY, oCreator)) nUsesAllowed = 3; + + else if(GetHasFeat(FEAT_BREW_2PERDAY, oCreator)) nUsesAllowed = 2; + + else nUsesAllowed = 1; + + int nUsed = GetLocalInt(oCreator, "PRC_POTIONS_BREWED"); + + if(nUsed >= nUsesAllowed) + { + SendMessageToPC(oCreator, "You must rest before you can brew any more potions"); + return OBJECT_INVALID; + } + */ + + int nCasterLevel = GetAlternativeCasterLevel(oCreator, PRCGetCasterLevel(oCreator)); + + if (nPropID != -1) + { + itemproperty ipProp = ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE); + oTarget = CreateItemOnObject(X2_CI_BREWPOTION_NEWITEM_RESREF,oCreator); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp,oTarget); + if(GetPRCSwitch(PRC_BREW_POTION_CASTER_LEVEL)) + { + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oTarget); + itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oTarget); + itemproperty ipDC = ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)); + AddItemProperty(DURATION_TYPE_PERMANENT,ipDC,oTarget); + } + + //Increment usage + //SetLocalInt(oCreator, "PRC_POTIONS_BREWED", nUsed++); + } + return oTarget; +} + +// ----------------------------------------------------------------------------- +// Wrapper for the crafting cost calculation, returns GP required +// ----------------------------------------------------------------------------- +int CIGetCraftGPCost(int nLevel, int nMod, string sCasterLevelSwitch) +{ + int nLvlRow = IPGetIPConstCastSpellFromSpellID(PRCGetSpellId()); + int nCLevel;// = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow)); + //PRC modification + if(GetPRCSwitch(sCasterLevelSwitch)) + { + nCLevel = PRCGetCasterLevel(); + } + else + { + nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow)); + } + + // ------------------------------------------------------------------------- + // in case we don't get a valid CLevel, use spell level instead + // ------------------------------------------------------------------------- + if (nCLevel ==0) + { + nCLevel = nLevel; + } + int nRet = nCLevel * nLevel * nMod; + return nRet; + +} + +// ----------------------------------------------------------------------------- +// Georg, 2003-06-12 +// Create a new playermade wand object with properties matching nSpellID +// and return it +// ----------------------------------------------------------------------------- +object CICraftCraftWand(object oCreator, int nSpellID ) +{ + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + + object oTarget; + // * GZ 2003-09-11: If the current spell cast is not acid fog, and + // * returned property ID is 0, bail out to prevent + // * creation of acid fog items. + if (nPropID == 0 && nSpellID != 0) + { + FloatingTextStrRefOnCreature(84544,oCreator); + return OBJECT_INVALID; + } + + + //int nClass = PRCGetLastSpellCastClass(); + int nCasterLevel = GetAlternativeCasterLevel(oCreator, PRCGetCasterLevel(oCreator)); + + if (nPropID != -1) + { + itemproperty ipProp = ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE); + oTarget = CreateItemOnObject(X2_CI_CRAFTWAND_NEWITEM_RESREF,oCreator); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp,oTarget); + + if(GetPRCSwitch(PRC_CRAFT_WAND_CASTER_LEVEL)) + { + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oTarget); + itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oTarget); + itemproperty ipDC = ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)); + AddItemProperty(DURATION_TYPE_PERMANENT,ipDC,oTarget); + } + + //int nType = CI_GetClassMagicType(nClass); + //itemproperty ipLimit; + + /* //this is a bit silly, really, removed in line with other crafting types + if (nType == X2_CI_MAGICTYPE_DIVINE) + { + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_PALADIN); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_RANGER); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_DRUID); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_CLERIC); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + } + else if (nType == X2_CI_MAGICTYPE_ARCANE) + { + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_WIZARD); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_SORCERER); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + ipLimit = ItemPropertyLimitUseByClass(CLASS_TYPE_BARD); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + } + + if(nClass != CLASS_TYPE_WARLOCK) + { + ipLimit = ItemPropertyLimitUseByClass(nClass); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLimit,oTarget); + } + */ + + int nCharges = nCasterLevel + d20(); + + if (nCharges == 0) // stupi cheaters + { + nCharges = 10+d20(); + } + // Hard core rule mode enabled + if (GetModuleSwitchValue(MODULE_SWITCH_ENABLE_CRAFT_WAND_50_CHARGES)) + { + SetItemCharges(oTarget,50); + } + else + { + SetItemCharges(oTarget,nCharges); + } + // TODOL Add use restrictions there when item becomes available + } + return oTarget; +} + +// ----------------------------------------------------------------------------- +// Georg, 2003-06-12 +// Create and Return a magic wand with an item property +// matching nSpellID. Charges are set to d20 + casterlevel +// capped at 50 max +// ----------------------------------------------------------------------------- +object CICraftScribeScroll(object oCreator, int nSpellID) +{ + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + object oTarget; + // Handle optional material components + string sMat = GetMaterialComponentTag(nPropID); + if (sMat != "") + { + object oMat = GetItemPossessedBy(oCreator,sMat); + if (oMat== OBJECT_INVALID) + { + FloatingTextStrRefOnCreature(83374, oCreator); // Missing material component + return OBJECT_INVALID; + } + else + { + DestroyObject (oMat); + } + } + + // get scroll resref from scrolls lookup 2da + int nClass =PRCGetLastSpellCastClass (); + string sClass = ""; + switch (nClass) + { + case CLASS_TYPE_WIZARD: + sClass = "Wiz_Sorc"; + break; + + case CLASS_TYPE_SORCERER: + sClass = "Wiz_Sorc"; + break; + case CLASS_TYPE_CLERIC: + case CLASS_TYPE_UR_PRIEST: + sClass = "Cleric"; + break; + case CLASS_TYPE_PALADIN: + sClass = "Paladin"; + break; + case CLASS_TYPE_DRUID: + case CLASS_TYPE_BLIGHTER: + sClass = "Druid"; + break; + case CLASS_TYPE_RANGER: + sClass = "Ranger"; + break; + case CLASS_TYPE_BARD: + sClass = "Bard"; + break; + } + string sResRef; + if (sClass != "") + { + sResRef = Get2DACache(X2_CI_2DA_SCROLLS,sClass,nSpellID); + if (sResRef != "") + { + oTarget = CreateItemOnObject(sResRef,oCreator); + } + + } + else + { + sResRef = "craft_scroll"; + oTarget = CreateItemOnObject(sResRef ,oCreator); + RemoveItemProperty(oTarget, GetFirstItemProperty(oTarget)); + itemproperty ipSpell = ItemPropertyCastSpell(nPropID, IP_CONST_CASTSPELL_NUMUSES_SINGLE_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,ipSpell,oTarget); + } + if(GetPRCSwitch(PRC_SCRIBE_SCROLL_CASTER_LEVEL)) + { + int nCasterLevel = GetAlternativeCasterLevel(oCreator, PRCGetCasterLevel(oCreator)); + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oTarget); + itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oTarget); + itemproperty ipDC = ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)); + AddItemProperty(DURATION_TYPE_PERMANENT,ipDC,oTarget); + } + + if (oTarget == OBJECT_INVALID) + { + WriteTimestampedLogEntry("prc_x2_craft::CICraftScribeScroll failed - Resref: " + sResRef + " Class: " + sClass + "(" +IntToString(nClass) +") " + " SpellID " + IntToString (nSpellID)); + } + return oTarget; +} + +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to brew a potion +// ----------------------------------------------------------------------------- +int CICraftCheckBrewPotion(object oSpellTarget, object oCaster, int nID = 0) +{ + + if(nID == 0) nID = PRCGetSpellId(); + + object oSpellTarget = PRCGetSpellTargetObject(); + object oCaster = OBJECT_SELF; + int nLevel = CIGetSpellInnateLevel(nID,TRUE); + if(GetPRCSwitch(PRC_BREW_POTION_CASTER_LEVEL)) + { + int nMetaMagic = PRCGetMetaMagicFeat(); + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; +These dont work as IPs since they are hardcoded */ + } + } + + // ------------------------------------------------------------------------- + // check if brew potion feat is there + // ------------------------------------------------------------------------- + if (GetHasFeat(X2_CI_BREWPOTION_FEAT_ID, oCaster) != TRUE) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; + } + + // ------------------------------------------------------------------------- + // check if spell is below maxlevel for brew potions + // ------------------------------------------------------------------------- + int nPotionMaxLevel = GetPRCSwitch(X2_CI_BREWPOTION_MAXLEVEL); + if(nPotionMaxLevel == 0) + nPotionMaxLevel = 3; + + //Master Alchemist + + if(GetHasFeat(FEAT_BREW_POTION_9TH, oCaster)) nPotionMaxLevel = 9; + else if(GetHasFeat(FEAT_BREW_POTION_8TH, oCaster)) nPotionMaxLevel = 8; + else if(GetHasFeat(FEAT_BREW_POTION_7TH, oCaster)) nPotionMaxLevel = 7; + else if(GetHasFeat(FEAT_BREW_POTION_6TH, oCaster)) nPotionMaxLevel = 6; + else if(GetHasFeat(FEAT_BREW_POTION_5TH, oCaster)) nPotionMaxLevel = 5; + else if(GetHasFeat(FEAT_BREW_POTION_4TH, oCaster)) nPotionMaxLevel = 4; + + if (nLevel > nPotionMaxLevel) + { + FloatingTextStrRefOnCreature(76416, oCaster); + return TRUE; + } + + // ------------------------------------------------------------------------- + // Check if the spell is allowed to be used with Brew Potions + // ------------------------------------------------------------------------- + if (CIGetIsSpellRestrictedFromCraftFeat(nID, X2_CI_BREWPOTION_FEAT_ID)) + { + FloatingTextStrRefOnCreature(83450, oCaster); + return TRUE; + } + + // ------------------------------------------------------------------------- + // XP/GP Cost Calculation + // ------------------------------------------------------------------------- + int nCostModifier = GetPRCSwitch(X2_CI_BREWPOTION_COSTMODIFIER); + if(nCostModifier == 0) + nCostModifier = 50; + int nCost = CIGetCraftGPCost(nLevel, nCostModifier, PRC_BREW_POTION_CASTER_LEVEL); + + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_BREW_POTION, FALSE); + + // ------------------------------------------------------------------------- + // Does Player have enough gold? + // ------------------------------------------------------------------------- + //if (GetGold(oCaster) < nGoldCost) + if(!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + + + // ------------------------------------------------------------------------- + // check for sufficient XP to cast spell + // ------------------------------------------------------------------------- + //if (nMinXPForLevel > nNewXP || nNewXP == 0 ) + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + + // ------------------------------------------------------------------------- + // Here we brew the new potion + // ------------------------------------------------------------------------- + object oPotion = CICraftBrewPotion(oCaster, nID); + + // ------------------------------------------------------------------------- + // Verify Results + // ------------------------------------------------------------------------- + if (GetIsObjectValid(oPotion)) + { + //TakeGoldFromCreature(nGoldCost, oCaster, TRUE); + //SetXP(oCaster, nNewXP); + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = Get2DACache("spells", "Name", nID); + sName = "Potion of "+GetStringByStrRef(StringToInt(sName)); + SetName(oPotion, sName); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + +} + + + +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to create a scroll +// ----------------------------------------------------------------------------- +int CICraftCheckScribeScroll(object oSpellTarget, object oCaster, int nID = 0) +{ + if(nID == 0) nID = PRCGetSpellId(); + + // ------------------------------------------------------------------------- + // check if scribe scroll feat is there + // ------------------------------------------------------------------------- + if (GetHasFeat(X2_CI_SCRIBESCROLL_FEAT_ID, oCaster) != TRUE) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; + } + + // ------------------------------------------------------------------------- + // Check if the spell is allowed to be used with Scribe Scroll + // ------------------------------------------------------------------------- + if (CIGetIsSpellRestrictedFromCraftFeat(nID, X2_CI_SCRIBESCROLL_FEAT_ID)) + { + FloatingTextStrRefOnCreature(83451, oCaster); // can not be used with this feat + return TRUE; + } + + // ------------------------------------------------------------------------- + // XP/GP Cost Calculation + // ------------------------------------------------------------------------- + int nLevel = CIGetSpellInnateLevel(nID,TRUE); + int nCostModifier = GetPRCSwitch(X2_CI_SCRIBESCROLL_COSTMODIFIER); + if(nCostModifier == 0) + nCostModifier = 25; + int nCost = CIGetCraftGPCost(nLevel, nCostModifier, PRC_SCRIBE_SCROLL_CASTER_LEVEL); + + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_SCRIBE_SCROLL, FALSE); + + if(GetPRCSwitch(PRC_SCRIBE_SCROLL_CASTER_LEVEL)) + { + int nMetaMagic = PRCGetMetaMagicFeat(); + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; +These dont work as IPs since they are hardcoded */ + } + } + + // ------------------------------------------------------------------------- + // Does Player have enough gold? + // ------------------------------------------------------------------------- + if(!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + + // ------------------------------------------------------------------------- + // check for sufficient XP to cast spell + // ------------------------------------------------------------------------- + //if (nMinXPForLevel > nNewXP || nNewXP == 0 ) + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + + // ------------------------------------------------------------------------- + // Here we scribe the scroll + // ------------------------------------------------------------------------- + object oScroll = CICraftScribeScroll(oCaster, nID); + + // ------------------------------------------------------------------------- + // Verify Results + // ------------------------------------------------------------------------- + if (GetIsObjectValid(oScroll)) + { + //---------------------------------------------------------------------- + // Some scrollsare ar not identified ... fix that here + //---------------------------------------------------------------------- + SetIdentified(oScroll,TRUE); + ActionPlayAnimation (ANIMATION_FIREFORGET_READ,1.0); + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + + return FALSE; +} + + +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a wand +// ----------------------------------------------------------------------------- +int CICraftCheckCraftWand(object oSpellTarget, object oCaster, int nID = 0) +{ + + if(nID == 0) nID = PRCGetSpellId(); + + // ------------------------------------------------------------------------- + // check if craft wand feat is there + // ------------------------------------------------------------------------- + if (GetHasFeat(X2_CI_CRAFTWAND_FEAT_ID, oCaster) != TRUE) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + + // ------------------------------------------------------------------------- + // Check if the spell is allowed to be used with Craft Wand + // ------------------------------------------------------------------------- + if (CIGetIsSpellRestrictedFromCraftFeat(nID, X2_CI_CRAFTWAND_FEAT_ID)) + { + FloatingTextStrRefOnCreature(83452, oCaster); // can not be used with this feat + return TRUE; + } + + int nLevel = CIGetSpellInnateLevel(nID,TRUE); + if(GetPRCSwitch(PRC_CRAFT_WAND_CASTER_LEVEL)) + { + int nMetaMagic = PRCGetMetaMagicFeat(); + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; +These dont work as IPs since they are hardcoded */ + } + } + + // ------------------------------------------------------------------------- + // check if spell is below maxlevel for craft want + // ------------------------------------------------------------------------- + int nMaxLevel = GetPRCSwitch(X2_CI_CRAFTWAND_MAXLEVEL); + if(nMaxLevel == 0) + nMaxLevel = 4; + if (nLevel > nMaxLevel) + { + FloatingTextStrRefOnCreature(83623, oCaster); + return TRUE; + } + + // ------------------------------------------------------------------------- + // XP/GP Cost Calculation + // ------------------------------------------------------------------------- + int nCostMod = GetPRCSwitch(X2_CI_CRAFTWAND_COSTMODIFIER); + if(nCostMod == 0) + nCostMod = 750; + int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_WAND_CASTER_LEVEL); + + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_WAND, FALSE); + + // ------------------------------------------------------------------------- + // Does Player have enough gold? + // ------------------------------------------------------------------------- + if(!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + + // more calculations on XP cost + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + + // ------------------------------------------------------------------------- + // check for sufficient XP to cast spell + // ------------------------------------------------------------------------- + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + + // ------------------------------------------------------------------------- + // Here we craft the wand + // ------------------------------------------------------------------------- + object oWand = CICraftCraftWand(oCaster, nID); + + // ------------------------------------------------------------------------- + // Verify Results + // ------------------------------------------------------------------------- + if (GetIsObjectValid(oWand)) + { + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = Get2DACache("spells", "Name", nID); + sName = "Wand of "+GetStringByStrRef(StringToInt(sName)); + SetName(oWand, sName); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + + return FALSE; +} + +int CICraftCheckCraftStaff(object oSpellTarget, object oCaster, int nSpellID = 0) +{ + + if(nSpellID == 0) nSpellID = PRCGetSpellId(); + int nCasterLevel = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int bSuccess = TRUE; + int nCount = 0; + itemproperty ip = GetFirstItemProperty(oSpellTarget); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + nCount++; + ip = GetNextItemProperty(oSpellTarget); + } + if(nCount >= 8) + { + FloatingTextStringOnCreature("* Failure - Too many castspell itemproperties *", oCaster); + return TRUE; + } + if(!GetHasFeat(X2_CI_CRAFTSTAFF_FEAT_ID, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + int nMetaMagic = PRCGetMetaMagicFeat(); + if(nMetaMagic && !GetHasFeat(X2_CI_CRAFTSTAFF_EPIC_FEAT_ID, oCaster)) + { + FloatingTextStringOnCreature("* Failure - You must be able to craft epic staves to apply metamagic *", oCaster); + return TRUE; // tried item creation but do not know how to do it + } + if(CIGetIsSpellRestrictedFromCraftFeat(nSpellID, X2_CI_CRAFTSTAFF_FEAT_ID)) + { + FloatingTextStrRefOnCreature(16829169, oCaster); // can not be used with this feat + return TRUE; + } + int nLevel = CIGetSpellInnateLevel(nSpellID,TRUE); + if(GetPRCSwitch(PRC_CRAFT_STAFF_CASTER_LEVEL)) + { + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; +These dont work as IPs since they are hardcoded */ + } + } + int nCostMod = GetPRCSwitch(X2_CI_CRAFTSTAFF_COSTMODIFIER); + if(!nCostMod) nCostMod = 750; + int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID); + int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow)); + int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_STAFF_CASTER_LEVEL); + + //discount for second or 3+ spells + if(nCount+1 == 2) + nCost = (nCost*3)/4; + else if(nCount+1 >= 3) + nCost = nCost/2; + + //takes epic xp costs into account + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_STAFF, (nMetaMagic > 0)); + + if(costs.nGoldCost < 1) costs.nXPCost = 1; + if(costs.nXPCost < 1) costs.nXPCost = 1; + //if(GetGold(oCaster) < nGoldCost) // enough gold? + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = (nHD * (nHD - 1)) * 500; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + //if (nMinXPForLevel > nNewXP || nNewXP == 0 ) + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpellID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + if (nPropID == 0 && nSpellID != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + if (nPropID != -1) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE),oSpellTarget); + + if(GetPRCSwitch(PRC_CRAFT_STAFF_CASTER_LEVEL)) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel),oSpellTarget); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()),oSpellTarget); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)),oSpellTarget); + } + } + else + bSuccess = FALSE; + + if(bSuccess) + { + //TakeGoldFromCreature(nGoldCost, oCaster, TRUE); + //SetXP(oCaster, nNewXP); + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + //DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = GetName(oCaster)+"'s Magic Staff"; + //sName = Get2DACache("spells", "Name", nID); + //sName = "Wand of "+GetStringByStrRef(StringToInt(sName)); + SetName(oSpellTarget, sName); + SetItemCursedFlag(oSpellTarget, FALSE); + SetDroppableFlag(oSpellTarget, TRUE); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + return TRUE; +} + +int CICraftCheckCraftRod(object oSpellTarget, object oCaster, int nSpellID = 0) +{ + + if(nSpellID == 0) nSpellID = PRCGetSpellId(); + int nCasterLevel = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int bSuccess = TRUE; + int nCount = 0; + itemproperty ip = GetFirstItemProperty(oSpellTarget); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + nCount++; + ip = GetNextItemProperty(oSpellTarget); + } + if(nCount >= 8) + { + FloatingTextStringOnCreature("* Failure - Too many castspell itemproperties *", oCaster); + return TRUE; + } + if(!GetHasFeat(X2_CI_CRAFTROD_FEAT_ID, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + int nMetaMagic = PRCGetMetaMagicFeat(); + if(nMetaMagic && !GetHasFeat(X2_CI_CRAFTROD_EPIC_FEAT_ID, oCaster)) + { + FloatingTextStringOnCreature("* Failure - You must be able to craft epic rods to apply metamagic *", oCaster); + return TRUE; // tried item creation but do not know how to do it + } + if(CIGetIsSpellRestrictedFromCraftFeat(nSpellID, X2_CI_CRAFTROD_FEAT_ID)) + { + FloatingTextStrRefOnCreature(16829169, oCaster); // can not be used with this feat + return TRUE; + } + int nLevel = CIGetSpellInnateLevel(nSpellID,TRUE); + if(GetPRCSwitch(PRC_CRAFT_ROD_CASTER_LEVEL)) + { + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; +These dont work as IPs since they are hardcoded */ + } + } + int nCostMod = GetPRCSwitch(X2_CI_CRAFTROD_COSTMODIFIER); + if(!nCostMod) nCostMod = 750; + int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID); + int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow)); + int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_ROD_CASTER_LEVEL); + + //discount for second or 3+ spells + if(nCount+1 == 2) + nCost = (nCost*3)/4; + else if(nCount+1 >= 3) + nCost = nCost/2; + + //takes epic xp costs into account + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_ROD, (nMetaMagic > 0)); + + if(costs.nGoldCost < 1) costs.nXPCost = 1; + if(costs.nXPCost < 1) costs.nXPCost = 1; + //if(GetGold(oCaster) < nGoldCost) // enough gold? + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = (nHD * (nHD - 1)) * 500; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + //if (nMinXPForLevel > nNewXP || nNewXP == 0 ) + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpellID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + if (nPropID == 0 && nSpellID != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + if (nPropID != -1) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE),oSpellTarget); + + if(GetPRCSwitch(PRC_CRAFT_ROD_CASTER_LEVEL)) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()),oSpellTarget); + } + } + else + bSuccess = FALSE; + + if(bSuccess) + { + //TakeGoldFromCreature(nGoldCost, oCaster, TRUE); + //SetXP(oCaster, nNewXP); + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + //DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = GetName(oCaster)+"'s Magic Rod"; + //sName = Get2DACache("spells", "Name", nID); + //sName = "Wand of "+GetStringByStrRef(StringToInt(sName)); + SetName(oSpellTarget, sName); + SetItemCursedFlag(oSpellTarget, FALSE); + SetDroppableFlag(oSpellTarget, TRUE); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + return TRUE; +} + +int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0) +{ + if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF; + // Get the item used to cast the spell + object oItem = GetSpellCastItem(); + if(GetResRef(oItem) == "prc_rune_1") + { + string sName = GetName(GetItemPossessor(oItem)); + if (DEBUG) FloatingTextStringOnCreature(sName + " has just cast a rune spell", oCaster, FALSE); + + if(DEBUG) DoDebug("Checking for One Use runes"); + // This check is used to clear up the one use runes + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + { + if(DEBUG) DoDebug("Rune can cast spells"); + if (GetItemPropertyCostTableValue(ip) == 5) // Only one use runes have 2 charges per use + { + if(DEBUG) DoDebug("Rune has 2 charges a use, marking it a one use rune"); + // Give it enough time for the spell to finish casting + DestroyObject(oItem, 1.0); + if(DEBUG) DoDebug("Rune destroyed."); + } + } + + ip = GetNextItemProperty(oItem); + } + } + + // If Inscribing is turned off, the spell functions as normal + if(!GetLocalInt(oCaster, "InscribeRune")) return TRUE; + + // No point being in here if you don't have runes. + if(!GetHasFeat(FEAT_INSCRIBE_RUNE, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + + // No point scribing runes from items, and its not allowed. + if (oItem != OBJECT_INVALID) + { + FloatingTextStringOnCreature("You cannot scribe a rune from an item.", oCaster, FALSE); + return TRUE; + } + + if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); + int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int nDC = PRCGetSaveDC(oTarget, oCaster); + if(!nSpell) nSpell = PRCGetSpellId(); + int nSpellLevel = 0; + int nClass = GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster); + + // This accounts for the fact that there is no bonus to runecraft at level 10 + // Also adjusts it to fit the epic progression, which starts at 13 + if (nClass >= 10) nClass -= 3; + // Bonus to Runecrafting checks from the Runecaster class + int nRuneCraft = (nClass + 2)/3; + // Runecraft local int that counts uses/charges + int nCount = GetLocalInt(oCaster, "RuneCounter"); + + int nLastClass = PRCGetLastSpellCastClass(); + if (nLastClass == CLASS_TYPE_CLERIC || nLastClass == CLASS_TYPE_UR_PRIEST) nSpellLevel = StringToInt(lookup_spell_cleric_level(nSpell)); + else if (nLastClass == CLASS_TYPE_DRUID) nSpellLevel = StringToInt(lookup_spell_druid_level(nSpell)); + else if (nLastClass == CLASS_TYPE_WIZARD || nLastClass == CLASS_TYPE_SORCERER) nSpellLevel = StringToInt(lookup_spell_level(nSpell)); + // If none of these work, check the innate level of the spell + if (nSpellLevel == 0) nSpellLevel = StringToInt(lookup_spell_innate(nSpell)); + // Minimum level. + if (nSpellLevel == 0) nSpellLevel = 1; + + // This will be modified with Runecaster code later. + int nCharges = 1; + if (GetLocalInt(oCaster, "RuneCharges")) nCharges = nCount; + if (GetLocalInt(oCaster, "RuneUsesPerDay")) + { + // 5 is the max uses per day + if (nCount > 5) nCount = 5; + int nMaxUses = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_RUNE_MAXUSESPERDAY)); + if (nCount > nMaxUses) nCount = nMaxUses; + nCharges = nCount; + } + // Can't have no charges + if (nCharges == 0) nCharges = 1; + int nMaxCharges = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_RUNE_MAXCHARGES)); + if (nCount > nMaxCharges) nCharges = nMaxCharges; + + FloatingTextStringOnCreature("Spell Level: " + IntToString(nSpellLevel), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("Caster Level: " + IntToString(nCaster), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("Number of Charges: " + IntToString(nCharges), OBJECT_SELF, FALSE); + + // Gold cost multipler, varies depending on the ability used to craft + int nMultiplier = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_RUNE_BASECOST)); + if (nClass > 0) nMultiplier /= 2; + if (GetLocalInt(oCaster, "RuneCharges")) nMultiplier = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_RUNE_CHARGES)); + if (GetLocalInt(oCaster, "RuneUsesPerDay")) nMultiplier = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_RUNE_PERDAY)); + + // Cost of the rune in gold and XP + int nCost = nSpellLevel * nCaster * nCharges * nMultiplier; + + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_INSCRIBE_RUNE, FALSE); + + FloatingTextStringOnCreature("Gold Cost: " + IntToString(costs.nGoldCost), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("XP Cost: " + IntToString(costs.nXPCost), OBJECT_SELF, FALSE); + + // See if the caster has enough gold and XP to scribe a rune, and that he passes the skill check. + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + int nGold = GetGold(oCaster); + int nNewGold = nGold - costs.nGoldCost; + int nCheck = FALSE; + // Does the PC have Maximize Rune turned on? + int nMaximize = 0; + if (GetLocalInt(oCaster, "MaximizeRune")) nMaximize = 5; + // The check does not use GetIsSkillSuccessful so it doesn't show on the PC + if ((GetSkillRank(SKILL_CRAFT_ARMOR, oCaster) + d20() + nRuneCraft) >= (20 + nSpellLevel + nMaximize)) nCheck = TRUE; + + + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStringOnCreature("You do not have enough gold to scribe this rune.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + if (!GetHasXPToSpend(oCaster, costs.nXPCost) ) + { + FloatingTextStringOnCreature("You do not have enough experience to scribe this rune.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + if (!nCheck) + { + FloatingTextStringOnCreature("You have failed the craft check to scribe this rune.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + + // Steal all the code from craft wand. + // The reason craft wand is used is because it is possible to create runes with charges using the Runecaster class. + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpell); + + // * GZ 2003-09-11: If the current spell cast is not acid fog, and + // * returned property ID is 0, bail out to prevent + // * creation of acid fog items. + if (nPropID == 0 && nSpell != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpell, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + + if (nPropID != -1) + { + // This part is always done + int nRuneChant = 0; + if (nClass >= 30) nRuneChant = 10; + else if (nClass >= 27) nRuneChant = 9; + else if (nClass >= 24) nRuneChant = 8; + else if (nClass >= 21) nRuneChant = 7; + else if (nClass >= 18) nRuneChant = 6; + else if (nClass >= 15) nRuneChant = 5; + else if (nClass >= 12) nRuneChant = 4; + else if (nClass >= 9) nRuneChant = 3; + else if (nClass >= 5) nRuneChant = 2; + else if (nClass >= 2) nRuneChant = 1; + + // Since we know they can now pay for it, create the rune stone + object oRune = CreateItemOnObject("prc_rune_1", oCaster, 1, IntToString(nRuneChant)); + + // If they have this active, the bonuses are already added, so skip + // If they don't, add the bonuses down below + if(GetHasSpellEffect(SPELL_RUNE_CHANT)) + nRuneChant = 0; + + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, PRCGetCasterLevel()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oRune); + itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpell, PRCGetMetaMagicFeat()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oRune); + itemproperty ipDC = ItemPropertyCastSpellDC(nSpell, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF) + nRuneChant); + AddItemProperty(DURATION_TYPE_PERMANENT,ipDC,oRune); + // If Maximize Rune is turned on and we pass the check, add the Maximize IProp + if (GetLocalInt(oCaster, "MaximizeRune")) + { + itemproperty ipMax = ItemPropertyCastSpellMetamagic(nSpell, METAMAGIC_MAXIMIZE); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMax,oRune); + } + + // If its uses per day instead of charges, we do some different stuff here + if (GetLocalInt(oCaster, "RuneUsesPerDay")) + { + int nIPUses; + if (nCount == 1) nIPUses = IP_CONST_CASTSPELL_NUMUSES_1_USE_PER_DAY; + else if (nCount == 2) nIPUses = IP_CONST_CASTSPELL_NUMUSES_2_USES_PER_DAY; + else if (nCount == 3) nIPUses = IP_CONST_CASTSPELL_NUMUSES_3_USES_PER_DAY; + else if (nCount == 4) nIPUses = IP_CONST_CASTSPELL_NUMUSES_4_USES_PER_DAY; + // Caps out at 5 per day + else if (nCount >= 5) nIPUses = IP_CONST_CASTSPELL_NUMUSES_5_USES_PER_DAY; + + itemproperty ipProp = ItemPropertyCastSpell(nPropID,nIPUses); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp,oRune); + } + else if (nCharges == 1) // This is to handle one use runes so the spellhooking works + { + itemproperty ipProp = ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp,oRune); + // This is done so the item exists when it is used for the game to read data off of + nCharges = 3; + } + else // Do the normal charges + { + itemproperty ipProp = ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp,oRune); + } + SetItemCharges(oRune,nCharges); + SetXP(oCaster,nNewXP); + TakeGoldFromCreature(costs.nGoldCost, oCaster, TRUE); + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = Get2DACache("spells", "Name", nSpell); + sName = "Rune of "+GetStringByStrRef(StringToInt(sName)); + if(GetLocalInt(oCaster, "MaximizeRune")) + sName = "Maximized "+sName; + SetName(oRune, sName); + } + + // If we have made it this far, they have crafted the rune and the spell has been used up, so it returns false. + return FALSE; +} + +int AttuneGem(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0) +{ + if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF; + // Get the item used to cast the spell + object oItem = GetSpellCastItem(); + if (GetTag(oItem) == "prc_attunegem") + { + string sName = GetName(GetItemPossessor(oItem)); + if (DEBUG) FloatingTextStringOnCreature(sName + " has just cast a gem spell", oCaster, FALSE); + + if(DEBUG) DoDebug("Checking for One Use Gems"); + // This check is used to clear up the one use Gems + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + { + if(DEBUG) DoDebug("Gem can cast spells"); + if (GetItemPropertyCostTableValue(ip) == 5) // Only one use Gems have 2 charges per use + { + if(DEBUG) DoDebug("Gem has 2 charges a use, marking it a one use Gem"); + // Give it enough time for the spell to finish casting + DestroyObject(oItem, 1.0); + if(DEBUG) DoDebug("Gem destroyed."); + } + } + + ip = GetNextItemProperty(oItem); + } + } + + // If Attune Gem is turned off, the spell functions as normal + if(!GetLocalInt(oCaster, "AttuneGem")) return TRUE; + + // No point being in here if you don't have Gems. + if(!GetHasFeat(FEAT_ATTUNE_GEM, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + + // No point scribing Gems from items, and its not allowed. + if (oItem != OBJECT_INVALID) + { + FloatingTextStringOnCreature("You cannot scribe a Gem from an item.", oCaster, FALSE); + return TRUE; + } + // oTarget here should be the gem. If it's not, fail. + if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); + // Only accepts bioware gems + if (GetStringLeft(GetResRef(oTarget), 5) == "it_gem") + { + FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE); + // And out we go + return TRUE; + } + + int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int nDC = PRCGetSaveDC(oTarget, oCaster); + if(!nSpell) nSpell = PRCGetSpellId(); + int nSpellLevel; + + if (PRCGetLastSpellCastClass() == CLASS_TYPE_CLERIC || PRCGetLastSpellCastClass() == CLASS_TYPE_UR_PRIEST) nSpellLevel = StringToInt(lookup_spell_cleric_level(nSpell)); + else if (PRCGetLastSpellCastClass() == CLASS_TYPE_DRUID) nSpellLevel = StringToInt(lookup_spell_druid_level(nSpell)); + else if (PRCGetLastSpellCastClass() == CLASS_TYPE_WIZARD || PRCGetLastSpellCastClass() == CLASS_TYPE_SORCERER) nSpellLevel = StringToInt(lookup_spell_level(nSpell)); + // If none of these work, check the innate level of the spell + if (nSpellLevel == 0) nSpellLevel = StringToInt(lookup_spell_innate(nSpell)); + // Minimum level. + if (nSpellLevel == 0) nSpellLevel = 1; + + int nCharges = 1; + + FloatingTextStringOnCreature("Spell Level: " + IntToString(nSpellLevel), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("Caster Level: " + IntToString(nCaster), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("Number of Charges: " + IntToString(nCharges), OBJECT_SELF, FALSE); + + // Gold cost multipler, varies depending on the ability used to craft + int nMultiplier = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_GEM_BASECOST)); + + // Cost of the Gem in gold and XP + int nCost = nSpellLevel * nCaster * nMultiplier; + + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_ATTUNE_GEM, FALSE); + + FloatingTextStringOnCreature("Gold Cost: " + IntToString(costs.nGoldCost), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("XP Cost: " + IntToString(costs.nXPCost), OBJECT_SELF, FALSE); + + // See if the caster has enough gold and XP to scribe a Gem, and that he passes the skill check. + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + int nGold = GetGold(oCaster); + int nNewGold = nGold - costs.nGoldCost; + int nCheck = FALSE; + + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStringOnCreature("You do not have enough gold to scribe this Gem.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + if (!GetHasXPToSpend(oCaster, costs.nXPCost) ) + { + FloatingTextStringOnCreature("You do not have enough experience to scribe this Gem.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + + // Is the gem worth enough? + int nGemGold = GetGoldPieceValue(oTarget); + int nGemLevel = nGemGold / StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_GEM_PERLEVEL)); + if (nGemLevel > 9) nGemLevel = 9; + if (nSpellLevel > nGemLevel) + { + FloatingTextStringOnCreature("Gem is not high enough level for this spell", oCaster, FALSE); + // The spell casts normally + return TRUE; + } + + // Steal all the code from craft wand. + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpell); + + // * GZ 2003-09-11: If the current spell cast is not acid fog, and + // * returned property ID is 0, bail out to prevent + // * creation of acid fog items. + if (nPropID == 0 && nSpell != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpell, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + + if (nPropID != -1) + { + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, PRCGetCasterLevel()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel, oTarget); + itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpell, PRCGetMetaMagicFeat()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta, oTarget); + itemproperty ipDC = ItemPropertyCastSpellDC(nSpell, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)); + AddItemProperty(DURATION_TYPE_PERMANENT,ipDC, oTarget); + +/* if (nCharges == 1) // This is to handle one use Gems so the spellhooking works + { + itemproperty ipProp = ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp, oTarget); + // This is done so the item exists when it is used for the game to read data off of + nCharges = 3; + } */ + + int nUseType = IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE; + if (nCharges == 1) + { + nUseType = IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE; + nCharges = 3; + } + itemproperty ipProp = ItemPropertyCastSpell(nPropID, nUseType); + AddItemProperty(DURATION_TYPE_PERMANENT, ipProp, oTarget); + + if (GetIsObjectValid(oTarget)) + { + itemproperty ipCurrent = GetFirstItemProperty(oTarget); + int bFound = FALSE; + while (GetIsItemPropertyValid(ipCurrent)) + { + if (GetItemPropertyType(ipCurrent) == ITEM_PROPERTY_CAST_SPELL) + { + FloatingTextStringOnCreature("? CastSpell IP successfully added", oCaster, FALSE); + bFound = TRUE; + break; + } + ipCurrent = GetNextItemProperty(oTarget); + } + + if (!bFound) + { + FloatingTextStringOnCreature("? CastSpell IP NOT FOUND on item", oCaster, FALSE); + } + } + else + { + FloatingTextStringOnCreature("? oTarget is invalid", oCaster, FALSE); + } + + SetItemCharges(oTarget, nCharges); + SetXP(oCaster, nNewXP); + TakeGoldFromCreature(costs.nGoldCost, oCaster, TRUE); + + string sName; + sName = Get2DACache("spells", "Name", nSpell); + sName = "Gem of "+GetStringByStrRef(StringToInt(sName)); + SetName(oTarget, sName); + + // This is done to allow the item to be set properly, and then alter the tag + object oNewGem = CopyObject(oTarget, GetLocation(oCaster), oCaster, "prc_attunegem"); + DestroyObject(oTarget, 0.1); + } + + // If we have made it this far, they have crafted the Gem and the spell has been used up, so it returns false. + return FALSE; +} + +int CraftSkullTalisman(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0) +{ + if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF; + // Get the item used to cast the spell + object oItem = GetSpellCastItem(); + if (GetTag(oItem) == "prc_skulltalis") + { + string sName = GetName(GetItemPossessor(oItem)); + if (DEBUG) FloatingTextStringOnCreature(sName + " has just cast a skull talisman spell", oCaster, FALSE); + + if (DEBUG) DoDebug("Checking for One Use Skulls"); + // This check is used to clear up the one use SkullTalismans + itemproperty ip = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + { + if (DEBUG) DoDebug("Skull Talisman can cast spells"); + if (GetItemPropertyCostTableValue(ip) == 5) // Only one use Skull Talismans have 2 charges per use + { + if(DEBUG) DoDebug("Skull Talisman has 2 charges a use, marking it a one use Skull Talisman"); + // Give it enough time for the spell to finish casting + DestroyObject(oItem, 1.0); + if(DEBUG) DoDebug("Skull Talisman destroyed."); + } + } + + ip = GetNextItemProperty(oItem); + } + } + + // If Inscribing is turned off, the spell functions as normal + if(!GetLocalInt(oCaster, "CraftSkullTalisman")) return TRUE; + + // No point being in here if you don't have SkullTalismans. + if(!GetHasFeat(FEAT_CRAFT_SKULL_TALISMAN, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + + // No point scribing SkullTalismans from items, and its not allowed. + if (oItem != OBJECT_INVALID) + { + FloatingTextStringOnCreature("You cannot scribe a Skull Talisman from an item.", oCaster, FALSE); + return TRUE; + } + // oTarget here should be the Caster. + if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); + + int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int nDC = PRCGetSaveDC(oTarget, oCaster); + if(!nSpell) nSpell = PRCGetSpellId(); + int nSpellLevel; + + if (PRCGetLastSpellCastClass() == CLASS_TYPE_CLERIC || PRCGetLastSpellCastClass() == CLASS_TYPE_UR_PRIEST) nSpellLevel = StringToInt(lookup_spell_cleric_level(nSpell)); + else if (PRCGetLastSpellCastClass() == CLASS_TYPE_DRUID) nSpellLevel = StringToInt(lookup_spell_druid_level(nSpell)); + else if (PRCGetLastSpellCastClass() == CLASS_TYPE_WIZARD || PRCGetLastSpellCastClass() == CLASS_TYPE_SORCERER) nSpellLevel = StringToInt(lookup_spell_level(nSpell)); + // If none of these work, check the innate level of the spell + if (nSpellLevel == 0) nSpellLevel = StringToInt(lookup_spell_innate(nSpell)); + // Minimum level. + if (nSpellLevel == 0) nSpellLevel = 1; + + int nCharges = 1; + + FloatingTextStringOnCreature("Spell Level: " + IntToString(nSpellLevel), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("Caster Level: " + IntToString(nCaster), OBJECT_SELF, FALSE); + + // Gold cost multipler, varies depending on the ability used to craft + int nMultiplier = StringToInt(Get2DACache("prc_rune_craft", "Cost", PRC_SKULL_BASECOST)); + + // Cost of the Skull Talisman in gold and XP + int nCost = nSpellLevel * nCaster * nMultiplier; + + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_SKULL_TALISMAN, FALSE); + + FloatingTextStringOnCreature("Gold Cost: " + IntToString(costs.nGoldCost), OBJECT_SELF, FALSE); + FloatingTextStringOnCreature("XP Cost: " + IntToString(costs.nXPCost), OBJECT_SELF, FALSE); + + // See if the caster has enough gold and XP to scribe a Skull Talisman, and that he passes the skill check. + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = ((nHD * (nHD - 1)) / 2) * 1000; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + int nGold = GetGold(oCaster); + int nNewGold = nGold - costs.nGoldCost; + int nCheck = FALSE; + + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStringOnCreature("You do not have enough gold to scribe this SkullTalisman.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + if (!GetHasXPToSpend(oCaster, costs.nXPCost) ) + { + FloatingTextStringOnCreature("You do not have enough experience to scribe this SkullTalisman.", oCaster, FALSE); + // Since they don't have enough, the spell casts normally + return TRUE; + } + + // Create the item to have all the effects applied to + oTarget = CreateItemOnObject("prc_skulltalis", oCaster, 1); + + // Steal all the code from craft wand. + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpell); + + // * GZ 2003-09-11: If the current spell cast is not acid fog, and + // * returned property ID is 0, bail out to prevent + // * creation of acid fog items. + if (nPropID == 0 && nSpell != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpell, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + + if (nPropID != -1) + { + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, PRCGetCasterLevel()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oTarget); + itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpell, PRCGetMetaMagicFeat()); + AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oTarget); + itemproperty ipDC = ItemPropertyCastSpellDC(nSpell, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)); + AddItemProperty(DURATION_TYPE_PERMANENT,ipDC,oTarget); + + if (nCharges == 1) // This is to handle one use Skull Talismans so the spellhooking works + { + itemproperty ipProp = ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE); + AddItemProperty(DURATION_TYPE_PERMANENT,ipProp,oTarget); + // This is done so the item exists when it is used for the game to read data off of + nCharges = 3; + } + + SetItemCharges(oTarget, nCharges); + SetXP(oCaster, nNewXP); + TakeGoldFromCreature(costs.nGoldCost, oCaster, TRUE); + + string sName; + sName = Get2DACache("spells", "Name", nSpell); + sName = "Skull Talisman of "+ GetStringByStrRef(StringToInt(sName)); + SetName(oTarget, sName); + + // This is done to allow the item to be set properly, and then alter the tag + CopyObject(oTarget, GetLocation(oCaster), oCaster, "prc_skulltalis"); + DestroyObject(oTarget, 0.1); + } + + // If we have made it this far, they have crafted the Skull Talisman and the spell has been used up, so it returns false. + return FALSE; +} + + +// ----------------------------------------------------------------------------- +// Georg, July 2003 +// Checks if the caster intends to use his item creation feats and +// calls appropriate item creation subroutine if conditions are met +// (spell cast on correct item, etc). +// Returns TRUE if the spell was used for an item creation feat +// ----------------------------------------------------------------------------- +int CIGetSpellWasUsedForItemCreation(object oSpellTarget) +{ + object oCaster = OBJECT_SELF; + + // ------------------------------------------------------------------------- + // Spell cast on crafting base item (blank scroll, etc) ? + // ------------------------------------------------------------------------- + if (!CIGetIsCraftFeatBaseItem(oSpellTarget)) + { + return FALSE; // not blank scroll baseitem + } + else + { + // --------------------------------------------------------------------- + // Check Item Creation Feats were disabled through x2_inc_switches + // --------------------------------------------------------------------- + if (GetModuleSwitchValue(MODULE_SWITCH_DISABLE_ITEM_CREATION_FEATS) == TRUE) + { + FloatingTextStrRefOnCreature(83612, oCaster); // * Item creation feats are not enabled in this module * + return FALSE; + } + if (GetLocalInt(GetArea(oCaster), PRC_AREA_DISABLE_CRAFTING)) + { + FloatingTextStrRefOnCreature(16832014, oCaster); // * Item creation feats are not enabled in this area * + return FALSE; + } + + // --------------------------------------------------------------------- + // Ensure that item creation does not work one item was cast on another + // --------------------------------------------------------------------- + if (GetSpellCastItem() != OBJECT_INVALID) + { + FloatingTextStrRefOnCreature(83373, oCaster); // can not use one item to enchant another + return TRUE; + } + + // --------------------------------------------------------------------- + // Ok, what kind of feat the user wants to use by examining the base itm + // --------------------------------------------------------------------- + int nBt = GetBaseItemType(oSpellTarget); + int nRet = FALSE; + switch (nBt) + { + case BASE_ITEM_BLANK_POTION : + // ------------------------------------------------- + // Brew Potion + // ------------------------------------------------- + nRet = CICraftCheckBrewPotion(oSpellTarget,oCaster); + break; + + + case BASE_ITEM_BLANK_SCROLL : + // ------------------------------------------------- + // Scribe Scroll + // ------------------------------------------------- + nRet = CICraftCheckScribeScroll(oSpellTarget,oCaster); + break; + + + case BASE_ITEM_BLANK_WAND : + // ------------------------------------------------- + // Craft Wand + // ------------------------------------------------- + nRet = CICraftCheckCraftWand(oSpellTarget,oCaster); + break; + + case BASE_ITEM_CRAFTED_ROD : + // ------------------------------------------------- + // Craft Rod + // ------------------------------------------------- + nRet = CICraftCheckCraftRod(oSpellTarget,oCaster); + break; + + case BASE_ITEM_CRAFTED_STAFF : + // ------------------------------------------------- + // Craft Staff + // ------------------------------------------------- + nRet = CICraftCheckCraftStaff(oSpellTarget,oCaster); + break; + + // you could add more crafting basetypes here.... + } + + return nRet; + + } + +} + +// ----------------------------------------------------------------------------- +// Makes oPC do a Craft check using nSkill to create the item supplied in sResRe +// If oContainer is specified, the item will be created there. +// Throwing weapons are created with stack sizes of 10, ammo with 20 +// ----------------------------------------------------------------------------- +object CIUseCraftItemSkill(object oPC, int nSkill, string sResRef, int nDC, object oContainer = OBJECT_INVALID) +{ + int bSuccess = GetIsSkillSuccessful(oPC, nSkill, nDC); + object oNew; + if (bSuccess) + { + // actual item creation + // if a crafting container was specified, create inside + int bFix; + if (oContainer == OBJECT_INVALID) + { + //------------------------------------------------------------------ + // We create the item in the work container to get rid of the + // stackable item problems that happen when we create the item + // directly on the player + //------------------------------------------------------------------ + if (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oPC)) + oNew = CreateItemOnObject(sResRef,IPGetIPWorkContainer(oPC),1,GetName(oPC)); + else + oNew = CreateItemOnObject(sResRef,IPGetIPWorkContainer(oPC)); + bFix = TRUE; + } + else + { + if (GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER, oPC)) + oNew = CreateItemOnObject(sResRef,oContainer,1,GetName(oPC)); + else + oNew = CreateItemOnObject(sResRef,oContainer); + } + + int nBase = GetBaseItemType(oNew); + if (nBase == BASE_ITEM_BOLT || nBase == BASE_ITEM_ARROW || nBase == BASE_ITEM_BULLET) + { + SetItemStackSize(oNew, 20); + } + else if (nBase == BASE_ITEM_THROWINGAXE || nBase == BASE_ITEM_SHURIKEN || nBase == BASE_ITEM_DART) + { + SetItemStackSize(oNew, 10); + } + + //---------------------------------------------------------------------- + // Get around the whole stackable item mess... + //---------------------------------------------------------------------- + if (bFix) + { + object oRet = CopyObject(oNew,GetLocation(oPC),oPC); + DestroyObject(oNew); + oNew = oRet; + } + } + else + { + oNew = OBJECT_INVALID; + } + + return oNew; +} + + +// ----------------------------------------------------------------------------- +// georg, 2003-06-13 ( +// Craft an item. This is only to be called from the crafting conversation +// spawned by x2_s2_crafting!!! +// ----------------------------------------------------------------------------- +int CIDoCraftItemFromConversation(int nNumber) +{ + string sNumber = IntToString(nNumber); + object oPC = GetPCSpeaker(); + //object oMaterial = GetLocalObject(oPC,"X2_CI_CRAFT_MATERIAL"); + object oMajor = GetLocalObject(oPC,"X2_CI_CRAFT_MAJOR"); + object oMinor = GetLocalObject(oPC,"X2_CI_CRAFT_MINOR"); + int nSkill = GetLocalInt(oPC,"X2_CI_CRAFT_SKILL"); + int nMode = GetLocalInt(oPC,"X2_CI_CRAFT_MODE"); + string sResult; + string s2DA; + int nDC; + + + DeleteLocalObject(oPC,"X2_CI_CRAFT_MAJOR"); + DeleteLocalObject(oPC,"X2_CI_CRAFT_MINOR"); + + if (!GetIsObjectValid(oMajor)) + { + FloatingTextStrRefOnCreature(83374,oPC); //"Invalid target" + DeleteLocalInt(oPC,"X2_CRAFT_SUCCESS"); + return FALSE; + } + else + { + if (GetItemPossessor(oMajor) != oPC) + { + FloatingTextStrRefOnCreature(83354,oPC); //"Invalid target" + DeleteLocalInt(oPC,"X2_CRAFT_SUCCESS"); + return FALSE; + } + } + + // If we are in container mode, + if (nMode == X2_CI_CRAFTMODE_CONTAINER) + { + if (!GetIsObjectValid(oMinor)) + { + FloatingTextStrRefOnCreature(83374,oPC); //"Invalid target" + DeleteLocalInt(oPC,"X2_CRAFT_SUCCESS"); + return FALSE; + } + else if (GetItemPossessor(oMinor) != oPC) + { + FloatingTextStrRefOnCreature(83354,oPC); //"Invalid target" + DeleteLocalInt(oPC,"X2_CRAFT_SUCCESS"); + return FALSE; + } + } + + + if (nSkill == 26) // craft weapon + { + s2DA = X2_CI_CRAFTING_WP_2DA; + } + else if (nSkill == 25) + { + s2DA = X2_CI_CRAFTING_AR_2DA; + } + + int nRow = GetLocalInt(oPC,"X2_CI_CRAFT_RESULTROW"); + struct craft_struct stItem = CIGetCraftItemStructFrom2DA(s2DA,nRow,nNumber); + object oContainer = OBJECT_INVALID; + + // --------------------------------------------------------------------------- + // We once used a crafting container, but found it too complicated. Code is still + // left in here for the community + // --------------------------------------------------------------------------- + if (nMode == X2_CI_CRAFTMODE_CONTAINER) + { + oContainer = GetItemPossessedBy(oPC,"x2_it_craftcont"); + } + + // Do the crafting... + object oRet = CIUseCraftItemSkill( oPC, nSkill, stItem.sResRef, stItem.nDC, oContainer) ; + + // * If you made an item, it should always be identified; + SetIdentified(oRet,TRUE); + + if (GetIsObjectValid(oRet)) + { + // ----------------------------------------------------------------------- + // Copy all item properties from the major object on the resulting item + // Through we problably won't use this, its a neat thing to have for the + // community + // to enable magic item creation from the crafting system + // ----------------------------------------------------------------------- + //if (GetGold(oPC) GetGoldPieceValue(oOldItem)) + { + nTotal = GetGoldPieceValue(oOldItem)+1; + } + return nTotal; +} + +// ----------------------------------------------------------------------------- +// returns the cost in gold piece that it would +// cost to modify oOlditem to look like oNewItem +// ----------------------------------------------------------------------------- +int CIGetArmorModificationDC(object oOldItem, object oNewItem) +{ + int nTotal = 0; + int nPart; + int nDC =0; + for (nPart = 0; nPartnTotal) + { + nTotal = nDC; + } + } + } + + nTotal = GetItemACValue(oOldItem) + nTotal + 5; + + return nTotal; +} + +// ----------------------------------------------------------------------------- +// returns TRUE if the spell matching nSpellID is prevented from being used +// with the CraftFeat matching nFeatID +// This is controlled in des_crft_spells.2da +// ----------------------------------------------------------------------------- +int CIGetIsSpellRestrictedFromCraftFeat(int nSpellID, int nFeatID) +{ + string sCol; + switch(nFeatID) + { + case X2_CI_BREWPOTION_FEAT_ID: sCol = "NoPotion"; break; + case X2_CI_SCRIBESCROLL_FEAT_ID: sCol = "NoScroll"; break; + case X2_CI_CRAFTWAND_FEAT_ID: + case X2_CI_CRAFTROD_FEAT_ID: + case X2_CI_CRAFTSTAFF_FEAT_ID: sCol = "NoWand"; break; + } + return !(!StringToInt(Get2DACache(X2_CI_CRAFTING_SP_2DA,sCol,nSpellID))); +} + +// ----------------------------------------------------------------------------- +// Retrieve the row in des_crft_bmat too look up receipe +// ----------------------------------------------------------------------------- +int CIGetCraftingReceipeRow(int nMode, object oMajor, object oMinor, int nSkill) +{ + if (nMode == X2_CI_CRAFTMODE_CONTAINER || nMode == X2_CI_CRAFTMODE_ASSEMBLE ) + { + int nMinorId = StringToInt(Get2DACache("des_crft_amat",GetTag(oMinor),1)); + int nMajorId = StringToInt(Get2DACache("des_crft_bmat",GetTag(oMajor),nMinorId)); + return nMajorId; + } + else if (nMode == X2_CI_CRAFTMODE_BASE_ITEM) + { + int nLookUpRow; + string sTag = GetTag(oMajor); + switch (nSkill) + { + case 26: nLookUpRow =1 ; break; + case 25: nLookUpRow= 2 ; break; + } + int nRet = StringToInt(Get2DACache(X2_CI_CRAFTING_MAT_2DA,sTag,nLookUpRow)); + return nRet; + } + else + { + return 0; // error + } +} + +// ----------------------------------------------------------------------------- +// used to set all variable required for the crafting conversation +// (Used materials, number of choises, 2da row, skill and mode) +// ----------------------------------------------------------------------------- +void CISetupCraftingConversation(object oPC, int nNumber, int nSkill, int nReceipe, object oMajor, object oMinor, int nMode) +{ + + SetLocalObject(oPC,"X2_CI_CRAFT_MAJOR",oMajor); + if (nMode == X2_CI_CRAFTMODE_CONTAINER || nMode == X2_CI_CRAFTMODE_ASSEMBLE ) + { + SetLocalObject(oPC,"X2_CI_CRAFT_MINOR", oMinor); + } + SetLocalInt(oPC,"X2_CI_CRAFT_NOOFITEMS",nNumber); // number of crafting choises for this material + SetLocalInt(oPC,"X2_CI_CRAFT_SKILL",nSkill); // skill used (craft armor or craft waeapon) + SetLocalInt(oPC,"X2_CI_CRAFT_RESULTROW",nReceipe); // number of crafting choises for this material + SetLocalInt(oPC,"X2_CI_CRAFT_MODE",nMode); +} + +// ----------------------------------------------------------------------------- +// oItem - The item used for crafting +// ----------------------------------------------------------------------------- +struct craft_receipe_struct CIGetCraftingModeFromTarget(object oPC,object oTarget, object oItem = OBJECT_INVALID) +{ + struct craft_receipe_struct stStruct; + + + if (GetBaseItemType(oItem) == 112 ) // small + { + stStruct.oMajor = oItem; + stStruct.nMode = X2_CI_CRAFTMODE_BASE_ITEM; + return stStruct; + } + + if (!GetIsObjectValid(oTarget)) + { + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + } + + + // A small craftitem was used on a large one + if (GetBaseItemType(oItem) == 110 ) // small + { + if (GetBaseItemType(oTarget) == 109) // large + { + stStruct.nMode = X2_CI_CRAFTMODE_ASSEMBLE; // Mode is ASSEMBLE + stStruct.oMajor = oTarget; + stStruct.oMinor = oItem; + return stStruct; + } + else + { + FloatingTextStrRefOnCreature(84201,oPC); + } + + } + + // ----------------------------------------------------------------------------- + // *** CONTAINER IS NO LONGER USED IN OFFICIAL CAMPAIGN + // BUT CODE LEFT IN FOR COMMUNITY. + // THE FOLLOWING CONDITION IS NEVER TRUE FOR THE OC (no crafting container) + // To reactivate, create a container with tag x2_it_craftcont + int bCraftCont = (GetTag(oTarget) == "x2_it_craftcont"); + + + if (bCraftCont == TRUE) + { + // First item in container is baseitem .. mode = baseitem + if ( GetBaseItemType(GetFirstItemInInventory(oTarget)) == 112) + { + stStruct.nMode = X2_CI_CRAFTMODE_BASE_ITEM; + stStruct.oMajor = GetFirstItemInInventory(oTarget); + return stStruct; + } + else + { + object oTest = GetFirstItemInInventory(oTarget); + int nCount =1; + int bMajor = FALSE; + int bMinor = FALSE; + // No item in inventory ... mode = fail + if (!GetIsObjectValid(oTest)) + { + FloatingTextStrRefOnCreature(84200,oPC); + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + } + else + { + while (GetIsObjectValid(oTest) && nCount <3) + { + if (GetBaseItemType(oTest) == 109) + { + stStruct.oMajor = oTest; + bMajor = TRUE; + } + else if (GetBaseItemType(oTest) == 110) + { + stStruct.oMinor = oTest; + bMinor = TRUE; + } + else if ( GetBaseItemType(oTest) == 112) + { + stStruct.nMode = X2_CI_CRAFTMODE_BASE_ITEM; + stStruct.oMajor = oTest; + return stStruct; + } + oTest = GetNextItemInInventory(oTarget); + if (GetIsObjectValid(oTest)) + { + nCount ++; + } + } + + if (nCount >2) + { + FloatingTextStrRefOnCreature(84356,oPC); + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + } + else if (nCount <2) + { + FloatingTextStrRefOnCreature(84356,oPC); + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + } + + if (bMajor && bMinor) + { + stStruct.nMode = X2_CI_CRAFTMODE_CONTAINER; + return stStruct; + } + else + { + FloatingTextStrRefOnCreature(84356,oPC); + //FloatingTextStringOnCreature("Temp: Wrong combination of items in the crafting container",oPC); + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + } + + } + } + } + else + { + // not a container but a baseitem + if (GetBaseItemType(oTarget) == 112) + { + stStruct.nMode = X2_CI_CRAFTMODE_BASE_ITEM; + stStruct.oMajor = oTarget; + return stStruct; + + } + else + { + if (GetBaseItemType(oTarget) == 109 || GetBaseItemType(oTarget) == 110) + { + FloatingTextStrRefOnCreature(84357,oPC); + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + } + else + { + FloatingTextStrRefOnCreature(84357,oPC); + // not a valid item + stStruct.nMode = X2_CI_CRAFTMODE_INVALID; + return stStruct; + + } + } + } +} + +// ----------------------------------------------------------------------------- +// *** Crafting Conversation Functions *** +// ----------------------------------------------------------------------------- +int CIGetInModWeaponOrArmorConv(object oPC) +{ + return GetLocalInt(oPC,"X2_L_CRAFT_MODIFY_CONVERSATION"); +} + + +void CISetCurrentModMode(object oPC, int nMode) +{ + if (nMode == X2_CI_MODMODE_INVALID) + { + DeleteLocalInt(oPC,"X2_L_CRAFT_MODIFY_MODE"); + } + else + { + SetLocalInt(oPC,"X2_L_CRAFT_MODIFY_MODE",nMode); + } +} + +int CIGetCurrentModMode(object oPC) +{ + return GetLocalInt(oPC,"X2_L_CRAFT_MODIFY_MODE"); +} + + +object CIGetCurrentModBackup(object oPC) +{ + return GetLocalObject(GetPCSpeaker(),"X2_O_CRAFT_MODIFY_BACKUP"); +} + +object CIGetCurrentModItem(object oPC) +{ + return GetLocalObject(GetPCSpeaker(),"X2_O_CRAFT_MODIFY_ITEM"); +} + + +void CISetCurrentModBackup(object oPC, object oBackup) +{ + SetLocalObject(GetPCSpeaker(),"X2_O_CRAFT_MODIFY_BACKUP",oBackup); +} + +void CISetCurrentModItem(object oPC, object oItem) +{ + SetLocalObject(GetPCSpeaker(),"X2_O_CRAFT_MODIFY_ITEM",oItem); +} + + +// ----------------------------------------------------------------------------- +// * This does multiple things: +// - store the part currently modified +// - setup the custom token for the conversation +// - zoom the camera to that part +// ----------------------------------------------------------------------------- +void CISetCurrentModPart(object oPC, int nPart, int nStrRef) +{ + SetLocalInt(oPC,"X2_TAILOR_CURRENT_PART",nPart); + + if (CIGetCurrentModMode(oPC) == X2_CI_MODMODE_ARMOR) + { + + // * Make the camera float near the PC + float fFacing = GetFacing(oPC) + 180.0; + + if (nPart == ITEM_APPR_ARMOR_MODEL_LSHOULDER || nPart == ITEM_APPR_ARMOR_MODEL_LFOREARM || + nPart == ITEM_APPR_ARMOR_MODEL_LHAND || nPart == ITEM_APPR_ARMOR_MODEL_LBICEP) + { + fFacing += 80.0; + } + + if (nPart == ITEM_APPR_ARMOR_MODEL_RSHOULDER || nPart == ITEM_APPR_ARMOR_MODEL_RFOREARM || + nPart == ITEM_APPR_ARMOR_MODEL_RHAND || nPart == ITEM_APPR_ARMOR_MODEL_RBICEP) + { + fFacing -= 80.0; + } + + float fPitch = 75.0; + if (fFacing > 359.0) + { + fFacing -=359.0; + } + + float fDistance = 3.5f; + if (nPart == ITEM_APPR_ARMOR_MODEL_PELVIS || nPart == ITEM_APPR_ARMOR_MODEL_BELT ) + { + fDistance = 2.0f; + } + + if (nPart == ITEM_APPR_ARMOR_MODEL_LSHOULDER || nPart == ITEM_APPR_ARMOR_MODEL_RSHOULDER ) + { + fPitch = 50.0f; + fDistance = 3.0f; + } + else if (nPart == ITEM_APPR_ARMOR_MODEL_LFOREARM || nPart == ITEM_APPR_ARMOR_MODEL_LHAND) + { + fDistance = 2.0f; + fPitch = 60.0f; + } + else if (nPart == ITEM_APPR_ARMOR_MODEL_NECK) + { + fPitch = 90.0f; + } + else if (nPart == ITEM_APPR_ARMOR_MODEL_RFOOT || nPart == ITEM_APPR_ARMOR_MODEL_LFOOT ) + { + fDistance = 3.5f; + fPitch = 47.0f; + } + else if (nPart == ITEM_APPR_ARMOR_MODEL_LTHIGH || nPart == ITEM_APPR_ARMOR_MODEL_RTHIGH ) + { + fDistance = 2.5f; + fPitch = 65.0f; + } + else if ( nPart == ITEM_APPR_ARMOR_MODEL_RSHIN || nPart == ITEM_APPR_ARMOR_MODEL_LSHIN ) + { + fDistance = 3.5f; + fPitch = 95.0f; + } + + if (GetRacialType(oPC) == RACIAL_TYPE_HALFORC) + { + fDistance += 1.0f; + } + + SetCameraFacing(fFacing, fDistance, fPitch,CAMERA_TRANSITION_TYPE_VERY_FAST) ; + } + + int nCost = GetLocalInt(oPC,"X2_TAILOR_CURRENT_COST"); + int nDC = GetLocalInt(oPC,"X2_TAILOR_CURRENT_DC"); + + SetCustomToken(X2_CI_MODIFYARMOR_GP_CTOKENBASE,IntToString(nCost)); + SetCustomToken(X2_CI_MODIFYARMOR_GP_CTOKENBASE+1,IntToString(nDC)); + + + SetCustomToken(XP_IP_ITEMMODCONVERSATION_CTOKENBASE,GetStringByStrRef(nStrRef)); +} + +int CIGetCurrentModPart(object oPC) +{ + return GetLocalInt(oPC,"X2_TAILOR_CURRENT_PART"); +} + + +void CISetDefaultModItemCamera(object oPC) +{ + float fDistance = 3.5f; + float fPitch = 75.0f; + float fFacing; + + if (CIGetCurrentModMode(oPC) == X2_CI_MODMODE_ARMOR) + { + fFacing = GetFacing(oPC) + 180.0; + if (fFacing > 359.0) + { + fFacing -=359.0; + } + } + else if (CIGetCurrentModMode(oPC) == X2_CI_MODMODE_WEAPON) + { + fFacing = GetFacing(oPC) + 180.0; + fFacing -= 90.0; + if (fFacing > 359.0) + { + fFacing -=359.0; + } + } + + SetCameraFacing(fFacing, fDistance, fPitch,CAMERA_TRANSITION_TYPE_VERY_FAST) ; +} + +void CIUpdateModItemCostDC(object oPC, int nDC, int nCost) +{ + SetLocalInt(oPC,"X2_TAILOR_CURRENT_COST", nCost); + SetLocalInt(oPC,"X2_TAILOR_CURRENT_DC",nDC); + SetCustomToken(X2_CI_MODIFYARMOR_GP_CTOKENBASE,IntToString(nCost)); + SetCustomToken(X2_CI_MODIFYARMOR_GP_CTOKENBASE+1,IntToString(nDC)); +} + + +// dc to modify oOlditem to look like oNewItem +int CIGetWeaponModificationCost(object oOldItem, object oNewItem) +{ + int nTotal = 0; + int nPart; + for (nPart = 0; nPart<=2; nPart++) + { + if (GetItemAppearance(oOldItem,ITEM_APPR_TYPE_WEAPON_MODEL, nPart) !=GetItemAppearance(oNewItem,ITEM_APPR_TYPE_WEAPON_MODEL, nPart)) + { + nTotal+= (GetGoldPieceValue(oOldItem)/4)+1; + } + } + + // Modification Cost should not exceed value of old item +1 GP + if (nTotal > GetGoldPieceValue(oOldItem)) + { + nTotal = GetGoldPieceValue(oOldItem)+1; + } + return nTotal; +} + +int GetMagicalArtisanFeat(int nCraftingFeat) +{ + int nReturn = 0; + switch(nCraftingFeat) + { + case FEAT_BREW_POTION: + { + nReturn = FEAT_MAGICAL_ARTISAN_BREW_POTION; + break; + } + case FEAT_SCRIBE_SCROLL: + { + nReturn = FEAT_MAGICAL_ARTISAN_SCRIBE_SCROLL; + break; + } + case FEAT_INSCRIBE_RUNE: + { + nReturn = FEAT_MAGICAL_ARTISAN_INSCRIBE_RUNE; + break; + } + case FEAT_ATTUNE_GEM: + { + nReturn = FEAT_MAGICAL_ARTISAN_ATTUNE_GEM; + break; + } + case FEAT_CRAFT_ARMS_ARMOR: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_MAGIC_ARMS; + break; + } + case FEAT_CRAFT_ROD: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_ROD; + break; + } + case FEAT_CRAFT_STAFF: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_STAFF; + break; + } + case FEAT_CRAFT_WAND: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_WAND; + break; + } + case FEAT_CRAFT_WONDROUS: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_WONDROUS; + break; + } + case FEAT_FORGE_RING: + { + nReturn = FEAT_MAGICAL_ARTISAN_FORGE_RING; + break; + } + case FEAT_CRAFT_SKULL_TALISMAN: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_SKULL_TALISMAN; + break; + } + default: + { + if(DEBUG) DoDebug("GetMagicalArtisanFeat: invalid crafting feat"); + break; + } + } + return nReturn; +} + +int GetPnPItemXPCost(int nCost, int bEpic) +{ + int nXP = nCost / 25; + if(bEpic) nXP = (nCost / 100) + 10000; + return nXP; +} + +int GetCraftingTime(int nCost) +{ + int nTemp = nCost / 1000; + if(nCost % 1000) nTemp++; + float fDelay; + switch(GetPRCSwitch(PRC_CRAFTING_TIME_SCALE)) + { + case 0: fDelay = HoursToSeconds(nTemp); break; //1 hour/1000gp, default + case 1: fDelay = 0.0; break; //off, no delay + case 2: fDelay = RoundsToSeconds(nTemp); break; //1 round/1000gp + case 3: fDelay = TurnsToSeconds(nTemp); break; //1 turn/1000gp + case 4: fDelay = HoursToSeconds(nTemp); break; //1 hour/1000gp + case 5: fDelay = 24 * HoursToSeconds(nTemp); break; //1 day/1000gp + } + int nMultiplyer = GetPRCSwitch(PRC_CRAFT_TIMER_MULTIPLIER); + if(nMultiplyer) + fDelay *= (IntToFloat(nMultiplyer) / 100.0); + return FloatToInt(fDelay / 6); +} + +int GetModifiedGoldCost(int nCost, object oPC, int nCraftingFeat) +{ + if(nCost == 0) + return nCost; + float fCost = IntToFloat(nCost); + if(GetHasFeat(FEAT_EXTRAORDINARY_ARTISAN_I , oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_EXTRAORDINARY_ARTISAN_II , oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_EXTRAORDINARY_ARTISAN_III , oPC)) fCost *= 0.75; + if(GetHasFeat(GetMagicalArtisanFeat(nCraftingFeat), oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_SHIELD_DWARF_WARDER, oPC) && FEAT_CRAFT_ARMS_ARMOR == nCraftingFeat) fCost *= 0.95; + int nScale = GetPRCSwitch(PRC_CRAFTING_COST_SCALE); + if(nScale > 0) + { //you're not getting away with negative values that easily :P + fCost = fCost * IntToFloat(nScale) / 100.0; + } + return FloatToInt(fCost); +} + +int GetModifiedXPCost(int nCost, object oPC, int nCraftingFeat) +{ + if(nCost == 0) + return nCost; + float fCost = IntToFloat(nCost); + if(GetHasFeat(FEAT_LEGENDARY_ARTISAN_I , oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_LEGENDARY_ARTISAN_II , oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_LEGENDARY_ARTISAN_III , oPC)) fCost *= 0.75; + if(GetHasFeat(GetMagicalArtisanFeat(nCraftingFeat), oPC)) fCost *= 0.75; + int nScale = GetPRCSwitch(PRC_CRAFTING_COST_SCALE); + if(nScale > 0) + { //you're not getting away with negative values that easily :P + fCost = fCost * IntToFloat(nScale) / 100.0; + } + return FloatToInt(fCost); +} + +int GetModifiedTimeCost(int nCost, object oPC, int nCraftingFeat) +{ + if(nCost == 0) + return nCost; + float fCost = IntToFloat(nCost); + if(GetLevelByClass(CLASS_TYPE_MAESTER, oPC)) fCost /= 2; + if(GetHasFeat(FEAT_EXCEPTIONAL_ARTISAN_I , oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_EXCEPTIONAL_ARTISAN_II , oPC)) fCost *= 0.75; + if(GetHasFeat(FEAT_EXCEPTIONAL_ARTISAN_III , oPC)) fCost *= 0.75; + if(GetHasFeat(GetMagicalArtisanFeat(nCraftingFeat), oPC)) fCost *= 0.75; + if(nCraftingFeat == FEAT_BREW_POTION) + { //master alchemist stuff here + if(GetHasFeat(FEAT_BREW_4PERDAY , oPC)) fCost /= 4; + else if(GetHasFeat(FEAT_BREW_3PERDAY , oPC)) fCost /= 3; + else if(GetHasFeat(FEAT_BREW_2PERDAY , oPC)) fCost /= 2; + } + int nScale = GetPRCSwitch(PRC_CRAFTING_COST_SCALE); + if(nScale > 0) + { //you're not getting away with negative values that easily :P + fCost = fCost * IntToFloat(nScale) / 100.0; + } + return FloatToInt(fCost); +} + +struct craft_cost_struct GetModifiedCostsFromBase(int nCost, object oPC, int nCraftingFeat, int bEpic) +{ + struct craft_cost_struct costs; + + costs.nGoldCost = GetModifiedGoldCost(nCost / 2, oPC, nCraftingFeat); + costs.nXPCost = GetModifiedXPCost(GetPnPItemXPCost(nCost, bEpic), oPC, nCraftingFeat); + costs.nTimeCost = GetModifiedTimeCost(GetCraftingTime(nCost), oPC, nCraftingFeat); + + return costs; +} + +//Checks crafting prereqs for warlocks +int CheckImbueItem(object oPC, int nSpell) +{ + if(!GetHasFeat(FEAT_IMBUE_ITEM, oPC)) return FALSE; + int nImbueDC; + int bArcane = TRUE; + int nLevel; + int nArcaneSpellLevel; + int nDivineSpellLevel; + string sTemp; + + sTemp = Get2DACache("spells", "Wiz_Sorc", nSpell); + if(sTemp == "") + { + sTemp = Get2DACache("spells", "Bard", nSpell); + if(sTemp == "") + { + bArcane = FALSE; //now checking the divine classes + sTemp = Get2DACache("spells", "Cleric", nSpell); + if(sTemp == "") + { + sTemp = Get2DACache("spells", "Druid", nSpell); + if(sTemp == "") + { + sTemp = Get2DACache("spells", "Paladin", nSpell); + if(sTemp == "") + { + sTemp = Get2DACache("spells", "Ranger", nSpell); + if(sTemp == "") + { + if(DEBUG) DoDebug("CheckImbueItem: ERROR - spell is neither arcane nor divine"); + return FALSE; + } + } + } + } + } + } + //warlocks with deceive item get to take 10 + return GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, StringToInt(sTemp) + (bArcane ? 15 : 25), GetHasFeat(FEAT_DECEIVE_ITEM, oPC) ? 10 : -1); +} + +// checks alternative crafting, eg. warlock, artificer +int CheckAlternativeCrafting(object oPC, int nSpell, struct craft_cost_struct costs) +{ + //nSpell1 = (PRCGetHasSpell(nTemp, oPC)) ? -1 : StringToInt(Get2DACache("spells", "Innate", nTemp)) + 20; + //if(nSpell1 == -1) nSpell1 = (GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, nSpell1, bTake10)) ? -1 : nSpell1; + + int bChecked = FALSE; + int bSuccess = FALSE; + int i; + int bTake10 = GetHasFeat(FEAT_SKILL_MASTERY_ARTIFICER, oPC) ? 10 : -1; + + //artificer crafting check + if(!bSuccess && GetLocalInt(oPC, "ArtificerCrafting")) + { + bChecked = TRUE; + //bSuccess = CheckImbueItem(oPC, nSpell); + for(i = 0; i < costs.nTimeCost; i++) + { + bSuccess = GetPRCIsSkillSuccessful(oPC, SKILL_USE_MAGIC_DEVICE, StringToInt(Get2DACache("spells", "Innate", nSpell)) + 20, bTake10); + if(bSuccess) + break; + } + } + //warlock crafting check + if(!bSuccess && GetLocalInt(oPC, "UsingImbueItem")) + { + bChecked = TRUE; + bSuccess = CheckImbueItem(oPC, nSpell); + } + + + if(!bChecked) + return TRUE; //we never checked because we had the actual spell, so successful + else + return bSuccess; +} + +int GetAlternativeCasterLevel(object oPC, int nLevel) +{ + // Battlesmith adds 3x class level to caster level for casting + nLevel += GetLevelByClass(CLASS_TYPE_BATTLESMITH) * 3; + nLevel += GetLevelByClass(CLASS_TYPE_IRONSOUL_FORGEMASTER) * 3; + if(GetLocalInt(oPC, "UsingImbueItem")) + { + nLevel = PRCMax(GetLocalInt(oPC, "InvokerLevel"), nLevel); + } + if(GetLocalInt(oPC, "ArtificerCrafting")) + { + nLevel = PRCMax(GetLevelByClass(CLASS_TYPE_ARTIFICER, oPC), nLevel); + } + return nLevel; +} + + +// Test main +//void main(){} diff --git a/src/include/prc_x2_itemprop.nss b/src/include/prc_x2_itemprop.nss new file mode 100644 index 0000000..e4a1be0 --- /dev/null +++ b/src/include/prc_x2_itemprop.nss @@ -0,0 +1,2022 @@ +//:://///////////////////////////////////////////// +//:://///////////////////////////////////////////// +//:: Item Property Functions +//:: prc_x2_itemprop +//:: Copyright (c) 2003 Bioware Corp. +//::////////////////////////////////////////////// +/* + + Holds item property and item modification + specific code. + + If you look for anything specific to item + properties, your chances are good to find it + in here. + +*/ +//::////////////////////////////////////////////// +//:: Created By: Georg Zoeller +//:: Created On: 2003-06-05 +//:: Last Update: 2003-10-07 +//::////////////////////////////////////////////// + +//:: Test void +//:: void main (){} + +//Changed by primogenitor to include CEP itemtypes + +// * The tag of the ip work container, a placeable which has to be set into each +// * module that is using any of the crafting functions. +const string X2_IP_WORK_CONTAINER_TAG = "x2_plc_ipbox"; +// * 2da for the AddProperty ItemProperty +const string X2_IP_ADDRPOP_2DA = "des_crft_props" ; +// * 2da for the Poison Weapon Itemproperty +const string X2_IP_POISONWEAPON_2DA = "des_crft_poison" ; +// * 2da for armor appearance +const string X2_IP_ARMORPARTS_2DA = "des_crft_aparts" ; +// * 2da for armor appearance +const string X2_IP_ARMORAPPEARANCE_2DA = "des_crft_appear" ; + +// * Base custom token for item modification conversations (do not change unless you want to change the conversation too) +const int XP_IP_ITEMMODCONVERSATION_CTOKENBASE = 12220; +const int X2_IP_ITEMMODCONVERSATION_MODE_TAILOR = 0; +const int X2_IP_ITEMMODCONVERSATION_MODE_CRAFT = 1; + +// * Number of maximum item properties allowed on most items +const int X2_IP_MAX_ITEM_PROPERTIES = 8; + +// * Constants used with the armor modification system +const int X2_IP_ARMORTYPE_NEXT = 0; +const int X2_IP_ARMORTYPE_PREV = 1; +const int X2_IP_ARMORTYPE_RANDOM = 2; +const int X2_IP_WEAPONTYPE_NEXT = 0; +const int X2_IP_WEAPONTYPE_PREV = 1; +const int X2_IP_WEAPONTYPE_RANDOM = 2; + +// * Policy constants for IPSafeAddItemProperty() +const int X2_IP_ADDPROP_POLICY_REPLACE_EXISTING = 0; +const int X2_IP_ADDPROP_POLICY_KEEP_EXISTING = 1; +const int X2_IP_ADDPROP_POLICY_IGNORE_EXISTING =2; + + +// * removes all itemproperties with matching nItemPropertyType and nItemPropertyDuration +void IPRemoveMatchingItemProperties( object oItem, int nItemPropertyType, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY, int nItemPropertySubType = -1 ); + +// * Removes ALL item properties from oItem matching nItemPropertyDuration +void IPRemoveAllItemProperties( object oItem, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY ); + +// * returns TRUE if item can be equipped. +// * Uses Get2DAString, so do not use in a loop! +int IPGetIsItemEquipable( object oItem ); + +// * Changes the color of an item armor +// * oItem - The armor +// * nColorType - ITEM_APPR_ARMOR_COLOR_* constant +// * nColor - color from 0 to 63 +// * Since oItem is destroyed in the process, the function returns +// * the item created with the color changed +object IPDyeArmor( object oItem, int nColorType, int nColor ); + + +// * Returns the container used for item property and appearance modifications in the +// * module. If it does not exist, create it. +object IPGetIPWorkContainer( object oCaller = OBJECT_SELF ); + +// * This function needs to be rather extensive and needs to be updated if there are new +// * ip types we want to use, but it goes into the item property include anyway +itemproperty IPGetItemPropertyByID( int nPropID, int nParam1 = 0, int nParam2 = 0, int nParam3 = 0, int nParam4 = 0 ); + +// * returns TRUE if oItem is a ranged weapon +int IPGetIsRangedWeapon( object oItem ); + +// * return TRUE if oItem is a melee weapon +int IPGetIsMeleeWeapon( object oItem ); + +// * return TRUE if oItem is a projectile (bolt, arrow, etc) +int IPGetIsProjectile( object oItem ); + +// * returns true if weapon is blugeoning (used for poison) +// * This uses Get2DAstring, so it is slow. Avoid using in loops! +int IPGetIsBludgeoningWeapon( object oItem ); + +// * Return the IP_CONST_CASTSPELL_* ID matching to the SPELL_* constant given in nSPELL_ID +// * This uses Get2DAstring, so it is slow. Avoid using in loops! +// * returns -1 if there is no matching property for a spell +int IPGetIPConstCastSpellFromSpellID( int nSpellID ); + +// * Returns TRUE if an item has ITEM_PROPERTY_ON_HIT and the specified nSubType +// * possible values for nSubType can be taken from IPRP_ONHIT.2da +// * popular ones: +// * 5 - Daze 19 - ItemPoison 24 - Vorpal +int IPGetItemHasItemOnHitPropertySubType( object oTarget, int nSubType ); + +// * Returns the number of possible armor part variations for the specified part +// * nPart - ITEM_APPR_ARMOR_MODEL_* constant +// * Uses Get2DAstring, so do not use in loops +int IPGetNumberOfAppearances( int nPart ); + + +// * Returns the next valid appearance type for oArmor +// * nPart - ITEM_APPR_ARMOR_MODEL_* constant +// * Uses Get2DAstring, so do not use in loops +int IPGetNextArmorAppearanceType(object oArmor, int nPart); + +// * Returns the previous valid appearance type for oArmor +// * nPart - ITEM_APPR_ARMOR_MODEL_* constant +// * Uses Get2DAstring, so do not use in loops +int IPGetPrevArmorAppearanceType(object oArmor, int nPart); + +// * Returns a random valid appearance type for oArmor +// * nPart - ITEM_APPR_ARMOR_MODEL_* constant +// * Uses Get2DAstring, so do not use in loops +int IPGetRandomArmorAppearanceType(object oArmor, int nPart); + +// * Returns a new armor based of oArmor with nPartModified +// * nPart - ITEM_APPR_ARMOR_MODEL_* constant of the part to be changed +// * nMode - +// * X2_IP_ARMORTYPE_NEXT - next valid appearance +// * X2_IP_ARMORTYPE_PREV - previous valid apperance; +// * X2_IP_ARMORTYPE_RANDOM - random valid appearance; +// * +// * bDestroyOldOnSuccess - Destroy oArmor in process? +// * Uses Get2DAstring, so do not use in loops +object IPGetModifiedArmor(object oArmor, int nPart, int nMode, int bDestroyOldOnSuccess); + + +// * Add an item property in a safe fashion, preventing unwanted stacking +// * Parameters: +// * oItem - the item to add the property to +// * ip - the itemproperty to add +// * fDuration - set 0.0f to add the property permanent, anything else is temporary +// * nAddItemPropertyPolicy - How to handle existing properties. Valid values are: +// * X2_IP_ADDPROP_POLICY_REPLACE_EXISTING - remove any property of the same type, subtype, durationtype before adding; +// * X2_IP_ADDPROP_POLICY_KEEP_EXISTING - do not add if any property with same type, subtype and durationtype already exists; +// * X2_IP_ADDPROP_POLICY_IGNORE_EXISTING - add itemproperty in any case - Do not Use with OnHit or OnHitSpellCast props! +// * +// * bIgnoreDurationType - If set to TRUE, an item property will be considered identical even if the DurationType is different. Be careful when using this +// * with X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, as this could lead to a temporary item property removing a permanent one +// * bIgnoreSubType - If set to TRUE an item property will be considered identical even if the SubType is different. +void IPSafeAddItemProperty(object oItem, itemproperty ip, float fDuration =0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE); + +// * Wrapper for GetItemHasItemProperty that returns true if +// * oItem has an itemproperty that matches ipCompareTo by Type AND DurationType AND SubType +// * nDurationType = Valid DURATION_TYPE_* or -1 to ignore +// * bIgnoreSubType - If set to TRUE an item property will be considered identical even if the SubType is different. +int IPGetItemHasProperty(object oItem, itemproperty ipCompareTo, int nDurationType, int bIgnoreSubType = FALSE); + +// * returns FALSE it the item has no sequencer property +// * returns number of spells that can be stored in any other case +int IPGetItemSequencerProperty(object oItem, object oPC = OBJECT_SELF); + +// * returns TRUE if the item has the OnHit:IntelligentWeapon property. +int IPGetIsIntelligentWeapon(object oItem); + +// * Mapping between numbers and power constants for ITEM_PROPERTY_DAMAGE_BONUS +// * returns the appropriate ITEM_PROPERTY_DAMAGE_POWER_* constant for nNumber +int IPGetDamagePowerConstantFromNumber(int nNumber); + +// * returns the appropriate ITEM_PROPERTY_DAMAGE_BONUS_= constant for nNumber +// * Do not pass in any number <1 ! Will return -1 on error +int IPGetDamageBonusConstantFromNumber(int nNumber); + +// * Special Version of Copy Item Properties for use with greater wild shape +// * oOld - Item equipped before polymorphing (source for item props) +// * oNew - Item equipped after polymorphing (target for item props) +// * bWeapon - Must be set TRUE when oOld is a weapon. +void IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE); + +// * Returns the current enhancement bonus of a weapon (+1 to +20), 0 if there is +// * no enhancement bonus. You can test for a specific type of enhancement bonus +// * by passing the appropritate ITEM_PROPERTY_ENHANCEMENT_BONUS* constant into +// * nEnhancementBonusType +int IPGetWeaponEnhancementBonus(object oWeapon, int nEnhancementBonusType = ITEM_PROPERTY_ENHANCEMENT_BONUS, int bIgnoreTemporary = TRUE); + +// * Shortcut function to set the enhancement bonus of a weapon to a certain bonus +// * Specifying bOnlyIfHigher as TRUE will prevent a bonus lower than the requested +// * bonus from being applied. Valid values for nBonus are 1 to 20. +void IPSetWeaponEnhancementBonus(object oWeapon, int nBonus, int bOnlyIfHigher = TRUE); + +// * Shortcut function to upgrade the enhancement bonus of a weapon by the +// * number specified in nUpgradeBy. If the resulting new enhancement bonus +// * would be out of bounds (>+20), it will be set to +20 +void IPUpgradeWeaponEnhancementBonus(object oWeapon, int nUpgradeBy); + +// * Returns TRUE if a character has any item equipped that has the itemproperty +// * defined in nItemPropertyConst in it (ITEM_PROPERTY_* constant) +int IPGetHasItemPropertyOnCharacter(object oPC, int nItemPropertyConst); + +// * Returns an integer with the number of properties present oItem +int IPGetNumberOfItemProperties(object oItem); + +// returns the IP damage constant for a given number +int IPDamageConstant(int nDamBon); + +#include "prc_ipfeat_const" +#include "inc_utility" + +//------------------------------------------------------------------------------ +// I M P L E M E N T A T I O N +//------------------------------------------------------------------------------ + +// ---------------------------------------------------------------------------- +// Removes all itemproperties with matching nItemPropertyType and +// nItemPropertyDuration (a DURATION_TYPE_* constant) +// ---------------------------------------------------------------------------- +void IPRemoveMatchingItemProperties(object oItem, int nItemPropertyType, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY, int nItemPropertySubType = -1) +{ + itemproperty ip = GetFirstItemProperty(oItem); + + // valid ip? + while (GetIsItemPropertyValid(ip)) + { + // same property type? + if ((GetItemPropertyType(ip) == nItemPropertyType)) + { + // same duration or duration ignored? + if (GetItemPropertyDurationType(ip) == nItemPropertyDuration || nItemPropertyDuration == -1) + { + // same subtype or subtype ignored + if (GetItemPropertySubType(ip) == nItemPropertySubType || nItemPropertySubType == -1) + { + // Put a warning into the logfile if someone tries to remove a permanent ip with a temporary one! + /*if (nItemPropertyDuration == DURATION_TYPE_TEMPORARY && GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { + WriteTimestampedLogEntry("prc_x2_itemprop:: IPRemoveMatchingItemProperties() - WARNING: Permanent item property removed by temporary on "+GetTag(oItem)); + } + */ + RemoveItemProperty(oItem, ip); + } + } + } + ip = GetNextItemProperty(oItem); + } +} + +// ---------------------------------------------------------------------------- +// Removes ALL item properties from oItem matching nItemPropertyDuration +// ---------------------------------------------------------------------------- +void IPRemoveAllItemProperties(object oItem, int nItemPropertyDuration = DURATION_TYPE_TEMPORARY) +{ + itemproperty ip = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyDurationType(ip) == nItemPropertyDuration) + { + RemoveItemProperty(oItem, ip); + } + ip = GetNextItemProperty(oItem); + } +} + +// ---------------------------------------------------------------------------- +// returns TRUE if item can be equipped. Uses Get2DAString, so do not use in a loop! +// ---------------------------------------------------------------------------- +int IPGetIsItemEquipable(object oItem) +{ + int nBaseType =GetBaseItemType(oItem); + + // fix, if we get BASE_ITEM_INVALID (usually because oItem is invalid), we + // need to make sure that this function returns FALSE + if(nBaseType==BASE_ITEM_INVALID) return FALSE; + + string sResult = Get2DACache("baseitems","EquipableSlots",nBaseType); + return (sResult != "0x00000"); +} + +// ---------------------------------------------------------------------------- +// Changes the color of an item armor +// oItem - The armor +// nColorType - ITEM_APPR_ARMOR_COLOR_* constant +// nColor - color from 0 to 63 +// Since oItem is destroyed in the process, the function returns +// the item created with the color changed +// ---------------------------------------------------------------------------- +object IPDyeArmor(object oItem, int nColorType, int nColor) +{ + object oRet = CopyItemAndModify(oItem,ITEM_APPR_TYPE_ARMOR_COLOR,nColorType,nColor,TRUE); + DestroyObject(oItem); // remove old item + return oRet; //return new item +} + +// ---------------------------------------------------------------------------- +// Returns the container used for item property and appearance modifications in the +// module. If it does not exist, it is created +// ---------------------------------------------------------------------------- +object IPGetIPWorkContainer(object oCaller = OBJECT_SELF) +{ + object oRet = GetObjectByTag(X2_IP_WORK_CONTAINER_TAG); + if (oRet == OBJECT_INVALID) + { + oRet = CreateObject(OBJECT_TYPE_PLACEABLE,X2_IP_WORK_CONTAINER_TAG,GetStartingLocation()); + effect eInvis = EffectVisualEffect( VFX_DUR_CUTSCENE_INVISIBILITY); + eInvis = ExtraordinaryEffect(eInvis); + ApplyEffectToObject(DURATION_TYPE_PERMANENT,eInvis,oRet); + if (oRet == OBJECT_INVALID) + { + WriteTimestampedLogEntry("prc_x2_itemprop - critical: Missing container with tag " +X2_IP_WORK_CONTAINER_TAG + "!!"); + } + } + + + return oRet; +} + +// ---------------------------------------------------------------------------- +// This function needs to be rather extensive and needs to be updated if there are new +// ip types we want to use, but it goes into the item property include anyway +// ---------------------------------------------------------------------------- +//This version has been fixed up by Sunjammer from the bioware forums +//http://nwn.bioware.com/forums/viewtopic.html?topic=431299&forum=47 +itemproperty IPGetItemPropertyByID(int nPropID, int nParam1=0, int nParam2=0, int nParam3=0, int nParam4=0) +{ + itemproperty ipRet; + if (nPropID == ITEM_PROPERTY_ABILITY_BONUS) + { + ipRet = ItemPropertyAbilityBonus(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_AC_BONUS) + { + ipRet = ItemPropertyACBonus(nParam1); + } + else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP) + { + ipRet = ItemPropertyACBonusVsAlign(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE) + { + ipRet = ItemPropertyACBonusVsDmgType(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP) + { + ipRet = ItemPropertyACBonusVsRace(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT) + { + ipRet = ItemPropertyACBonusVsSAlign(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS) + { + ipRet = ItemPropertyAttackBonus(nParam1); + } + else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP) + { + ipRet = ItemPropertyAttackBonusVsAlign(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP) + { + ipRet = ItemPropertyAttackBonusVsRace(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT) + { + ipRet = ItemPropertyAttackBonusVsSAlign(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION) + { + ipRet = ItemPropertyWeightReduction(nParam1); + } + else if (nPropID == ITEM_PROPERTY_BONUS_FEAT) + { + ipRet = PRCItemPropertyBonusFeat(nParam1); + } + else if (nPropID == ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N) + { + ipRet = ItemPropertyBonusLevelSpell(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_CAST_SPELL) + { + ipRet = ItemPropertyCastSpell(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_BONUS) + { + ipRet = ItemPropertyDamageBonus(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP) + { + ipRet = ItemPropertyDamageBonusVsAlign(nParam1, nParam2, nParam3); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP) + { + ipRet = ItemPropertyDamageBonusVsRace(nParam1, nParam2, nParam3); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT) + { + ipRet = ItemPropertyDamageBonusVsSAlign(nParam1, nParam2, nParam3); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_REDUCTION) + { + ipRet = ItemPropertyDamageReduction(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_RESISTANCE) + { + ipRet = ItemPropertyDamageResistance(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DAMAGE_VULNERABILITY) + { + ipRet = ItemPropertyDamageResistance(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DARKVISION) + { + ipRet = ItemPropertyDarkvision(); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_ABILITY_SCORE) + { + ipRet = ItemPropertyDecreaseAbility(nParam1,nParam2); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_AC) + { + ipRet = ItemPropertyDecreaseAC(nParam1,nParam2); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER) + { + ipRet = ItemPropertyAttackPenalty(nParam1); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER) + { + ipRet = ItemPropertyEnhancementPenalty(nParam1); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_SAVING_THROWS) + { + ipRet = ItemPropertyReducedSavingThrow(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC) + { + ipRet = ItemPropertyBonusSavingThrowVsX(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_DECREASED_SKILL_MODIFIER) + { + ipRet = ItemPropertyDecreaseSkill(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_ENHANCED_CONTAINER_REDUCED_WEIGHT) + { + ipRet = ItemPropertyContainerReducedWeight(nParam1); + } + else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS) + { + ipRet = ItemPropertyEnhancementBonus(nParam1); + } + else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP) + { + ipRet = ItemPropertyEnhancementBonusVsAlign(nParam1,nParam2); + } + else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT) + { + ipRet = ItemPropertyEnhancementBonusVsSAlign(nParam1,nParam2); + } + else if (nPropID == ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP) + { + ipRet = ItemPropertyEnhancementBonusVsRace(nParam1,nParam2); + } + else if (nPropID == ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE) + { + ipRet = ItemPropertyExtraMeleeDamageType(nParam1); + } + else if (nPropID == ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE) + { + ipRet = ItemPropertyExtraRangeDamageType(nParam1); + } + else if (nPropID == ITEM_PROPERTY_HASTE) + { + ipRet = ItemPropertyHaste(); + } + else if (nPropID == ITEM_PROPERTY_KEEN) + { + ipRet = ItemPropertyKeen(); + } + else if (nPropID == ITEM_PROPERTY_LIGHT) + { + ipRet = ItemPropertyLight(nParam1,nParam2); + } + else if (nPropID == ITEM_PROPERTY_MASSIVE_CRITICALS) + { + ipRet = ItemPropertyMassiveCritical(nParam1); + } + else if (nPropID == ITEM_PROPERTY_NO_DAMAGE) + { + ipRet = ItemPropertyNoDamage(); + } + else if (nPropID == ITEM_PROPERTY_ON_HIT_PROPERTIES) + { + ipRet = ItemPropertyOnHitProps(nParam1, nParam2, nParam3); + } + else if (nPropID == ITEM_PROPERTY_TRAP) + { + ipRet = ItemPropertyTrap(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_TRUE_SEEING) + { + ipRet = ItemPropertyTrueSeeing(); + } + else if (nPropID == ITEM_PROPERTY_UNLIMITED_AMMUNITION) + { + ipRet = ItemPropertyUnlimitedAmmo(nParam1); + } + // SJ -----------------------------------------------------------------start- + else if (nPropID == ITEM_PROPERTY_ONHITCASTSPELL) + { + // this constructor is bugged (@ v1.65) and will reduce nParam2 by 1 + // we can compensate for this until it is fixed by adding 1 here + // however someone (you) will have to remember to remove it later! + ipRet = ItemPropertyOnHitCastSpell(nParam1, nParam2 + 1); + //Primogenitor + //Not strictly true. You put in the level of the spell, its just that + //this doesnt match the row of the 2da directly. Thus you only need + //to remember this when getting the value later on. + ipRet = ItemPropertyOnHitCastSpell(nParam1, nParam2); + } + // SJ -------------------------------------------------------------------end- + else if (nPropID == ITEM_PROPERTY_ARCANE_SPELL_FAILURE) + { + ipRet = ItemPropertyArcaneSpellFailure(nParam1); + } + // SJ -----------------------------------------------------------------start- + else if (nPropID == ITEM_PROPERTY_DECREASED_DAMAGE) + { + ipRet = ItemPropertyDamagePenalty(nParam1); + } + else if (nPropID == ITEM_PROPERTY_FREEDOM_OF_MOVEMENT) + { + ipRet = ItemPropertyFreeAction(); + } + else if (nPropID == ITEM_PROPERTY_HEALERS_KIT) + { + ipRet = ItemPropertyHealersKit(nParam1); + } + else if (nPropID == ITEM_PROPERTY_HOLY_AVENGER) + { + ipRet = ItemPropertyHolyAvenger(); + } + else if (nPropID == ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE) + { + ipRet = ItemPropertyDamageImmunity(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS) + { + ipRet = ItemPropertyImmunityMisc(nParam1); + } + else if (nPropID == ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL) + { + ipRet = ItemPropertySpellImmunitySpecific(nParam1); + } + else if (nPropID == ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL) + { + ipRet = ItemPropertySpellImmunitySchool(nParam1); + } + else if (nPropID == ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL) + { + // this constructor is bugged (@ v1.65) and will reduce nParam1 by 1 + // we can compensate for this until it is fixed by adding 1 here + // however someone (you) will have to remember to remove it later! + //Primogenitor + //Fixed as of 1.67 + ipRet = ItemPropertyImmunityToSpellLevel(nParam1); + } + else if (nPropID == ITEM_PROPERTY_IMPROVED_EVASION) + { + ipRet = ItemPropertyImprovedEvasion(); + } + else if (nPropID == ITEM_PROPERTY_MIGHTY) + { + ipRet = ItemPropertyMaxRangeStrengthMod(nParam1); + } + else if (nPropID == ITEM_PROPERTY_MONSTER_DAMAGE) + { + ipRet = ItemPropertyMonsterDamage(nParam1); + } + else if (nPropID == ITEM_PROPERTY_ON_MONSTER_HIT) + { + ipRet = ItemPropertyOnMonsterHitProperties(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_REGENERATION) + { + ipRet = ItemPropertyRegeneration(nParam1); + } + else if (nPropID == ITEM_PROPERTY_REGENERATION_VAMPIRIC) + { + ipRet = ItemPropertyVampiricRegeneration(nParam1); + } + else if (nPropID == ITEM_PROPERTY_SAVING_THROW_BONUS) + { + ipRet = ItemPropertyBonusSavingThrowVsX(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC) + { + ipRet = ItemPropertyBonusSavingThrow(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_SKILL_BONUS) + { + ipRet = ItemPropertySkillBonus(nParam1, nParam2); + } + else if (nPropID == ITEM_PROPERTY_SPECIAL_WALK) + { + ipRet = ItemPropertySpecialWalk(nParam1); + } + else if (nPropID == ITEM_PROPERTY_SPELL_RESISTANCE) + { + ipRet = ItemPropertyBonusSpellResistance(nParam1); + } + else if (nPropID == ITEM_PROPERTY_THIEVES_TOOLS) + { + ipRet = ItemPropertyThievesTools(nParam1); + } + else if (nPropID == ITEM_PROPERTY_TURN_RESISTANCE) + { + ipRet = ItemPropertyTurnResistance(nParam1); + } + else if (nPropID == ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP) + { + ipRet = ItemPropertyLimitUseByAlign(nParam1); + } + else if (nPropID == ITEM_PROPERTY_USE_LIMITATION_CLASS) + { + ipRet = ItemPropertyLimitUseByClass(nParam1); + } + else if (nPropID == ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE) + { + ipRet = ItemPropertyLimitUseByRace(nParam1); + } + else if (nPropID == ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT) + { + ipRet = ItemPropertyLimitUseBySAlign(nParam1); + } + else if (nPropID == ITEM_PROPERTY_VISUALEFFECT) + { + ipRet = ItemPropertyVisualEffect(nParam1); + } + else if (nPropID == ITEM_PROPERTY_WEIGHT_INCREASE) + { + ipRet = ItemPropertyWeightIncrease(nParam1); + } + // SJ -------------------------------------------------------------------end- + return ipRet; +} + + +// ---------------------------------------------------------------------------- +// Returns TRUE if oItem is a projectile +// ---------------------------------------------------------------------------- +int IPGetIsProjectile(object oItem) +{ + int nBT = GetBaseItemType(oItem); + return nBT == BASE_ITEM_ARROW + || nBT == BASE_ITEM_BOLT + || nBT == BASE_ITEM_BULLET; +} + +// ---------------------------------------------------------------------------- +// Returns TRUE if oItem is a ranged weapon +// ---------------------------------------------------------------------------- +int IPGetIsRangedWeapon(object oItem) +{ + return GetWeaponRanged(oItem); // doh ! +} + +// ---------------------------------------------------------------------------- +// Returns TRUE if oItem is a melee weapon +// ---------------------------------------------------------------------------- +int IPGetIsMeleeWeapon(object oItem) +{ +//wepon type/size && !ranged + + + //Declare major variables + int nItem = GetBaseItemType(oItem); + +if(nItem == BASE_ITEM_BASTARDSWORD + || nItem == BASE_ITEM_BATTLEAXE + || nItem == BASE_ITEM_DOUBLEAXE + || nItem == BASE_ITEM_GREATAXE + || nItem == BASE_ITEM_GREATSWORD + || nItem == BASE_ITEM_HALBERD + || nItem == BASE_ITEM_HANDAXE + || nItem == BASE_ITEM_KAMA + || nItem == BASE_ITEM_KATANA + || nItem == BASE_ITEM_KUKRI + || nItem == BASE_ITEM_LONGSWORD + || nItem == BASE_ITEM_SCIMITAR + || nItem == BASE_ITEM_SCYTHE + || nItem == BASE_ITEM_SICKLE + || nItem == BASE_ITEM_TWOBLADEDSWORD + || nItem == BASE_ITEM_CLUB + || nItem == BASE_ITEM_DAGGER + || nItem == BASE_ITEM_DIREMACE + || nItem == BASE_ITEM_HEAVYFLAIL + || nItem == BASE_ITEM_LIGHTFLAIL + || nItem == BASE_ITEM_LIGHTHAMMER + || nItem == BASE_ITEM_LIGHTMACE + || nItem == BASE_ITEM_MORNINGSTAR + || nItem == BASE_ITEM_QUARTERSTAFF + || nItem == BASE_ITEM_MAGICSTAFF + || nItem == BASE_ITEM_RAPIER + || nItem == BASE_ITEM_WHIP + || nItem == BASE_ITEM_SHORTSPEAR + || nItem == BASE_ITEM_SHORTSWORD + || nItem == BASE_ITEM_WARHAMMER + || nItem == BASE_ITEM_DWARVENWARAXE + || nItem == BASE_ITEM_TRIDENT + || nItem == BASE_ITEM_HEAVY_PICK + || nItem == BASE_ITEM_LIGHT_PICK + || nItem == BASE_ITEM_SAI + || nItem == BASE_ITEM_NUNCHAKU + || nItem == BASE_ITEM_FALCHION + || nItem == BASE_ITEM_SAP + || nItem == BASE_ITEM_KATAR + || nItem == BASE_ITEM_HEAVY_MACE + || nItem == BASE_ITEM_MAUL + || nItem == BASE_ITEM_DOUBLE_SCIMITAR + || nItem == BASE_ITEM_GOAD + || nItem == BASE_ITEM_EAGLE_CLAW + || nItem == BASE_ITEM_ELVEN_LIGHTBLADE // PRC weapons + || nItem == BASE_ITEM_ELVEN_THINBLADE + || nItem == BASE_ITEM_ELVEN_COURTBLADE + || nItem == BASE_ITEM_CRAFTED_STAFF + || nItem == 300 //CEP Trident + || nItem == 303 //CEP Sai + || nItem == 304 //CEP nunchaku + || nItem == 305 //CEP falchion + || nItem == 309 //CEP assassin dager + || nItem == 310 //CEP katar + || nItem == 312 //CEP light mace 2 + || nItem == 313 //CEP kukri2 + || nItem == 316 //CEP falchion + || nItem == 317 //CEP heavymace + || nItem == 318 //CEP maul + || nItem == 319 //CEP sh_x1_mercuryls + || nItem == 320 //CEP sh_x1_mercurygs + || nItem == 321 //CEP sh_x1_doublesc + || nItem == 322 //CEP goad + || nItem == 323 //CEP windfirewheel + || nItem == 324 //CEP maugdoublesword + ) + { + return TRUE; + } + return FALSE; +} + +// ---------------------------------------------------------------------------- +// Returns TRUE if weapon is a blugeoning weapon +// Uses Get2DAString! +// ---------------------------------------------------------------------------- +int IPGetIsBludgeoningWeapon(object oItem) +{ + int nBT = GetBaseItemType(oItem); + int nWeapon = ( StringToInt(Get2DACache("baseitems","WeaponType",nBT))); + // 2 = bludgeoning + return (nWeapon == 2); +} + +// ---------------------------------------------------------------------------- +// Return the IP_CONST_CASTSPELL_* ID matching to the SPELL_* constant given +// in nSPELL_ID. +// This uses Get2DAstring, so it is slow. Avoid using in loops! +// returns -1 if there is no matching property for a spell +// ---------------------------------------------------------------------------- +int IPGetIPConstCastSpellFromSpellID(int nSpellID) +{ + // look up Spell Property Index + string sTemp = Get2DACache("des_crft_spells","IPRP_SpellIndex",nSpellID); + + if (sTemp == "") // invalid nSpellID + { + if (DEBUG) DoDebug("prc_x2_craft.nss::GetIPConstCastSpellFromSpellID called with invalid nSpellID" + IntToString(nSpellID)); + return -1; + } + + int nSpellPrpIdx = StringToInt(sTemp); + return nSpellPrpIdx; +} +// ---------------------------------------------------------------------------- +// Returns TRUE if an item has ITEM_PROPERTY_ON_HIT and the specified nSubType +// possible values for nSubType can be taken from IPRP_ONHIT.2da +// popular ones: +// 5 - Daze +// 19 - ItemPoison +// 24 - Vorpal +// ---------------------------------------------------------------------------- +int IPGetItemHasItemOnHitPropertySubType(object oTarget, int nSubType) +{ + if (GetItemHasItemProperty(oTarget,ITEM_PROPERTY_ON_HIT_PROPERTIES)) + { + itemproperty ipTest = GetFirstItemProperty(oTarget); + + // loop over item properties to see if there is already a poison effect + while (GetIsItemPropertyValid(ipTest)) + { + + if (GetItemPropertySubType(ipTest) == nSubType) //19 == onhit poison + { + return TRUE; + } + + ipTest = GetNextItemProperty(oTarget); + + } + } + return FALSE; +} + +// ---------------------------------------------------------------------------- +// Returns the number of possible armor part variations for the specified part +// nPart - ITEM_APPR_ARMOR_MODEL_* constant +// Uses Get2DAstring, so do not use in loops +// ---------------------------------------------------------------------------- +int IPGetNumberOfArmorAppearances(int nPart) +{ + int nRet; + //SpeakString(Get2DAString(X2_IP_ARMORPARTS_2DA ,"NumParts",nPart)); + nRet = StringToInt(Get2DAString(X2_IP_ARMORPARTS_2DA ,"NumParts",nPart)); + return nRet; +} + +// ---------------------------------------------------------------------------- +// (private) +// Returns the previous or next armor appearance type, depending on the specified +// mode (X2_IP_ARMORTYPE_NEXT || X2_IP_ARMORTYPE_PREV) +// ---------------------------------------------------------------------------- +int IPGetArmorAppearanceType(object oArmor, int nPart, int nMode) +{ + string sMode; + + switch (nMode) + { + case X2_IP_ARMORTYPE_NEXT : sMode ="Next"; + break; + case X2_IP_ARMORTYPE_PREV : sMode ="Prev"; + break; + } + + int nCurrApp = GetItemAppearance(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL,nPart); + int nRet; + + if (nPart ==ITEM_APPR_ARMOR_MODEL_TORSO) + { + nRet = StringToInt(Get2DAString(X2_IP_ARMORAPPEARANCE_2DA ,sMode,nCurrApp)); + return nRet; + } + else + { + int nMax = IPGetNumberOfArmorAppearances(nPart)-1; // index from 0 .. numparts -1 + int nMin = 1; // this prevents part 0 from being chosen (naked) + + // return a random valid armor tpze + if (nMode == X2_IP_ARMORTYPE_RANDOM) + { + return Random(nMax)+nMin; + } + + else + { + if (nMode == X2_IP_ARMORTYPE_NEXT) + { + // current appearance is max, return min + if (nCurrApp == nMax) + { + return nMin; + } + // current appearance is min, return max -1 + else if (nCurrApp == nMin) + { + nRet = nMin+1; + return nRet; + } + + //SpeakString("next"); + // next + nRet = nCurrApp +1; + return nRet; + } + else // previous + { + // current appearance is max, return nMax-1 + if (nCurrApp == nMax) + { + nRet = nMax--; + return nRet; + } + // current appearance is min, return max + else if (nCurrApp == nMin) + { + return nMax; + } + + //SpeakString("prev"); + + nRet = nCurrApp -1; + return nRet; + } + } + + } + +} + +// ---------------------------------------------------------------------------- +// Returns the next valid appearance type for oArmor +// Uses Get2DAstring, so do not use in loops +// ---------------------------------------------------------------------------- +int IPGetNextArmorAppearanceType(object oArmor, int nPart) +{ + return IPGetArmorAppearanceType(oArmor, nPart, X2_IP_ARMORTYPE_NEXT ); + +} + +// ---------------------------------------------------------------------------- +// Returns the next valid appearance type for oArmor +// Uses Get2DAstring, so do not use in loops +// ---------------------------------------------------------------------------- +int IPGetPrevArmorAppearanceType(object oArmor, int nPart) +{ + return IPGetArmorAppearanceType(oArmor, nPart, X2_IP_ARMORTYPE_PREV ); +} + +// ---------------------------------------------------------------------------- +// Returns the next valid appearance type for oArmor +// Uses Get2DAstring, so do not use in loops +// ---------------------------------------------------------------------------- +int IPGetRandomArmorAppearanceType(object oArmor, int nPart) +{ + return IPGetArmorAppearanceType(oArmor, nPart, X2_IP_ARMORTYPE_RANDOM ); +} + +// ---------------------------------------------------------------------------- +// Returns a new armor based of oArmor with nPartModified +// nPart - ITEM_APPR_ARMOR_MODEL_* constant of the part to be changed +// nMode - +// X2_IP_ARMORTYPE_NEXT - next valid appearance +// X2_IP_ARMORTYPE_PREV - previous valid apperance; +// X2_IP_ARMORTYPE_RANDOM - random valid appearance (torso is never changed); +// bDestroyOldOnSuccess - Destroy oArmor in process? +// Uses Get2DAstring, so do not use in loops +// ---------------------------------------------------------------------------- +object IPGetModifiedArmor(object oArmor, int nPart, int nMode, int bDestroyOldOnSuccess) +{ + int nNewApp = IPGetArmorAppearanceType(oArmor, nPart, nMode ); + //SpeakString("old: " + IntToString(GetItemAppearance(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL,nPart))); + //SpeakString("new: " + IntToString(nNewApp)); + + object oNew = CopyItemAndModify(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL, nPart, nNewApp,TRUE); + + if (oNew != OBJECT_INVALID) + { + if( bDestroyOldOnSuccess ) + { + DestroyObject(oArmor); + } + return oNew; + } + // Safety fallback, return old armor on failures + return oArmor; +} + +// ---------------------------------------------------------------------------- +// Creates a special ring on oCreature that gives +// all weapon and armor proficiencies to the wearer +// Item is set non dropable +// ---------------------------------------------------------------------------- +object IPCreateProficiencyFeatItemOnCreature(object oCreature) +{ + // create a simple golden ring + object oRing = CreateItemOnObject("nw_it_mring023",oCreature); + + // just in case + SetDroppableFlag(oRing, FALSE); + + itemproperty ip = ItemPropertyBonusFeat(IP_CONST_FEAT_ARMOR_PROF_HEAVY); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing); + ip = ItemPropertyBonusFeat(IP_CONST_FEAT_ARMOR_PROF_MEDIUM); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing); + ip = ItemPropertyBonusFeat(IP_CONST_FEAT_ARMOR_PROF_LIGHT); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing); + ip = ItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_EXOTIC); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing); + ip = ItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_MARTIAL); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing); + ip = ItemPropertyBonusFeat(IP_CONST_FEAT_WEAPON_PROF_SIMPLE); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oRing); + + return oRing; +} + + +// ---------------------------------------------------------------------------- +// Add an item property in a safe fashion, preventing unwanted stacking +// Parameters: +// oItem - the item to add the property to +// ip - the itemproperty to add +// fDuration - set 0.0f to add the property permanent, anything else is temporary +// nAddItemPropertyPolicy - How to handle existing properties. Valid values are: +// X2_IP_ADDPROP_POLICY_REPLACE_EXISTING - remove any property of the same type, subtype, durationtype before adding; +// X2_IP_ADDPROP_POLICY_KEEP_EXISTING - do not add if any property with same type, subtype and durationtype already exists; +// X2_IP_ADDPROP_POLICY_IGNORE_EXISTING - add itemproperty in any case - Do not Use with OnHit or OnHitSpellCast props! +// bIgnoreDurationType - If set to TRUE, an item property will be considered identical even if the DurationType is different. Be careful when using this +// with X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, as this could lead to a temporary item property removing a permanent one +// bIgnoreSubType - If set to TRUE an item property will be considered identical even if the SubType is different. +// +// * WARNING: This function is used all over the game. Touch it and break it and the wrath +// of the gods will come down on you faster than you can saz "I didn't do it" +// ---------------------------------------------------------------------------- +void IPSafeAddItemProperty(object oItem, itemproperty ip, float fDuration =0.0f, int nAddItemPropertyPolicy = X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, int bIgnoreDurationType = FALSE, int bIgnoreSubType = FALSE) +{ + int nType = GetItemPropertyType(ip); + int nSubType = GetItemPropertySubType(ip); + int nDuration; + // if duration is 0.0f, make the item property permanent + if (fDuration == 0.0f) + { + + nDuration = DURATION_TYPE_PERMANENT; + } else + { + + nDuration = DURATION_TYPE_TEMPORARY; + } + + int nDurationCompare = nDuration; + if (bIgnoreDurationType) + { + nDurationCompare = -1; + } + + if (nAddItemPropertyPolicy == X2_IP_ADDPROP_POLICY_REPLACE_EXISTING) + { + + // remove any matching properties + if (bIgnoreSubType) + { + nSubType = -1; + } + IPRemoveMatchingItemProperties(oItem, nType, nDurationCompare, nSubType ); + } + else if (nAddItemPropertyPolicy == X2_IP_ADDPROP_POLICY_KEEP_EXISTING ) + { + // do not replace existing properties + if(IPGetItemHasProperty(oItem, ip, nDurationCompare, bIgnoreSubType)) + { + return; // item already has property, return + } + } + else //X2_IP_ADDPROP_POLICY_IGNORE_EXISTING + { + + } + + if (nDuration == DURATION_TYPE_PERMANENT) + { + AddItemProperty(nDuration,ip, oItem); + } + else + { + AddItemProperty(nDuration,ip, oItem,fDuration); + } +} + +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +int IPGetItemHasProperty(object oItem, itemproperty ipCompareTo, int nDurationCompare, int bIgnoreSubType = FALSE) +{ + itemproperty ip = GetFirstItemProperty(oItem); + + //PrintString ("Filter - T:" + IntToString(GetItemPropertyType(ipCompareTo))+ " S: " + IntToString(GetItemPropertySubType(ipCompareTo)) + " (Ignore: " + IntToString (bIgnoreSubType) + ") D:" + IntToString(nDurationCompare)); + while (GetIsItemPropertyValid(ip)) + { + // PrintString ("Testing - T: " + IntToString(GetItemPropertyType(ip))); + if ((GetItemPropertyType(ip) == GetItemPropertyType(ipCompareTo))) + { + //PrintString ("**Testing - S: " + IntToString(GetItemPropertySubType(ip))); + if (GetItemPropertySubType(ip) == GetItemPropertySubType(ipCompareTo) || bIgnoreSubType) + { + // PrintString ("***Testing - d: " + IntToString(GetItemPropertyDurationType(ip))); + if (GetItemPropertyDurationType(ip) == nDurationCompare || nDurationCompare == -1) + { + //PrintString ("***FOUND"); + return TRUE; // if duration is not ignored and durationtypes are equal, true + } + } + } + ip = GetNextItemProperty(oItem); + } + //PrintString ("Not Found"); + return FALSE; +} + + +object IPGetTargetedOrEquippedMeleeWeapon() +{ + object oTarget = PRCGetSpellTargetObject(); + if(GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + if (IPGetIsMeleeWeapon(oTarget)) + { + return oTarget; + } + else + { + return OBJECT_INVALID; + } + + } + + object oWeapon1 = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if (GetIsObjectValid(oWeapon1) && IPGetIsMeleeWeapon(oWeapon1)) + { + return oWeapon1; + } + + oWeapon1 = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (GetIsObjectValid(oWeapon1) && IPGetIsMeleeWeapon(oWeapon1)) + { + return oWeapon1; + } + + oWeapon1 = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget); + if (GetIsObjectValid(oWeapon1)) + { + return oWeapon1; + } + + oWeapon1 = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget); + if (GetIsObjectValid(oWeapon1)) + { + return oWeapon1; + } + + return OBJECT_INVALID; + +} + +object IPGetTargetedOrEquippedArmor(int bAllowShields = FALSE) +{ + object oTarget = PRCGetSpellTargetObject(); + + // If the target is a valid item + if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + // Check if the item is armor + if (GetBaseItemType(oTarget) == BASE_ITEM_ARMOR) + { + return oTarget; + } + // Check if the item is a shield and shields are allowed + if (bAllowShields && (GetBaseItemType(oTarget) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oTarget) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oTarget) == BASE_ITEM_TOWERSHIELD)) + { + return oTarget; + } + return OBJECT_INVALID; + } + + // If the target is a creature + if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_CREATURE) + { + // Check the equipped armor + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if (GetIsObjectValid(oArmor) && GetBaseItemType(oArmor) == BASE_ITEM_ARMOR) + { + return oArmor; + } + + // Check the equipped shield if shields are allowed + if (bAllowShields) + { + oArmor = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (GetIsObjectValid(oArmor) && (GetBaseItemType(oArmor) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oArmor) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oArmor) == BASE_ITEM_TOWERSHIELD)) + { + return oArmor; + } + } + } + + // Return invalid object if no valid armor or shield is found + return OBJECT_INVALID; +} + + +/* object IPGetTargetedOrEquippedArmor(int bAllowShields = FALSE) +{ + object oTarget = PRCGetSpellTargetObject(); + if(GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + if (GetBaseItemType(oTarget) == BASE_ITEM_ARMOR) + { + return oTarget; + } + else + { + if ((bAllowShields) && (GetBaseItemType(oTarget) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oTarget) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oTarget) == BASE_ITEM_TOWERSHIELD)) + { + return oTarget; + } + else + { + return OBJECT_INVALID; + } + } + + } + + object oArmor1 = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if (GetIsObjectValid(oArmor1) && GetBaseItemType(oArmor1) == BASE_ITEM_ARMOR) + { + return oArmor1; + } + if (bAllowShields != FALSE) + { + oArmor1 = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (GetIsObjectValid(oArmor1) && (GetBaseItemType(oTarget) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oTarget) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oTarget) == BASE_ITEM_TOWERSHIELD)) + { + return oArmor1; + } + } + + + + return OBJECT_INVALID; + +} + */ +// ---------------------------------------------------------------------------- +// Returns FALSE it the item has no sequencer property +// Returns number of spells that can be stored in any other case +// ---------------------------------------------------------------------------- +int IPGetItemSequencerProperty(object oItem, object oPC = OBJECT_SELF) +{ + if (!GetItemHasItemProperty(oItem, ITEM_PROPERTY_CAST_SPELL)) + { + return FALSE; + } + + int nCnt; + itemproperty ip; + + ip = GetFirstItemProperty(oItem); + + while (GetIsItemPropertyValid(ip) && nCnt ==0) + { + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_CAST_SPELL) + { + if(GetItemPropertySubType(ip) == 523) // sequencer 3 + { + nCnt = 3; + } + else if(GetItemPropertySubType(ip) == 522) // sequencer 2 + { + nCnt = 2; + } + else if(GetItemPropertySubType(ip) == 521) // sequencer 1 + { + nCnt = 1; + } + } + ip = GetNextItemProperty(oItem); + } + return nCnt; +} + +// ---------------------------------------------------------------------------- +// Returns number of spells that can be channeled through item +// ---------------------------------------------------------------------------- +int IPGetItemChannelingProperty(object oItem, object oPC = OBJECT_SELF) +{ + int nCnt; + //arcane archer check + if(GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER, oPC) >= 2 + && GetPRCSwitch(PRC_USE_NEW_IMBUE_ARROW) + && GetBaseItemType(oItem) == BASE_ITEM_ARROW) + { + FloatingTextStringOnCreature("* Imbue Arrow success *", oPC); + nCnt = 1; + } + //spellsword + else if(GetLevelByClass(CLASS_TYPE_SPELLSWORD, oPC) >= 4 + && IPGetIsMeleeWeapon(oItem)) + { + //These are the channel spell charges for the day + int nUses = GetPersistantLocalInt(oPC,"spellswordchannelcharges"); + if(nUses == 0) + { + FloatingTextStringOnCreature("* You have no Channel Spell uses remaining *",oPC); + } + else + { + int iLevel = GetLevelByClass(CLASS_TYPE_SPELLSWORD,oPC); + //Here we check if the spellsword has the multiple channel spell ability + //and store the spell on the weapon with the StoreSpells function. + //If there are multiple channels, we inform the function in which order + //they are stored with the help of a local integer. + if(iLevel >= 4 && iLevel < 10) + nCnt = 1; + else if(iLevel >= 10 && iLevel < 20) + nCnt = 2; + else if(iLevel >= 20 && iLevel < 30) + nCnt = 3; + else if(iLevel >= 30) + nCnt = 4; + + nUses -= 1; + FloatingTextStringOnCreature(IntToString(nUses)+" uses of Channel Spell remaining",oPC); + SetPersistantLocalInt(oPC, "spellswordchannelcharges", nUses); + } + } + return nCnt; +} + +void IPCopyItemProperties(object oSource, object oTarget, int bIgnoreCraftProps = TRUE) +{ + itemproperty ip = GetFirstItemProperty(oSource); + int nSub; + + while (GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT) + { + if (bIgnoreCraftProps) + { + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_CAST_SPELL) + { + nSub = GetItemPropertySubType(ip); + // filter crafting properties + if (nSub != 498 && nSub != 499 && nSub != 526 && nSub != 527) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oTarget); + } + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oTarget); + } + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oTarget); + } + } + ip = GetNextItemProperty(oSource); + } +} + +int IPGetIsIntelligentWeapon(object oItem) +{ + int bRet = FALSE ; + itemproperty ip = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyType(ip) == ITEM_PROPERTY_ONHITCASTSPELL) + { + if (GetItemPropertySubType(ip) == 135) + { + return TRUE; + } + } + ip = GetNextItemProperty(oItem); + } + return bRet; +} + +// ---------------------------------------------------------------------------- +// (private) +// ---------------------------------------------------------------------------- +int IPGetWeaponAppearanceType(object oWeapon, int nPart, int nMode) +{ + string sMode; + + switch (nMode) + { + case X2_IP_WEAPONTYPE_NEXT : sMode ="Next"; + break; + case X2_IP_WEAPONTYPE_PREV : sMode ="Prev"; + break; + } + + int nCurrApp = GetItemAppearance(oWeapon,ITEM_APPR_TYPE_WEAPON_MODEL,nPart); + int nRet; + + int nMax = 9;// IPGetNumberOfArmorAppearances(nPart)-1; // index from 0 .. numparts -1 + int nMin = 1; + + // return a random valid armor tpze + if (nMode == X2_IP_WEAPONTYPE_RANDOM) + { + return Random(nMax)+nMin; + } + + else + { + if (nMode == X2_IP_WEAPONTYPE_NEXT) + { + // current appearance is max, return min + if (nCurrApp == nMax) + { + return nMax; + } + // current appearance is min, return max -1 + else if (nCurrApp == nMin) + { + nRet = nMin +1; + return nRet; + } + + //SpeakString("next"); + // next + nRet = nCurrApp +1; + return nRet; + } + else // previous + { + // current appearance is max, return nMax-1 + if (nCurrApp == nMax) + { + nRet = nMax--; + return nRet; + } + // current appearance is min, return max + else if (nCurrApp == nMin) + { + return nMin; + } + + //SpeakString("prev"); + + nRet = nCurrApp -1; + return nRet; + } + + + } +} + +// ---------------------------------------------------------------------------- +// Returns a new armor based of oArmor with nPartModified +// nPart - ITEM_APPR_WEAPON_MODEL_* constant of the part to be changed +// nMode - +// X2_IP_WEAPONTYPE_NEXT - next valid appearance +// X2_IP_WEAPONTYPE_PREV - previous valid apperance; +// X2_IP_WEAPONTYPE_RANDOM - random valid appearance (torso is never changed); +// bDestroyOldOnSuccess - Destroy oArmor in process? +// Uses Get2DAstring, so do not use in loops +// ---------------------------------------------------------------------------- +object IPGetModifiedWeapon(object oWeapon, int nPart, int nMode, int bDestroyOldOnSuccess) +{ + int nNewApp = IPGetWeaponAppearanceType(oWeapon, nPart, nMode ); + //SpeakString("old: " + IntToString(GetItemAppearance(oWeapon,ITEM_APPR_TYPE_WEAPON_MODEL,nPart))); + //SpeakString("new: " + IntToString(nNewApp)); + object oNew = CopyItemAndModify(oWeapon,ITEM_APPR_TYPE_WEAPON_MODEL, nPart, nNewApp,TRUE); + if (oNew != OBJECT_INVALID) + { + if( bDestroyOldOnSuccess ) + { + DestroyObject(oWeapon); + } + return oNew; + } + // Safety fallback, return old weapon on failures + return oWeapon; +} + + +object IPCreateAndModifyArmorRobe(object oArmor, int nRobeType) +{ + object oRet = CopyItemAndModify(oArmor,ITEM_APPR_TYPE_ARMOR_MODEL,ITEM_APPR_ARMOR_MODEL_ROBE,nRobeType+2,TRUE); + if (GetIsObjectValid(oRet)) + { + return oRet; + } + else // safety net + { + return oArmor; + } +} + + +// ---------------------------------------------------------------------------- +// Provide mapping between numbers and power constants for +// ITEM_PROPERTY_DAMAGE_BONUS +// ---------------------------------------------------------------------------- +int IPGetDamagePowerConstantFromNumber(int nNumber) +{ + switch (nNumber) + { + case 0: return DAMAGE_POWER_NORMAL; + case 1: return DAMAGE_POWER_PLUS_ONE; + case 2: return DAMAGE_POWER_PLUS_TWO; + case 3: return DAMAGE_POWER_PLUS_THREE; + case 4: return DAMAGE_POWER_PLUS_FOUR; + case 5: return DAMAGE_POWER_PLUS_FIVE; + case 6: return DAMAGE_POWER_PLUS_SIX; + case 7: return DAMAGE_POWER_PLUS_SEVEN; + case 8: return DAMAGE_POWER_PLUS_EIGHT; + case 9: return DAMAGE_POWER_PLUS_NINE; + case 10: return DAMAGE_POWER_PLUS_TEN; + case 11: return DAMAGE_POWER_PLUS_ELEVEN; + case 12: return DAMAGE_POWER_PLUS_TWELVE; + case 13: return DAMAGE_POWER_PLUS_THIRTEEN; + case 14: return DAMAGE_POWER_PLUS_FOURTEEN; + case 15: return DAMAGE_POWER_PLUS_FIFTEEN; + case 16: return DAMAGE_POWER_PLUS_SIXTEEN; + case 17: return DAMAGE_POWER_PLUS_SEVENTEEN; + case 18: return DAMAGE_POWER_PLUS_EIGHTEEN ; + case 19: return DAMAGE_POWER_PLUS_NINTEEN; + case 20: return DAMAGE_POWER_PLUS_TWENTY; + } + + if (nNumber>20) + { + return DAMAGE_POWER_PLUS_TWENTY; + } + else + { + return DAMAGE_POWER_NORMAL; + } +} + +// ---------------------------------------------------------------------------- +// Provide mapping between numbers and bonus constants for ITEM_PROPERTY_DAMAGE_BONUS +// Note that nNumber should be > 0! +// ---------------------------------------------------------------------------- +int IPGetDamageBonusConstantFromNumber(int nNumber) +{ + switch (nNumber) + { + case 1: return DAMAGE_BONUS_1; + case 2: return DAMAGE_BONUS_2; + case 3: return DAMAGE_BONUS_3; + case 4: return DAMAGE_BONUS_4; + case 5: return DAMAGE_BONUS_5; + case 6: return DAMAGE_BONUS_6; + case 7: return DAMAGE_BONUS_7; + case 8: return DAMAGE_BONUS_8; + case 9: return DAMAGE_BONUS_9; + case 10: return DAMAGE_BONUS_10; + case 11: return DAMAGE_BONUS_11; + case 12: return DAMAGE_BONUS_12; + case 13: return DAMAGE_BONUS_13; + case 14: return DAMAGE_BONUS_14; + case 15: return DAMAGE_BONUS_15; + case 16: return DAMAGE_BONUS_16; + case 17: return DAMAGE_BONUS_17; + case 18: return DAMAGE_BONUS_18; + case 19: return DAMAGE_BONUS_19; + case 20: return DAMAGE_BONUS_20; + case 21: return DAMAGE_BONUS_21; + case 22: return DAMAGE_BONUS_22; + case 23: return DAMAGE_BONUS_23; + case 24: return DAMAGE_BONUS_24; + case 25: return DAMAGE_BONUS_25; + case 26: return DAMAGE_BONUS_26; + case 27: return DAMAGE_BONUS_27; + case 28: return DAMAGE_BONUS_28; + case 29: return DAMAGE_BONUS_29; + case 30: return DAMAGE_BONUS_30; + case 31: return DAMAGE_BONUS_31; + case 32: return DAMAGE_BONUS_32; + case 33: return DAMAGE_BONUS_33; + case 34: return DAMAGE_BONUS_34; + case 35: return DAMAGE_BONUS_35; + case 36: return DAMAGE_BONUS_36; + case 37: return DAMAGE_BONUS_37; + case 38: return DAMAGE_BONUS_38; + case 39: return DAMAGE_BONUS_39; + case 40: return DAMAGE_BONUS_40; + case 41: return DAMAGE_BONUS_41; + case 42: return DAMAGE_BONUS_42; + case 43: return DAMAGE_BONUS_43; + case 44: return DAMAGE_BONUS_44; + case 45: return DAMAGE_BONUS_45; + case 46: return DAMAGE_BONUS_46; + case 47: return DAMAGE_BONUS_47; + case 48: return DAMAGE_BONUS_48; + case 49: return DAMAGE_BONUS_49; + case 50: return DAMAGE_BONUS_50; + + } + + if (nNumber>50) + { + return DAMAGE_BONUS_50; + } + else + { + return -1; + } +} + +// ---------------------------------------------------------------------------- +// GZ, Sept. 30 2003 +// Special Version of Copy Item Properties for use with greater wild shape +// oOld - Item equipped before polymorphing (source for item props) +// oNew - Item equipped after polymorphing (target for item props) +// bWeapon - Must be set TRUE when oOld is a weapon. +// ---------------------------------------------------------------------------- +void IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE) +{ + if (GetIsObjectValid(oOld) && GetIsObjectValid(oNew)) + { + itemproperty ip = GetFirstItemProperty(oOld); + while (GetIsItemPropertyValid(ip)) + { + if (bWeapon) + { + if (GetWeaponRanged(oOld) == GetWeaponRanged(oNew) ) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); + } + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); + } + ip = GetNextItemProperty(oOld); + + } + } +} + +// ---------------------------------------------------------------------------- +// Returns the current enhancement bonus of a weapon (+1 to +20), 0 if there is +// no enhancement bonus. You can test for a specific type of enhancement bonus +// by passing the appropritate ITEM_PROPERTY_ENHANCEMENT_BONUS* constant into +// nEnhancementBonusType +// +// Now gets the best enhancement, and ignores temporary ones by default - Flaming_Sword +// ---------------------------------------------------------------------------- +int IPGetWeaponEnhancementBonus(object oWeapon, int nEnhancementBonusType = ITEM_PROPERTY_ENHANCEMENT_BONUS, int bIgnoreTemporary = TRUE) +{ + itemproperty ip = GetFirstItemProperty(oWeapon); + int nFound = 0, nTemp = 0; + while (nFound == 0 && GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyType(ip) == nEnhancementBonusType) + { + if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT || !bIgnoreTemporary) + { + nTemp = GetItemPropertyCostTableValue(ip); + nFound = PRCMax(nFound, nTemp); + } + } + ip = GetNextItemProperty(oWeapon); + } + return nFound; +} + +// ---------------------------------------------------------------------------- +// Shortcut function to set the enhancement bonus of a weapon to a certain bonus +// Specifying bOnlyIfHigher as TRUE will prevent a bonus lower than the requested +// bonus from being applied. Valid values for nBonus are 1 to 20. +// ---------------------------------------------------------------------------- +void IPSetWeaponEnhancementBonus(object oWeapon, int nBonus, int bOnlyIfHigher = TRUE) +{ + int nCurrent = IPGetWeaponEnhancementBonus(oWeapon); + + itemproperty ip = GetFirstItemProperty(oWeapon); + + if (bOnlyIfHigher && nCurrent > nBonus) + { + return; + } + + if (nBonus <1 || nBonus > 20) + { + return; + } + + while (GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_ENHANCEMENT_BONUS) + { + RemoveItemProperty(oWeapon,ip); + } + ip = GetNextItemProperty(oWeapon); + } + + ip = ItemPropertyEnhancementBonus(nBonus); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oWeapon); +} + + +// ---------------------------------------------------------------------------- +// Shortcut function to upgrade the enhancement bonus of a weapon by the +// number specified in nUpgradeBy. If the resulting new enhancement bonus +// would be out of bounds (>+20), it will be set to +20 +// ---------------------------------------------------------------------------- +void IPUpgradeWeaponEnhancementBonus(object oWeapon, int nUpgradeBy) +{ + int nCurrent = IPGetWeaponEnhancementBonus(oWeapon); + + itemproperty ip = GetFirstItemProperty(oWeapon); + + int nNew = nCurrent + nUpgradeBy; + if (nNew <1 ) + { + nNew = 1; + } + else if (nNew >20) + { + nNew = 20; + } + + while (GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyType(ip) ==ITEM_PROPERTY_ENHANCEMENT_BONUS) + { + RemoveItemProperty(oWeapon,ip); + } + ip = GetNextItemProperty(oWeapon); + } + + ip = ItemPropertyEnhancementBonus(nNew); + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oWeapon); + +} + +int IPGetHasItemPropertyByConst(int nItemProp, object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + while (GetIsItemPropertyValid(ip)) + { + if (GetItemPropertyType(ip) ==nItemProp) + { + return TRUE; + } + ip = GetNextItemProperty(oItem); + } + return FALSE; + +} + +// ---------------------------------------------------------------------------- +// Returns TRUE if a use limitation of any kind is present on oItem +// ---------------------------------------------------------------------------- +int IPGetHasUseLimitation(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + int nType; + while (GetIsItemPropertyValid(ip)) + { + nType = GetItemPropertyType(ip); + if ( + nType == ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP || + nType == ITEM_PROPERTY_USE_LIMITATION_CLASS || + nType == ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE || + nType == ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT ) + { + return TRUE; + } + ip = GetNextItemProperty(oItem); + } + return FALSE; + +} + +//------------------------------------------------------------------------------ +// GZ, Oct 2003 +// Returns TRUE if a character has any item equipped that has the itemproperty +// defined in nItemPropertyConst in it (ITEM_PROPERTY_* constant) +//------------------------------------------------------------------------------ +int IPGetHasItemPropertyOnCharacter(object oPC, int nItemPropertyConst) +{ + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oLeftHand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + + int bHas = IPGetHasItemPropertyByConst(nItemPropertyConst, oWeaponOld); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oLeftHand); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oArmorOld); + if (bHas) + return TRUE; + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oRing1Old); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oRing2Old); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oAmuletOld); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oCloakOld); + if (bHas) + return TRUE; + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oBootsOld); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oBeltOld); + bHas = bHas || IPGetHasItemPropertyByConst(nItemPropertyConst, oHelmetOld); + + return bHas; + +} + +//------------------------------------------------------------------------------ +// GZ, Oct 24, 2003 +// Returns an integer with the number of properties present oItem +//------------------------------------------------------------------------------ +int IPGetNumberOfItemProperties(object oItem) +{ + itemproperty ip = GetFirstItemProperty(oItem); + int nCount = 0; + while (GetIsItemPropertyValid(ip)) + { + nCount++; + ip = GetNextItemProperty(oItem); + } + return nCount; +} + +int IPDamageConstant(int nDamBon) +{ + int nIPBonus; + switch(nDamBon) + { + case 1: nIPBonus = IP_CONST_DAMAGEBONUS_1; break; + case 2: nIPBonus = IP_CONST_DAMAGEBONUS_2; break; + case 3: nIPBonus = IP_CONST_DAMAGEBONUS_3; break; + case 4: nIPBonus = IP_CONST_DAMAGEBONUS_4; break; + case 5: nIPBonus = IP_CONST_DAMAGEBONUS_5; break; + case 6: nIPBonus = IP_CONST_DAMAGEBONUS_6; break; + case 7: nIPBonus = IP_CONST_DAMAGEBONUS_7; break; + case 8: nIPBonus = IP_CONST_DAMAGEBONUS_8; break; + case 9: nIPBonus = IP_CONST_DAMAGEBONUS_9; break; + case 10: nIPBonus = IP_CONST_DAMAGEBONUS_10; break; + case 11: nIPBonus = IP_CONST_DAMAGEBONUS_11; break; + case 12: nIPBonus = IP_CONST_DAMAGEBONUS_12; break; + case 13: nIPBonus = IP_CONST_DAMAGEBONUS_13; break; + case 14: nIPBonus = IP_CONST_DAMAGEBONUS_14; break; + case 15: nIPBonus = IP_CONST_DAMAGEBONUS_15; break; + case 16: nIPBonus = IP_CONST_DAMAGEBONUS_16; break; + case 17: nIPBonus = IP_CONST_DAMAGEBONUS_17; break; + case 18: nIPBonus = IP_CONST_DAMAGEBONUS_18; break; + case 19: nIPBonus = IP_CONST_DAMAGEBONUS_19; break; + case 20: nIPBonus = IP_CONST_DAMAGEBONUS_20; break; + case 21: nIPBonus = IP_CONST_DAMAGEBONUS_21; break; + case 22: nIPBonus = IP_CONST_DAMAGEBONUS_22; break; + case 23: nIPBonus = IP_CONST_DAMAGEBONUS_23; break; + case 24: nIPBonus = IP_CONST_DAMAGEBONUS_24; break; + case 25: nIPBonus = IP_CONST_DAMAGEBONUS_25; break; + case 26: nIPBonus = IP_CONST_DAMAGEBONUS_26; break; + case 27: nIPBonus = IP_CONST_DAMAGEBONUS_27; break; + case 28: nIPBonus = IP_CONST_DAMAGEBONUS_28; break; + case 29: nIPBonus = IP_CONST_DAMAGEBONUS_29; break; + case 30: nIPBonus = IP_CONST_DAMAGEBONUS_30; break; + case 31: nIPBonus = IP_CONST_DAMAGEBONUS_31; break; + case 32: nIPBonus = IP_CONST_DAMAGEBONUS_32; break; + case 33: nIPBonus = IP_CONST_DAMAGEBONUS_33; break; + case 34: nIPBonus = IP_CONST_DAMAGEBONUS_34; break; + case 35: nIPBonus = IP_CONST_DAMAGEBONUS_35; break; + case 36: nIPBonus = IP_CONST_DAMAGEBONUS_36; break; + case 37: nIPBonus = IP_CONST_DAMAGEBONUS_37; break; + case 38: nIPBonus = IP_CONST_DAMAGEBONUS_38; break; + case 39: nIPBonus = IP_CONST_DAMAGEBONUS_39; break; + case 40: nIPBonus = IP_CONST_DAMAGEBONUS_40; break; + case 41: nIPBonus = IP_CONST_DAMAGEBONUS_41; break; + case 42: nIPBonus = IP_CONST_DAMAGEBONUS_42; break; + case 43: nIPBonus = IP_CONST_DAMAGEBONUS_43; break; + case 44: nIPBonus = IP_CONST_DAMAGEBONUS_44; break; + case 45: nIPBonus = IP_CONST_DAMAGEBONUS_45; break; + case 46: nIPBonus = IP_CONST_DAMAGEBONUS_46; break; + case 47: nIPBonus = IP_CONST_DAMAGEBONUS_47; break; + case 48: nIPBonus = IP_CONST_DAMAGEBONUS_48; break; + case 49: nIPBonus = IP_CONST_DAMAGEBONUS_49; break; + case 50: nIPBonus = IP_CONST_DAMAGEBONUS_50; break; + } + if (nDamBon > 20) nIPBonus = IP_CONST_DAMAGEBONUS_50; + + return nIPBonus; +} + + +/* int IPDamageConstant(int nDamBon) +{ + int nIPBonus; + switch(nDamBon) + { + case 1: nIPBonus = IP_CONST_DAMAGEBONUS_1; break; + case 2: nIPBonus = IP_CONST_DAMAGEBONUS_2; break; + case 3: nIPBonus = IP_CONST_DAMAGEBONUS_3; break; + case 4: nIPBonus = IP_CONST_DAMAGEBONUS_4; break; + case 5: nIPBonus = IP_CONST_DAMAGEBONUS_5; break; + case 6: nIPBonus = IP_CONST_DAMAGEBONUS_6; break; + case 7: nIPBonus = IP_CONST_DAMAGEBONUS_7; break; + case 8: nIPBonus = IP_CONST_DAMAGEBONUS_8; break; + case 9: nIPBonus = IP_CONST_DAMAGEBONUS_9; break; + case 10: nIPBonus = IP_CONST_DAMAGEBONUS_10; break; + case 11: nIPBonus = IP_CONST_DAMAGEBONUS_11; break; + case 12: nIPBonus = IP_CONST_DAMAGEBONUS_12; break; + case 13: nIPBonus = IP_CONST_DAMAGEBONUS_13; break; + case 14: nIPBonus = IP_CONST_DAMAGEBONUS_14; break; + case 15: nIPBonus = IP_CONST_DAMAGEBONUS_15; break; + case 16: nIPBonus = IP_CONST_DAMAGEBONUS_16; break; + case 17: nIPBonus = IP_CONST_DAMAGEBONUS_17; break; + case 18: nIPBonus = IP_CONST_DAMAGEBONUS_18; break; + case 19: nIPBonus = IP_CONST_DAMAGEBONUS_19; break; + case 20: nIPBonus = IP_CONST_DAMAGEBONUS_20; break; + } + if (nDamBon > 20) nIPBonus = IP_CONST_DAMAGEBONUS_20; + + return nIPBonus; +} */ + +int IPOnHitSaveDC(int nSaveDC) +{ + int nIPBonus = IP_CONST_ONHIT_SAVEDC_10; + switch(nSaveDC) + { + case 10: nIPBonus = IP_CONST_ONHIT_SAVEDC_10; break; + case 11: nIPBonus = IP_CONST_ONHIT_SAVEDC_11; break; + case 12: nIPBonus = IP_CONST_ONHIT_SAVEDC_12; break; + case 13: nIPBonus = IP_CONST_ONHIT_SAVEDC_13; break; + case 14: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 15: nIPBonus = IP_CONST_ONHIT_SAVEDC_15; break; + case 16: nIPBonus = IP_CONST_ONHIT_SAVEDC_16; break; + case 17: nIPBonus = IP_CONST_ONHIT_SAVEDC_17; break; + case 18: nIPBonus = IP_CONST_ONHIT_SAVEDC_18; break; + case 19: nIPBonus = IP_CONST_ONHIT_SAVEDC_19; break; + case 20: nIPBonus = IP_CONST_ONHIT_SAVEDC_20; break; + case 21: nIPBonus = IP_CONST_ONHIT_SAVEDC_21; break; + case 22: nIPBonus = IP_CONST_ONHIT_SAVEDC_22; break; + case 23: nIPBonus = IP_CONST_ONHIT_SAVEDC_23; break; + case 24: nIPBonus = IP_CONST_ONHIT_SAVEDC_24; break; + case 25: nIPBonus = IP_CONST_ONHIT_SAVEDC_25; break; + case 26: nIPBonus = IP_CONST_ONHIT_SAVEDC_26; break; + case 27: nIPBonus = IP_CONST_ONHIT_SAVEDC_27; break; + case 28: nIPBonus = IP_CONST_ONHIT_SAVEDC_28; break; + case 29: nIPBonus = IP_CONST_ONHIT_SAVEDC_29; break; + case 30: nIPBonus = IP_CONST_ONHIT_SAVEDC_30; break; + case 31: nIPBonus = IP_CONST_ONHIT_SAVEDC_31; break; + case 32: nIPBonus = IP_CONST_ONHIT_SAVEDC_32; break; + case 33: nIPBonus = IP_CONST_ONHIT_SAVEDC_33; break; + case 34: nIPBonus = IP_CONST_ONHIT_SAVEDC_34; break; + case 35: nIPBonus = IP_CONST_ONHIT_SAVEDC_35; break; + case 36: nIPBonus = IP_CONST_ONHIT_SAVEDC_36; break; + case 37: nIPBonus = IP_CONST_ONHIT_SAVEDC_37; break; + case 38: nIPBonus = IP_CONST_ONHIT_SAVEDC_38; break; + case 39: nIPBonus = IP_CONST_ONHIT_SAVEDC_39; break; + case 40: nIPBonus = IP_CONST_ONHIT_SAVEDC_40; break; + case 41: nIPBonus = IP_CONST_ONHIT_SAVEDC_41; break; + case 42: nIPBonus = IP_CONST_ONHIT_SAVEDC_42; break; + case 43: nIPBonus = IP_CONST_ONHIT_SAVEDC_43; break; + case 44: nIPBonus = IP_CONST_ONHIT_SAVEDC_44; break; + case 45: nIPBonus = IP_CONST_ONHIT_SAVEDC_45; break; + case 46: nIPBonus = IP_CONST_ONHIT_SAVEDC_46; break; + case 47: nIPBonus = IP_CONST_ONHIT_SAVEDC_47; break; + case 48: nIPBonus = IP_CONST_ONHIT_SAVEDC_48; break; + case 49: nIPBonus = IP_CONST_ONHIT_SAVEDC_49; break; + case 50: nIPBonus = IP_CONST_ONHIT_SAVEDC_50; break; + case 51: nIPBonus = IP_CONST_ONHIT_SAVEDC_51; break; + case 52: nIPBonus = IP_CONST_ONHIT_SAVEDC_52; break; + case 53: nIPBonus = IP_CONST_ONHIT_SAVEDC_53; break; + case 54: nIPBonus = IP_CONST_ONHIT_SAVEDC_54; break; + case 55: nIPBonus = IP_CONST_ONHIT_SAVEDC_55; break; + case 56: nIPBonus = IP_CONST_ONHIT_SAVEDC_56; break; + case 57: nIPBonus = IP_CONST_ONHIT_SAVEDC_57; break; + case 58: nIPBonus = IP_CONST_ONHIT_SAVEDC_58; break; + case 59: nIPBonus = IP_CONST_ONHIT_SAVEDC_59; break; + case 60: nIPBonus = IP_CONST_ONHIT_SAVEDC_60; break; + case 61: nIPBonus = IP_CONST_ONHIT_SAVEDC_61; break; + case 62: nIPBonus = IP_CONST_ONHIT_SAVEDC_62; break; + case 63: nIPBonus = IP_CONST_ONHIT_SAVEDC_63; break; + case 64: nIPBonus = IP_CONST_ONHIT_SAVEDC_64; break; + case 65: nIPBonus = IP_CONST_ONHIT_SAVEDC_65; break; + case 66: nIPBonus = IP_CONST_ONHIT_SAVEDC_66; break; + case 67: nIPBonus = IP_CONST_ONHIT_SAVEDC_67; break; + case 68: nIPBonus = IP_CONST_ONHIT_SAVEDC_68; break; + case 69: nIPBonus = IP_CONST_ONHIT_SAVEDC_69; break; + case 70: nIPBonus = IP_CONST_ONHIT_SAVEDC_70; break; + } + if (nSaveDC < 10) nIPBonus = IP_CONST_ONHIT_SAVEDC_10; + if (nSaveDC > 70) nIPBonus = IP_CONST_ONHIT_SAVEDC_70; + + return nIPBonus; +} + +/* int IPOnHitSaveDC(int nSaveDC) +{ + int nIPBonus; + switch(nSaveDC) + { + case 10: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 11: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 12: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 13: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 14: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 15: nIPBonus = IP_CONST_ONHIT_SAVEDC_14; break; + case 16: nIPBonus = IP_CONST_ONHIT_SAVEDC_16; break; + case 17: nIPBonus = IP_CONST_ONHIT_SAVEDC_16; break; + case 18: nIPBonus = IP_CONST_ONHIT_SAVEDC_18; break; + case 19: nIPBonus = IP_CONST_ONHIT_SAVEDC_18; break; + case 20: nIPBonus = IP_CONST_ONHIT_SAVEDC_20; break; + case 21: nIPBonus = IP_CONST_ONHIT_SAVEDC_20; break; + case 22: nIPBonus = IP_CONST_ONHIT_SAVEDC_22; break; + case 23: nIPBonus = IP_CONST_ONHIT_SAVEDC_22; break; + case 24: nIPBonus = IP_CONST_ONHIT_SAVEDC_24; break; + case 25: nIPBonus = IP_CONST_ONHIT_SAVEDC_24; break; + case 26: nIPBonus = IP_CONST_ONHIT_SAVEDC_26; break; + } + if (nSaveDC > 26) nIPBonus = IP_CONST_ONHIT_SAVEDC_26; + + return nIPBonus; +} */ \ No newline at end of file diff --git a/src/include/prcsp_archmaginc.nss b/src/include/prcsp_archmaginc.nss new file mode 100644 index 0000000..4287a19 --- /dev/null +++ b/src/include/prcsp_archmaginc.nss @@ -0,0 +1,200 @@ +// +// Wrapper Functions for the Archmage Class and Feats +// + +// +// Notes: Normal use is to include prc_alterations. +// If this file if to be included elsewhere add the following lines +// to the target file: +// #include "prcsp_reputation" +// #include "prcsp_archmaginc" +// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/// @todo Change these to TLK reads + +const string MASTERY_OF_ELEMENTS_TAG = "archmage_mastery_elements"; +const string MASTERY_OF_ELEMENTS_NAME_TAG = "archmage_mastery_elements_name"; +const string MASTERY_OF_SHAPE_TAG = "archmage_mastery_shaping"; +const string MASTERY_OF_SHAPE_ON = "Shaping spells to protect allies."; +const string MASTERY_OF_SHAPE_OFF = "Spell shaping is disabled, allies may be effected."; +const string MASTERY_OF_ELEMENTS_ACID = "Elemental spell damage set to acid."; +const string MASTERY_OF_ELEMENTS_COLD = "Elemental spell damage set to cold."; +const string MASTERY_OF_ELEMENTS_ELECTRICAL = "Elemental spell damage set to electrical."; +const string MASTERY_OF_ELEMENTS_FIRE = "Elemental spell damage set to fire."; +const string MASTERY_OF_ELEMENTS_SONIC = "Elemental spell damage set to sonic."; +const string MASTERY_OF_ELEMENTS_OFF = "Elemental spell damage returned to normal."; + +const int FEAT_INACTIVE = 0; +const int FEAT_ACTIVE = 1; + +const int MASTERY_OF_SHAPE_EFFECT = 460; +const int MASTERY_OF_ELEMENTS_EFFECT_ACID = 448; +const int MASTERY_OF_ELEMENTS_EFFECT_ELECTRICAL = 463; +const int MASTERY_OF_ELEMENTS_EFFECT_OFF = 460; + +const int SPELL_MASTERY_ELEMENTS_NORMAL = 2000; +const int SPELL_MASTERY_ELEMENTS_ACID = 2003; +const int SPELL_MASTERY_ELEMENTS_COLD = 2002; +const int SPELL_MASTERY_ELEMENTS_ELECTRICITY = 2004; +const int SPELL_MASTERY_ELEMENTS_FIRE = 2001; +const int SPELL_MASTERY_ELEMENTS_SONIC = 2005; + +const int TIME_1_ROUND = 1; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines if Master of Shapes is active and applies in regards to the + * given target. + * + * @param oCaster A creature casting an area-affecting spell + * @param oTarget A creature that is in the affected area + * @return TRUE if the creature should be exempt from the spell due to + * Mastery of Shapes. FALSE otherwise + */ +int CheckMasteryOfShapes(object oCaster, object oTarget); + +void SetFeatVisualEffects(object oCaster, int nEffect, string sMessage); + +void ToggleMasteryOfShapes(object oCaster); + +void SetMasteryOfElements(); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "lookup_2da_spell" +#include "prcsp_reputation" +//#include "prc_inc_spells" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int CheckMasteryOfShapes(object oCaster, object oTarget) +{ + int bRetVal = FALSE; + + // This variable should not be set without the feat being available. + // If someone wants to cheat, let them. + if (GetLocalInt(oCaster, MASTERY_OF_SHAPE_TAG) == FEAT_ACTIVE && !GetIsReactionTypeHostile(oTarget, oCaster)) + { + bRetVal = TRUE; + } + + return bRetVal; +} + +int ExtraordinarySpellAim(object oCaster, object oTarget) +{ + int bRetVal = FALSE; + + // This variable should not be set without the feat being available. + // If someone wants to cheat, let them. + if(GetHasFeat(FEAT_EXTRAORDINARY_SPELL_AIM, oCaster) + && !GetLocalInt(oCaster, "ExtraordinarySpellAim") + && GetIsFriend(oTarget, oCaster)) + { + // Only once per spell + SetLocalInt(oCaster, "ExtraordinarySpellAim", TRUE); + DelayCommand(1.0, DeleteLocalInt(oCaster, "ExtraordinarySpellAim")); + if(GetIsSkillSuccessful(oCaster, SKILL_SPELLCRAFT, 25 + PRCGetSpellLevel(oCaster, PRCGetSpellId()))) + bRetVal = TRUE; + } + + return bRetVal; +} + +// +// Help with Visual Effects when setting feats +// +void SetFeatVisualEffects(object oCaster, int nEffect, string sMessage) +{ + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(nEffect), + oCaster, RoundsToSeconds(TIME_1_ROUND)); + + FloatingTextStringOnCreature(sMessage, OBJECT_SELF, FALSE); +} + +// +// Enable/Disable Mastery of Shapes +// +void ToggleMasteryOfShapes(object oCaster) +{ + if (GetLocalInt(OBJECT_SELF, MASTERY_OF_SHAPE_TAG) == FEAT_INACTIVE) { + SetLocalInt(OBJECT_SELF, MASTERY_OF_SHAPE_TAG, FEAT_ACTIVE); + SetFeatVisualEffects(oCaster, MASTERY_OF_SHAPE_EFFECT, MASTERY_OF_SHAPE_ON); + } + else { + SetLocalInt(OBJECT_SELF, MASTERY_OF_SHAPE_TAG, FEAT_INACTIVE); + SetFeatVisualEffects(oCaster, MASTERY_OF_SHAPE_EFFECT, MASTERY_OF_SHAPE_OFF); + } +} + +// +// This function sets the Mastery of Elements feat to a specific element +// +void SetMasteryOfElements() +{ + string msg = MASTERY_OF_ELEMENTS_OFF; + string sElem = ""; + int nEffect = MASTERY_OF_ELEMENTS_EFFECT_OFF; + int dmgType = FEAT_INACTIVE; + + switch (PRCGetSpellId()) { + case SPELL_MASTERY_ELEMENTS_ACID: + nEffect = MASTERY_OF_ELEMENTS_EFFECT_ACID; + dmgType = DAMAGE_TYPE_ACID; + msg = MASTERY_OF_ELEMENTS_ACID; + sElem = "Acid"; + break; + + case SPELL_MASTERY_ELEMENTS_COLD: + nEffect = VFX_IMP_AC_BONUS; + dmgType = DAMAGE_TYPE_COLD; + msg = MASTERY_OF_ELEMENTS_COLD; + sElem = "Cold"; + break; + + case SPELL_MASTERY_ELEMENTS_ELECTRICITY: + nEffect = MASTERY_OF_ELEMENTS_EFFECT_ELECTRICAL; + dmgType = DAMAGE_TYPE_ELECTRICAL; + msg = MASTERY_OF_ELEMENTS_ELECTRICAL; + sElem = "Electricity"; + break; + + case SPELL_MASTERY_ELEMENTS_FIRE: + nEffect = VFX_IMP_ELEMENTAL_PROTECTION; + dmgType = DAMAGE_TYPE_FIRE; + msg = MASTERY_OF_ELEMENTS_FIRE; + sElem = "Fire"; + break; + + case SPELL_MASTERY_ELEMENTS_SONIC: + nEffect = VFX_FNF_SOUND_BURST; + dmgType = DAMAGE_TYPE_SONIC; + msg = MASTERY_OF_ELEMENTS_SONIC; + sElem = "Sonic"; + break; + + default: + // Use the default initialized variables + break; + } + + SetLocalInt(OBJECT_SELF, MASTERY_OF_ELEMENTS_TAG, dmgType); + SetLocalString(OBJECT_SELF, MASTERY_OF_ELEMENTS_NAME_TAG, sElem); + SetFeatVisualEffects(PRCGetSpellTargetObject(), nEffect, msg); +} + +// Test main +//void main(){} diff --git a/src/include/prcsp_engine.nss b/src/include/prcsp_engine.nss new file mode 100644 index 0000000..833ec49 --- /dev/null +++ b/src/include/prcsp_engine.nss @@ -0,0 +1,401 @@ + +// Module Constants +const float CACHE_TIMEOUT_CAST = 2.0; +const string CASTER_LEVEL_TAG = "PRCEffectiveCasterLevel"; + +// Constants that dictate ResistSpell results +const int SPELL_RESIST_FAIL = 0; +const int SPELL_RESIST_PASS = 1; +const int SPELL_RESIST_GLOBE = 2; +const int SPELL_RESIST_MANTLE = 3; + +int PRCDoResistSpell(object oCaster, object oTarget, int nEffCasterLvl=0, float fDelay = 0.0); + +int CheckSpellfire(object oCaster, object oTarget, int bFriendly = FALSE); + +#include "prc_inc_racial" +//#include "prc_feat_const" +//#include "prc_class_const" +//#include "prcsp_reputation" +#include "prcsp_archmaginc" +//#include "prc_add_spell_dc" +#include "prc_add_spl_pen" + + +// +// This function is a wrapper should someone wish to rewrite the Bioware +// version. This is where it should be done. +// +int PRCResistSpell(object oCaster, object oTarget) +{ + return ResistSpell(oCaster, oTarget); +} + +// +// This function is a wrapper should someone wish to rewrite the Bioware +// version. This is where it should be done. +// +int PRCGetSpellResistance(object oTarget, object oCaster) +{ + int iSpellRes = GetSpellResistance(oTarget); + int nHD = GetHitDice(oTarget); + + //racial pack SR + int iRacialSpellRes = 0; + if(GetHasFeat(FEAT_SPELL27, oTarget)) + iRacialSpellRes = 27 + nHD; + else if(GetHasFeat(FEAT_SPELL25, oTarget)) + iRacialSpellRes = 25 + nHD; + else if(GetHasFeat(FEAT_SPELL23, oTarget)) + iRacialSpellRes = 23 + nHD; + else if(GetHasFeat(FEAT_SPELL22, oTarget)) + iRacialSpellRes = 22 + nHD; + else if(GetHasFeat(FEAT_SPELL21, oTarget)) + iRacialSpellRes = 21 + nHD; + else if(GetHasFeat(FEAT_SPELL20, oTarget)) + iRacialSpellRes = 20 + nHD; + else if(GetHasFeat(FEAT_SPELL19, oTarget)) + iRacialSpellRes = 19 + nHD; + else if(GetHasFeat(FEAT_SPELL18, oTarget)) + iRacialSpellRes = 18 + nHD; + else if(GetHasFeat(FEAT_SPELL17, oTarget)) + iRacialSpellRes = 17 + nHD; + else if(GetHasFeat(FEAT_SPELL16, oTarget)) + iRacialSpellRes = 16 + nHD; + else if(GetHasFeat(FEAT_SPELL15, oTarget)) + iRacialSpellRes = 15 + nHD; + else if(GetHasFeat(FEAT_SPELL14, oTarget)) + iRacialSpellRes = 14 + nHD; + else if(GetHasFeat(FEAT_SPELL13, oTarget)) + iRacialSpellRes = 13 + nHD; + else if(GetHasFeat(FEAT_SPELL11, oTarget)) + iRacialSpellRes = 11 + nHD; + else if(GetHasFeat(FEAT_SPELL10, oTarget)) + iRacialSpellRes = 10 + nHD; + else if(GetHasFeat(FEAT_SPELL8, oTarget)) + iRacialSpellRes = 8 + nHD; + else if(GetHasFeat(FEAT_SPELL5, oTarget)) + iRacialSpellRes = 5 + nHD; + if(iRacialSpellRes > iSpellRes) + iSpellRes = iRacialSpellRes; + + // Exalted Companion, can also be used for Celestial Template + if(GetLocalInt(oTarget, "CelestialTemplate") || GetLocalInt(oTarget, "PseudonaturalTemplate")) + { + int nSR = nHD * 2; + if (nSR > 25) nSR = 25; + if (nSR > iSpellRes) iSpellRes = nSR; + } + + // Enlightened Fist SR = 10 + monk level + enlightened fist level + if(GetHasFeat(FEAT_EF_DIAMOND_SOUL, oTarget)) + { + int nEF = 10 + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oTarget) + GetLevelByClass(CLASS_TYPE_MONK, oTarget); + if(nEF > iSpellRes) + iSpellRes = nEF; + } + + // Contemplative SR = 15 + contemplative level + if(GetHasFeat(FEAT_DIVINE_SOUL, oTarget)) + { + int nCont = 15 + GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oTarget); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + + // Marrutact + if(GetRacialType(oTarget) == RACIAL_TYPE_MARRUTACT) + { + int nCont = 9 + GetHitDice(oTarget); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + + // Hobgoblin Wsrsoul + if(GetRacialType(oTarget) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + { + int nCont = 8 + GetHitDice(oTarget); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + + // Exordius Weapon of Legacy + if(GetLocalInt(oTarget, "ExordiusSR")) + { + int nCont = 5 + GetHitDice(oTarget); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + + // Hammer of Witches Weapon of Legacy + if(GetLocalInt(oTarget, "HammerWitchesSR")) + { + // SR vs arcane only + if(GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster))) + { + int nCont = 5 + GetHitDice(oTarget); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + } + + // Ur-Priest + int nPriestLevel = GetLevelByClass(CLASS_TYPE_UR_PRIEST, oTarget); + if(nPriestLevel >= 4) + { + // SR vs divine only + if(GetIsDivineClass(PRCGetLastSpellCastClass(oCaster))) + { + //if(nPriestLevel > 50) nPriestLevel = 50; //:: cap if needed + + // Calculate bonus: 15 at level 4, then +5 for every additional 4 levels + int nCont = 15 + (((nPriestLevel - 4) / 4) * 5); + + if(nCont > iSpellRes) + iSpellRes = nCont; + } + } +/* // Ur-Priest + if(GetLevelByClass(CLASS_TYPE_UR_PRIEST, oTarget) >= 4) + { + // SR vs divine only + if(GetIsDivineClass(PRCGetLastSpellCastClass(oCaster))) + { + int nCont = 15; + if (GetLevelByClass(CLASS_TYPE_UR_PRIEST, oTarget) >= 8) nCont = 20; + if(nCont > iSpellRes) + iSpellRes = nCont; + } + } */ + + // Dread Carapace Heart Bind + if(GetIsIncarnumUser(oTarget)) + { + if (GetIsMeldBound(oTarget, MELD_DREAD_CARAPACE) == CHAKRA_CROWN) + { + int nCont = 5 + (4 * GetEssentiaInvested(oTarget, MELD_DREAD_CARAPACE)); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + if (GetHasSpellEffect(MELD_SPELLWARD_SHIRT, oTarget)) // MELD_SPELLWARD_SHIRT + { + int nCont = 5 + (4 * GetEssentiaInvested(oTarget, MELD_SPELLWARD_SHIRT)); + if(nCont > iSpellRes) + iSpellRes = nCont; + } + } + + // Foe Hunter SR stacks with normal SR when a spell is cast by their hated enemy + if(GetHasFeat(FEAT_HATED_ENEMY_SR, oTarget) && GetLocalInt(oTarget, "HatedFoe") == MyPRCGetRacialType(oCaster)) + { + iSpellRes += 15 + GetLevelByClass(CLASS_TYPE_FOE_HUNTER, oTarget); + } + + // Adds +4 to SR + if(GetHasFeat(FEAT_PSYCHIC_REFUSAL, oTarget)) + iSpellRes += 4; + + // Forsaker SR adds to existing + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget)) + iSpellRes = iSpellRes + 10 + GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget); + + return iSpellRes; +} + +// +// If a spell is resisted, display the effect +// +void PRCShowSpellResist(object oCaster, object oTarget, int nResist, float fDelay = 0.0) +{ + // If either caster/target is a PC send them a message + if (GetIsPC(oCaster)) + { + string message = nResist == SPELL_RESIST_FAIL ? + "Target is affected by the spell." : "Target resisted the spell."; + SendMessageToPC(oCaster, message); + } + if (GetIsPC(oTarget)) + { + string message = nResist == SPELL_RESIST_FAIL ? + "You are affected by the spell." : "You resisted the spell."; + SendMessageToPC(oTarget, message); + } + + if (nResist != SPELL_RESIST_FAIL) { + // Default to a standard resistance + int eve = VFX_IMP_MAGIC_RESISTANCE_USE; + + // Check for other resistances + if (nResist == SPELL_RESIST_GLOBE) + eve = VFX_IMP_GLOBE_USE; + else if (nResist == SPELL_RESIST_MANTLE) + eve = VFX_IMP_SPELL_MANTLE_USE; + + // Render the effect + DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, + EffectVisualEffect(eve), oTarget)); + } +} + +// +// This function overrides the BioWare MyResistSpell. +// TODO: Change name to PRCMyResistSpell. +// +int PRCDoResistSpell(object oCaster, object oTarget, int nEffCasterLvl=0, float fDelay = 0.0) +{ + int nResist; + + // Check if the archmage shape mastery applies to this target + if (CheckSpellfire(oCaster, oTarget) || CheckMasteryOfShapes(oCaster, oTarget) || ExtraordinarySpellAim(oCaster, oTarget) || (GetLocalInt(oCaster, "WOL_DesertWindFireball") && GetSpellId() == SPELL_FIREBALL)) + nResist = SPELL_RESIST_MANTLE; + else if(GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster) >= 20 && GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE)) + { + //Beguilers of level 20+ automatically overcome SR of targets denied Dex bonus to AC + nResist = SPELL_RESIST_FAIL; + } + else if(GetLocalInt(oCaster, "CunningBreach")) + { + //Factotum can pay to breach all SR for a round + nResist = SPELL_RESIST_FAIL; + } + //using vitriolic blast with eldritch spellweave + else if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET") + && GetLocalInt(oCaster, "BlastEssence") == INVOKE_VITRIOLIC_BLAST) + { + nResist = SPELL_RESIST_FAIL; + } + else { + // Check immunities and mantles, otherwise ignore the result completely + nResist = PRCResistSpell(oCaster, oTarget); + + //Resonating Resistance + if((nResist <= SPELL_RESIST_PASS) && (GetHasSpellEffect(SPELL_RESONATING_RESISTANCE, oTarget))) + { + nResist = PRCResistSpell(oCaster, oTarget); + } + + if (nResist <= SPELL_RESIST_PASS) + { + nResist = SPELL_RESIST_FAIL; + + // Because the version of this function was recently changed to + // optionally allow the caster level, we must calculate it here. + // The result will be cached for a period of time. + if (!nEffCasterLvl) { + nEffCasterLvl = GetLocalInt(oCaster, CASTER_LEVEL_TAG); + if (!nEffCasterLvl) { + nEffCasterLvl = PRCGetCasterLevel(oCaster) + SPGetPenetr(); + SetLocalInt(oCaster, CASTER_LEVEL_TAG, nEffCasterLvl); + DelayCommand(CACHE_TIMEOUT_CAST, + DeleteLocalInt(oCaster, CASTER_LEVEL_TAG)); + } + } + + // Pernicious Magic + // +4 caster level vs SR Weave user (not Evoc & Trans spells) + int iWeav; + if (GetHasFeat(FEAT_PERNICIOUSMAGIC,oCaster)) + { + if (!GetHasFeat(FEAT_SHADOWWEAVE,oTarget)) + { + int nSchool = GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR"); + if ( nSchool != SPELL_SCHOOL_EVOCATION && nSchool != SPELL_SCHOOL_TRANSMUTATION ) + iWeav=4; + } + } + + + // A tie favors the caster. + if ((nEffCasterLvl + d20(1)+iWeav) < PRCGetSpellResistance(oTarget, oCaster)) + nResist = SPELL_RESIST_PASS; + } + } + + // Karsites heal from resisting a spell + if(GetRacialType(oTarget) == RACIAL_TYPE_KARSITE && nResist == SPELL_RESIST_PASS) + { + int nSpellLevel = StringToInt(Get2DACache("spells", "Innate", PRCGetSpellId())); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nSpellLevel*2), oTarget); + } + + PRCShowSpellResist(oCaster, oTarget, nResist, fDelay); + + return nResist; +} + +//Returns the maximum number of spellfire levels oPC can store +int SpellfireMax(object oPC) +{ + //can't absorb spells without feat + if(!GetHasFeat(FEAT_SPELLFIRE_WIELDER, oPC)) return 0; + + int nCON = GetAbilityScore(oPC, ABILITY_CONSTITUTION); + + int i, nCount; + for (i = FEAT_EPIC_SPELLFIRE_WIELDER_I; i <= FEAT_EPIC_SPELLFIRE_WIELDER_X; i++) + { + if (GetHasFeat(i, oPC)) + nCON = nCON + 4; + } + if (DEBUG) DoDebug("SpellfireMax nCon is "+IntToString(nCON)); + + int nStorage = ((GetLevelByClass(CLASS_TYPE_SPELLFIRE, oPC) + 1) / 2) + 1; + if(nStorage > 5) nStorage = 5; + return nCON * nStorage; +} + +//Increases the number of stored spellfire levels on a creature +void AddSpellfireLevels(object oPC, int nLevels) +{ + int nMax = SpellfireMax(oPC); + int nStored = GetPersistantLocalInt(oPC, "SpellfireLevelStored"); + nStored += nLevels; + if(nStored > nMax) nStored = nMax; //capped + SetPersistantLocalInt(oPC, "SpellfireLevelStored", nStored); +} + +//Checks if spell target can absorb spells by being a spellfire wielder +int CheckSpellfire(object oCaster, object oTarget, int bFriendly = FALSE) +{ + //can't absorb spells without feat + if(!GetHasFeat(FEAT_SPELLFIRE_WIELDER, oTarget)) return 0; + + //Can't absorb own spells/powers if switch is set + if(GetPRCSwitch(PRC_SPELLFIRE_DISALLOW_CHARGE_SELF) && oTarget == oCaster) return 0; + + //abilities rely on access to weave + if(GetHasFeat(FEAT_SHADOWWEAVE, oTarget)) return 0; + + int nSpellID = PRCGetSpellId(); + if(!bFriendly && GetLocalInt(oCaster, "IsAOE_" + IntToString(nSpellID))) + return 0; //can't absorb hostile AOE spells + + int nSpellfireLevel = GetPersistantLocalInt(oTarget, "SpellfireLevelStored"); + if(DEBUG) DoDebug("CheckSpellfire: " + IntToString(nSpellfireLevel) + " levels stored", oTarget); + + int nMax = SpellfireMax(oTarget); + + if(DEBUG) DoDebug("CheckSpellfire: Maximum " + IntToString(nMax), oTarget); + + //can't absorb any more spells, sanity check + if(nSpellfireLevel >= nMax) return 0; + + //increasing stored levels + int nSpellLevel = GetLocalInt(oCaster, "PRC_CurrentManifest_PowerLevel"); //replicates GetPowerLevel(oCaster); + if(!nSpellLevel) //not a power //avoids compiler problems + { //with includes + string sInnate = Get2DACache("spells", "Innate", nSpellID);//lookup_spell_innate(nSpellID); + if(sInnate == "") return 0; //no innate level, unlike cantrips + nSpellLevel = StringToInt(sInnate); + } + /* + string sInnate = Get2DACache("spells", "Innate", nSpellID); + if(sInnate == "") return 0; //no innate level, unlike cantrips + int nSpellLevel = StringToInt(sInnate); + */ + + AddSpellfireLevels(oTarget, nSpellLevel); + + //absorbed + return 1; +} \ No newline at end of file diff --git a/src/include/prcsp_reputation.nss b/src/include/prcsp_reputation.nss new file mode 100644 index 0000000..d86441d --- /dev/null +++ b/src/include/prcsp_reputation.nss @@ -0,0 +1,149 @@ +#include "x2_inc_switches" + +// * Generic reputation wrapper +// * definition of constants: +// * SPELL_TARGET_ALLALLIES = Will affect all allies, even those in my faction who don't like me +// * SPELL_TARGET_STANDARDHOSTILE: 90% of offensive area spells will work +// this way. They will never hurt NEUTRAL or FRIENDLY NPCs. +// They will never hurt FRIENDLY PCs +// They WILL hurt NEUTRAL PCs +// * SPELL_TARGET_SELECTIVEHOSTILE: Will only ever hurt enemies + +// * Constants +// * see spellsIsTarget for a definition of these constants +const int SPELL_TARGET_ALLALLIES = 1; +const int SPELL_TARGET_STANDARDHOSTILE = 2; +const int SPELL_TARGET_SELECTIVEHOSTILE = 3; + +//:://///////////////////////////////////////////// +//:: spellsIsTarget +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + This is the reputation wrapper. + It performs the check to see if, based on the + constant provided + it is okay to target this target with the + spell effect. + + + MODIFIED APRIL 2003 + - Other player's associates will now be harmed in + Standard Hostile mode + - Will ignore dead people in all target attempts + + MODIFIED AUG 2003 - GZ + - Multiple henchmen support: made sure that + AoE spells cast by one henchmen do not + affect other henchmen in the party + +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: March 6 2003 +//::////////////////////////////////////////////// + +// This kind of spell will affect all friendlies and anyone in my +// party, even if we are upset with each other currently. +int +HandleDispositionALLALLIES(object oTarget, object oSource) +{ + return GetIsReactionTypeFriendly(oTarget,oSource) + || GetFactionEqual(oTarget,oSource); +} + +// Only harms enemies, ever. +// current list: +// call lightning, isaac missiles, firebrand, chain lightning, dirge, Nature's balance, +// Word of Faith +int +HandleDispositionSELECTIVEHOSTILE(object oTarget, object oSource) +{ + return GetIsEnemy(oTarget, oSource); +} + +int +HandleDispositionSTANDARDHOSTILE(object oTarget, object oSource) +{ + object oMaster = GetMaster(oTarget); + + // March 25 2003. In hardcore, casters will affect themselves and + // their associates. + if (GetGameDifficulty() > GAME_DIFFICULTY_NORMAL) { + if (oTarget == oSource || oMaster == oSource) + return TRUE; + } + + // April 9 2003. Hurt the associates of a hostile player + if (GetIsObjectValid(oMaster)) { + // Target is an associate of an unfriendly PC or is outright hostile + if ((!GetIsReactionTypeFriendly(oMaster,oSource) && GetIsPC(oMaster)) + || GetIsReactionTypeHostile(oMaster,oSource)) + return TRUE; + } + + // Spiderfriend Magic means that spells never hurt neutral or allied vermin + if (GetHasFeat(FEAT_SPIDERFRIEND_MAGIC, oSource) && !GetIsReactionTypeHostile(oTarget, oSource) && GetRacialType(oTarget) == RACIAL_TYPE_VERMIN) + return FALSE; + + // Assumption: In Full PvP players, even if in same party, are Neutral + // GZ: 2003-08-30: Patch to make creatures hurt each other in hardcore mode. + + // Hostile creatures are always a target. + if (GetIsReactionTypeHostile(oTarget,oSource)) + return TRUE; + + // Handle logic for neutral targets (we know !Hostile(), see above). + if (!GetIsReactionTypeFriendly(oTarget, oSource)) { + // 'neutral' PCs are targets + if (GetIsPC(oTarget)) + return TRUE; + + // Local Override is just an out for end users who want + // the area effect spells to hurt 'neutrals'. + if (GetLocalInt(GetModule(), "X0_G_ALLOWSPELLSTOHURT") == 10) + return TRUE; + + if (GetModuleSwitchValue(MODULE_SWITCH_ENABLE_NPC_AOE_HURT_ALLIES) + && GetGameDifficulty() > GAME_DIFFICULTY_NORMAL) + return TRUE; // Hostile Creature and Difficulty > Normal. + // In hardcore mode any creature is hostile. + } + + // Default response + return FALSE; +} + + +int spellsIsTarget(object oTarget, int nTargetType, object oSource) +{ + // * if dead, not a valid target + if (GetIsDead(oTarget)) + return FALSE; + + // GZ: Creatures with the same master will never damage each other + object oTargetMaster = GetMaster(oTarget); + object oSourceMaster = GetMaster(oSource); + if (GetIsObjectValid(oTargetMaster) + && GetIsObjectValid(oSourceMaster) + && !GetModuleSwitchValue(MODULE_SWITCH_ENABLE_MULTI_HENCH_AOE_DAMAGE) + && oTargetMaster == oSourceMaster + && nTargetType != SPELL_TARGET_ALLALLIES) + { + return FALSE; + } + + switch (nTargetType) { + case SPELL_TARGET_ALLALLIES: + return HandleDispositionALLALLIES(oTarget, oSource); + + case SPELL_TARGET_SELECTIVEHOSTILE: + return HandleDispositionSELECTIVEHOSTILE(oTarget, oSource); + + case SPELL_TARGET_STANDARDHOSTILE: + return HandleDispositionSTANDARDHOSTILE(oTarget, oSource); + } + + // unhandled dispositions + return FALSE; +} diff --git a/src/include/prgt_inc.nss b/src/include/prgt_inc.nss new file mode 100644 index 0000000..724da40 --- /dev/null +++ b/src/include/prgt_inc.nss @@ -0,0 +1,105 @@ +//:://///////////////////////////////////////////// +//:: Name Primogenitors Respawning Ground Trap include +//:: FileName prgt_inc +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + This was orignally designed to allow respawning ground traps + However, as of NWN 1.67 this is no longer needed. + + The secondary purpose of this is now most useful and that + is to provide a system where a wide variety of traps + can be set and used. + + This particular file provides interface functions +*/ +//::////////////////////////////////////////////// +//:: Created By: Primogenitor +//:: Created On: Quite some time ago +//::////////////////////////////////////////////// + +/* +int DAMAGE_TYPE_BLUDGEONING = 1; +int DAMAGE_TYPE_PIERCING = 2; +int DAMAGE_TYPE_SLASHING = 4; +int DAMAGE_TYPE_MAGICAL = 8; +int DAMAGE_TYPE_ACID = 16; +int DAMAGE_TYPE_COLD = 32; +int DAMAGE_TYPE_DIVINE = 64; +int DAMAGE_TYPE_ELECTRICAL = 128; +int DAMAGE_TYPE_FIRE = 256; +int DAMAGE_TYPE_NEGATIVE = 512; +int DAMAGE_TYPE_POSITIVE = 1024; +int DAMAGE_TYPE_SONIC = 2048; +*/ + +#include "prgt_inc_trap" +#include "inc_utility" +#include "prc_misc_const" + +const int TRAP_EVENT_TRIGGERED = 1; +const int TRAP_EVENT_DISARMED = 2; +const int TRAP_EVENT_RECOVERED = 3; //this is in addition to being disarmed + + +object PRGT_CreateTrapAtLocation(location lLoc, struct trap tTrap) +{ + object oTrap; + oTrap = CreateTrapAtLocation(TRAP_BASE_TYPE_PRGT, + lLoc, + tTrap.fSize, + "",//tag + STANDARD_FACTION_HOSTILE, + tTrap.sDisarmScript, + tTrap.sTriggerScript); + + SetLocalTrap(oTrap, "TrapSettings", tTrap); + SetTrapOneShot(oTrap, FALSE); + SetTrapRecoverable(oTrap, FALSE); + + return oTrap; +} + +void PRGT_CreateTrapOnObject(object oTrap, struct trap tTrap) +{ + CreateTrapOnObject(TRAP_BASE_TYPE_PRGT, + oTrap, + STANDARD_FACTION_HOSTILE, + tTrap.sDisarmScript, + tTrap.sTriggerScript); + + SetLocalTrap(oTrap, "TrapSettings", tTrap); + SetTrapOneShot(oTrap, FALSE); + SetTrapRecoverable(oTrap, FALSE); +} + + +void PRGT_VoidCreateTrapAtLocation(location lLoc, struct trap tTrap) +{ + PRGT_CreateTrapAtLocation(lLoc, tTrap); +} + +void DoTrapXP(object oTrap, object oTarget, int nEvent) +{ + switch(nEvent) + { + case TRAP_EVENT_TRIGGERED: + if(GetLocalString(GetModule(), PRC_PRGT_XP_SCRIPT_TRIGGERED) != "") + ExecuteScript(PRC_PRGT_XP_SCRIPT_TRIGGERED, oTarget); + else if(GetPRCSwitch(PRC_PRGT_XP_AWARD_FOR_TRIGGERED)) + GiveXPRewardToParty(oTarget, OBJECT_INVALID, GetLocalTrap(oTrap, "TrapSettings").nCR); + break; + case TRAP_EVENT_DISARMED: + if(GetLocalString(GetModule(), PRC_PRGT_XP_SCRIPT_DISARMED) != "") + ExecuteScript(PRC_PRGT_XP_SCRIPT_TRIGGERED, oTarget); + else if(GetPRCSwitch(PRC_PRGT_XP_AWARD_FOR_DISARMED)) + GiveXPRewardToParty(oTarget, OBJECT_INVALID, GetLocalTrap(oTrap, "TrapSettings").nCR); + break; + case TRAP_EVENT_RECOVERED: + if(GetLocalString(GetModule(), PRC_PRGT_XP_SCRIPT_RECOVERED) != "") + ExecuteScript(PRC_PRGT_XP_SCRIPT_TRIGGERED, oTarget); + else if(GetPRCSwitch(PRC_PRGT_XP_AWARD_FOR_RECOVERED)) + GiveXPRewardToParty(oTarget, OBJECT_INVALID, GetLocalTrap(oTrap, "TrapSettings").nCR); + break; + } +} diff --git a/src/include/prgt_inc_trap.nss b/src/include/prgt_inc_trap.nss new file mode 100644 index 0000000..2040b7a --- /dev/null +++ b/src/include/prgt_inc_trap.nss @@ -0,0 +1,350 @@ +//:://///////////////////////////////////////////// +//:: Name Primogenitors Respawning Ground Trap include +//:: FileName prgt_inc_trap +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + This was orignally designed to allow respawning ground traps + However, as of NWN 1.67 this is no longer needed. + + The secondary purpose of this is now most useful and that + is to provide a system where a wide variety of traps + can be set and used. + + This particular file details the trap struct used to track + all the relevant information in it +*/ +//::////////////////////////////////////////////// +//:: Created By: Primogenitor +//:: Created On: Quite some time ago +//::////////////////////////////////////////////// + +#include "prc_misc_const" +#include "inc_ecl" + + +struct trap +{ +//DC to detect the trap + int nDetectDC; +//DC to disarm the trap +//By PnP only rogues can disarm traps over DC 35? + int nDisarmDC; +//this is the script that is fired when the trap is +//triggered + string sTriggerScript; +//this is the script that is fired when the trap is +//disarmed + string sDisarmScript; +//if the trap casts a spell when triggered +//these control the details + int nSpellID; + int nSpellLevel; + int nSpellMetamagic; + int nSpellDC; +//these are for normal dmaging type traps + int nDamageType; + int nRadius; + int nDamageDice; + int nDamageSize; + int nDamageBonus; +//visual things + int nTargetVFX; + int nTrapVFX; + int nBeamVFX; + int nFakeSpell; + int nFakeSpellLoc; +//saves for half + int nAllowReflexSave; + int nAllowFortSave; + int nAllowWillSave; + int nSaveDC; +//this is a mesure of CR of the trap +//can be used by XP scripts + int nCR; +//delay before respawning once destroyed/disarmed + int nRespawnSeconds; +//CR passed to CreateRandomTrap when respawning +//if not set, uses same trap as before + int nRespawnRandomCR; +//this is the size of the trap on the ground +//if zero, 2.0 is used + float fSize; +}; + +struct trap GetLocalTrap(object oObject, string sVarName); +void SetLocalTrap(object oObject, string sVarName, struct trap tTrap); +void DeleteLocalTrap(object oObject, string sVarName); +struct trap CreateRandomTrap(int nCR = -1); + +/** + * Converts the given trap into a string. The structure members' names and + * values are listed separated by line breaks. + * + * @param tTrap A trap structure to convert into a string. + * @return A string representation of tTrap. + */ +string TrapToString(struct trap tTrap); + + +////////////////////////////////////// +/* Includes */ +////////////////////////////////////// + +#include "inc_utility" + + + +////////////////////////////////////// +/* Function Definitions */ +////////////////////////////////////// + +struct trap CreateRandomTrap(int nCR = -1) +{ + if(nCR == -1) + { + nCR = GetECL(GetFirstPC()); + nCR += Random(5)-2; + if(nCR < 1) + nCR = 1; + } + struct trap tReturn; + switch(Random(26)) + { + case 0: tReturn.nDamageType = DAMAGE_TYPE_PIERCING; break; + case 1: tReturn.nDamageType = DAMAGE_TYPE_PIERCING; break; + case 2: tReturn.nDamageType = DAMAGE_TYPE_PIERCING; break; + case 3: tReturn.nDamageType = DAMAGE_TYPE_PIERCING; break; + case 4: tReturn.nDamageType = DAMAGE_TYPE_PIERCING; break; + case 5: tReturn.nDamageType = DAMAGE_TYPE_PIERCING; break; + case 6: tReturn.nDamageType = DAMAGE_TYPE_BLUDGEONING; break; + case 7: tReturn.nDamageType = DAMAGE_TYPE_BLUDGEONING; break; + case 8: tReturn.nDamageType = DAMAGE_TYPE_BLUDGEONING; break; + case 9: tReturn.nDamageType = DAMAGE_TYPE_BLUDGEONING; break; + case 10: tReturn.nDamageType = DAMAGE_TYPE_BLUDGEONING; break; + case 11: tReturn.nDamageType = DAMAGE_TYPE_BLUDGEONING; break; + case 12: tReturn.nDamageType = DAMAGE_TYPE_SLASHING; break; + case 13: tReturn.nDamageType = DAMAGE_TYPE_SLASHING; break; + case 14: tReturn.nDamageType = DAMAGE_TYPE_SLASHING; break; + case 15: tReturn.nDamageType = DAMAGE_TYPE_SLASHING; break; + case 16: tReturn.nDamageType = DAMAGE_TYPE_SLASHING; break; + case 17: tReturn.nDamageType = DAMAGE_TYPE_SLASHING; break; + case 18: tReturn.nDamageType = DAMAGE_TYPE_FIRE; break; + case 19: tReturn.nDamageType = DAMAGE_TYPE_FIRE; break; + case 20: tReturn.nDamageType = DAMAGE_TYPE_COLD; break; + case 21: tReturn.nDamageType = DAMAGE_TYPE_COLD; break; + case 22: tReturn.nDamageType = DAMAGE_TYPE_ELECTRICAL; break; + case 23: tReturn.nDamageType = DAMAGE_TYPE_ELECTRICAL; break; + case 24: tReturn.nDamageType = DAMAGE_TYPE_ACID; break; + case 25: tReturn.nDamageType = DAMAGE_TYPE_SONIC; break; + } + + tReturn.nRadius = 5+(nCR/2); + tReturn.nDamageDice = 1+(nCR/2); + tReturn.nDamageSize = 6; + tReturn.nDamageBonus = 0; + tReturn.nDetectDC = 15+nCR; + tReturn.nDisarmDC = 15+nCR; + tReturn.nCR = nCR; + tReturn.nRespawnSeconds = 0; + tReturn.nRespawnRandomCR = nCR; + tReturn.sTriggerScript = "prgt_trap_fire"; + tReturn.sDisarmScript = "prgt_trap_disa"; + tReturn.fSize = 2.0; + + switch(tReturn.nDamageType) + { + case DAMAGE_TYPE_BLUDGEONING: + tReturn.nFakeSpellLoc = 773; //bolder tossing + tReturn.nRadius /= 2; + tReturn.nDamageDice *= 2; + tReturn.nAllowReflexSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_SLASHING: + tReturn.nTrapVFX = VFX_FNF_SWINGING_BLADE; + tReturn.nRadius /= 2; + tReturn.nDamageSize *= 2; + tReturn.nAllowReflexSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_PIERCING: + tReturn.nTargetVFX = VFX_IMP_SPIKE_TRAP; + tReturn.nRadius /= 4; + tReturn.nDamageSize *= 2; + tReturn.nDamageDice *= 2; + tReturn.nAllowReflexSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_COLD: + tReturn.nTrapVFX = VFX_FNF_ICESTORM; + tReturn.nTargetVFX = VFX_IMP_FROST_S; + tReturn.nRadius *= 2; + tReturn.nDamageSize /= 2; + tReturn.nDamageDice /= 2; + tReturn.nAllowFortSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_FIRE: + tReturn.nTrapVFX = VFX_FNF_FIREBALL; + tReturn.nTargetVFX = VFX_IMP_FLAME_S; + tReturn.nRadius *= 2; + tReturn.nDamageSize /= 2; + tReturn.nDamageDice /= 2; + tReturn.nAllowReflexSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_ELECTRICAL: + tReturn.nBeamVFX = VFX_BEAM_LIGHTNING; + tReturn.nTargetVFX = VFX_IMP_LIGHTNING_S; + tReturn.nRadius /= 4; + tReturn.nDamageSize *= 2; + tReturn.nDamageDice *= 2; + tReturn.nAllowReflexSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_SONIC: + tReturn.nTrapVFX = VFX_FNF_SOUND_BURST; + tReturn.nTargetVFX = VFX_IMP_SONIC; + tReturn.nRadius *= 2; + tReturn.nDamageSize /= 2; + tReturn.nDamageDice /= 2; + tReturn.nAllowFortSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + case DAMAGE_TYPE_ACID: + tReturn.nTrapVFX = VFX_FNF_GAS_EXPLOSION_ACID; + tReturn.nTargetVFX = VFX_IMP_ACID_S; + tReturn.nRadius *= 2; + tReturn.nDamageSize /= 2; + tReturn.nDamageDice /= 2; + tReturn.nAllowFortSave = TRUE; + tReturn.nSaveDC = 10+nCR; + break; + } + return tReturn; +} + +struct trap GetLocalTrap(object oObject, string sVarName) +{ + struct trap tReturn; + tReturn.nDetectDC = GetLocalInt(oObject, sVarName+".nDetectDC"); + tReturn.nDisarmDC = GetLocalInt(oObject, sVarName+".nDisarmDC"); + tReturn.sTriggerScript = GetLocalString(oObject, sVarName+".sTriggerScript"); + tReturn.sDisarmScript = GetLocalString(oObject, sVarName+".sDisarmScript"); + tReturn.nSpellID = GetLocalInt(oObject, sVarName+".nSpellID"); + tReturn.nSpellLevel = GetLocalInt(oObject, sVarName+".nSpellLevel"); + tReturn.nSpellMetamagic = GetLocalInt(oObject, sVarName+".nSpellMetamagic"); + tReturn.nSpellDC = GetLocalInt(oObject, sVarName+".nSpellDC"); + tReturn.nDamageType = GetLocalInt(oObject, sVarName+".nDamageType"); + tReturn.nRadius = GetLocalInt(oObject, sVarName+".nRadius"); + tReturn.nDamageDice = GetLocalInt(oObject, sVarName+".nDamageDice"); + tReturn.nDamageSize = GetLocalInt(oObject, sVarName+".nDamageSize"); + tReturn.nDamageBonus = GetLocalInt(oObject, sVarName+".nDamageBonus"); + tReturn.nAllowReflexSave= GetLocalInt(oObject, sVarName+".nAllowReflexSave"); + tReturn.nAllowFortSave = GetLocalInt(oObject, sVarName+".nAllowFortSave"); + tReturn.nAllowWillSave = GetLocalInt(oObject, sVarName+".nAllowWillSave"); + tReturn.nSaveDC = GetLocalInt(oObject, sVarName+".nSaveDC"); + tReturn.nTargetVFX = GetLocalInt(oObject, sVarName+".nTargetVFX"); + tReturn.nTrapVFX = GetLocalInt(oObject, sVarName+".nTrapVFX"); + tReturn.nFakeSpell = GetLocalInt(oObject, sVarName+".nFakeSpell"); + tReturn.nFakeSpellLoc = GetLocalInt(oObject, sVarName+".nFakeSpellLoc"); + tReturn.nBeamVFX = GetLocalInt(oObject, sVarName+".nBeamVFX"); + tReturn.nCR = GetLocalInt(oObject, sVarName+".nCR"); + tReturn.nRespawnSeconds = GetLocalInt(oObject, sVarName+".nRespawnSeconds"); + tReturn.nRespawnRandomCR= GetLocalInt(oObject, sVarName+".nRespawnRandomCR"); + tReturn.fSize = GetLocalFloat(oObject, sVarName+".fSize"); + + return tReturn; +} +void SetLocalTrap(object oObject, string sVarName, struct trap tTrap) +{ + SetLocalInt(oObject, sVarName+".nDetectDC", tTrap.nDetectDC); + SetLocalInt(oObject, sVarName+".nDisarmDC", tTrap.nDisarmDC); + SetLocalString(oObject, sVarName+".sTriggerScript", tTrap.sTriggerScript); + SetLocalString(oObject, sVarName+".sDisarmScript", tTrap.sDisarmScript); + SetLocalInt(oObject, sVarName+".nSpellID", tTrap.nSpellID); + SetLocalInt(oObject, sVarName+".nSpellLevel", tTrap.nSpellLevel); + SetLocalInt(oObject, sVarName+".nSpellMetamagic", tTrap.nSpellMetamagic); + SetLocalInt(oObject, sVarName+".nSpellDC", tTrap.nSpellDC); + SetLocalInt(oObject, sVarName+".nDamageType", tTrap.nDamageType); + SetLocalInt(oObject, sVarName+".nRadius", tTrap.nRadius); + SetLocalInt(oObject, sVarName+".nDamageDice", tTrap.nDamageDice); + SetLocalInt(oObject, sVarName+".nDamageSize", tTrap.nDamageSize); + SetLocalInt(oObject, sVarName+".nDamageBonus", tTrap.nDamageBonus); + SetLocalInt(oObject, sVarName+".nAllowReflexSave", tTrap.nAllowReflexSave); + SetLocalInt(oObject, sVarName+".nAllowFortSave", tTrap.nAllowFortSave); + SetLocalInt(oObject, sVarName+".nAllowWillSave", tTrap.nAllowWillSave); + SetLocalInt(oObject, sVarName+".nSaveDC", tTrap.nSaveDC); + SetLocalInt(oObject, sVarName+".nTargetVFX", tTrap.nTargetVFX); + SetLocalInt(oObject, sVarName+".nTrapVFX", tTrap.nTrapVFX); + SetLocalInt(oObject, sVarName+".nFakeSpell", tTrap.nFakeSpell); + SetLocalInt(oObject, sVarName+".nFakeSpellLoc", tTrap.nFakeSpellLoc); + SetLocalInt(oObject, sVarName+".nBeamVFX", tTrap.nBeamVFX); + SetLocalInt(oObject, sVarName+".nCR", tTrap.nCR); + SetLocalInt(oObject, sVarName+".nRespawnSeconds", tTrap.nRespawnSeconds); + SetLocalInt(oObject, sVarName+".nRespawnRandomCR", tTrap.nRespawnRandomCR); + SetLocalFloat(oObject, sVarName+".fSize", tTrap.fSize); +} +void DeleteLocalTrap(object oObject, string sVarName) +{ + DeleteLocalInt(oObject, sVarName+".nDetectDC"); + DeleteLocalInt(oObject, sVarName+".nDisarmDC"); + DeleteLocalString(oObject, sVarName+".sTriggerScript"); + DeleteLocalString(oObject, sVarName+".sDisarmScript"); + DeleteLocalInt(oObject, sVarName+".nSpellID"); + DeleteLocalInt(oObject, sVarName+".nSpellLevel"); + DeleteLocalInt(oObject, sVarName+".nSpellMetamagic"); + DeleteLocalInt(oObject, sVarName+".nSpellDC"); + DeleteLocalInt(oObject, sVarName+".nDamageType"); + DeleteLocalInt(oObject, sVarName+".nRadius"); + DeleteLocalInt(oObject, sVarName+".nDamageDice"); + DeleteLocalInt(oObject, sVarName+".nDamageSize"); + DeleteLocalInt(oObject, sVarName+".nDamageBonus"); + DeleteLocalInt(oObject, sVarName+".nAllowReflexSave"); + DeleteLocalInt(oObject, sVarName+".nAllowFortSave"); + DeleteLocalInt(oObject, sVarName+".nAllowWillSave"); + DeleteLocalInt(oObject, sVarName+".nSaveDC"); + DeleteLocalInt(oObject, sVarName+".nTargetVFX"); + DeleteLocalInt(oObject, sVarName+".nTrapVFX"); + DeleteLocalInt(oObject, sVarName+".nFakeSpell"); + DeleteLocalInt(oObject, sVarName+".nFakeSpellLoc"); + DeleteLocalInt(oObject, sVarName+".nBeamVFX"); + DeleteLocalInt(oObject, sVarName+".nCR"); + DeleteLocalInt(oObject, sVarName+".nRespawnSeconds"); + DeleteLocalInt(oObject, sVarName+".nRespawnRandomCR"); + DeleteLocalFloat(oObject, sVarName+".fSize"); +} + +string TrapToString(struct trap tTrap) +{ + string s; + s += "nDetectDC: " + IntToString(tTrap.nDetectDC) + "\n"; + s += "nDisarmDC: " + IntToString(tTrap.nDisarmDC) + "\n"; + s += "sTriggerScript: '" + tTrap.sTriggerScript + "'\n"; + s += "sDisarmScript: '" + tTrap.sDisarmScript + "'\n"; + s += "nSpellID: " + IntToString(tTrap.nSpellID) + "\n"; + s += "nSpellLevel: " + IntToString(tTrap.nSpellLevel) + "\n"; + s += "nSpellMetamagic: " + IntToString(tTrap.nSpellMetamagic) + "\n"; + s += "nSpellDC: " + IntToString(tTrap.nSpellDC) + "\n"; + s += "nDamageType: " + IntToString(tTrap.nDamageType) + "\n"; + s += "nRadius: " + IntToString(tTrap.nRadius) + "\n"; + s += "nDamageDice: " + IntToString(tTrap.nDamageDice) + "\n"; + s += "nDamageSize: " + IntToString(tTrap.nDamageSize) + "\n"; + s += "nDamageBonus: " + IntToString(tTrap.nDamageBonus) + "\n"; + s += "nAllowReflexSave: " + IntToString(tTrap.nAllowReflexSave) + "\n"; + s += "nAllowFortSave: " + IntToString(tTrap.nAllowFortSave) + "\n"; + s += "nAllowWillSave: " + IntToString(tTrap.nAllowWillSave) + "\n"; + s += "nSaveDC: " + IntToString(tTrap.nSaveDC) + "\n"; + s += "nTargetVFX: " + IntToString(tTrap.nTargetVFX) + "\n"; + s += "nTrapVFX: " + IntToString(tTrap.nTrapVFX) + "\n"; + s += "nFakeSpell: " + IntToString(tTrap.nFakeSpell) + "\n"; + s += "nFakeSpellLoc: " + IntToString(tTrap.nFakeSpellLoc) + "\n"; + s += "nBeamVFX: " + IntToString(tTrap.nBeamVFX) + "\n"; + s += "nCR: " + IntToString(tTrap.nCR) + "\n"; + s += "nRespawnSeconds: " + IntToString(tTrap.nRespawnSeconds) + "\n"; + s += "nRespawnRandomCR: " + IntToString(tTrap.nRespawnRandomCR) + "\n"; + s += "fSize: " + FloatToString(tTrap.fSize) + "\n"; + + return s; +} diff --git a/src/include/psi_inc_ac_const.nss b/src/include/psi_inc_ac_const.nss new file mode 100644 index 0000000..425bddc --- /dev/null +++ b/src/include/psi_inc_ac_const.nss @@ -0,0 +1,87 @@ +//:://///////////////////////////////////////////// +//:: Astral Construct constants include +//:: psi_inc_ac_const +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 23.01.2005 +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// +const int ASTRAL_CONSTRUCT_OPTION_BUFF = 1; +const int ASTRAL_CONSTRUCT_OPTION_CELERITY = 2; +const int ASTRAL_CONSTRUCT_OPTION_CLEAVE = 4; +const int ASTRAL_CONSTRUCT_OPTION_IMPROVED_SLAM = 8; +const int ASTRAL_CONSTRUCT_OPTION_DEFLECTION = 16; +const int ASTRAL_CONSTRUCT_OPTION_MOBILITY = 32; +const int ASTRAL_CONSTRUCT_OPTION_POWER_ATTACK = 64; +const int ASTRAL_CONSTRUCT_OPTION_RESISTANCE = 128; +const int ASTRAL_CONSTRUCT_OPTION_KNOCKDOWN = 256; +const int ASTRAL_CONSTRUCT_OPTION_ENERGY_TOUCH = 512; +const int ASTRAL_CONSTRUCT_OPTION_EXTRA_ATTACK = 1024; +const int ASTRAL_CONSTRUCT_OPTION_FAST_HEALING = 2048; +const int ASTRAL_CONSTRUCT_OPTION_HEAVY_DEFLECT = 4096; +const int ASTRAL_CONSTRUCT_OPTION_IMP_BUFF = 8192; +const int ASTRAL_CONSTRUCT_OPTION_IMP_CRIT = 16384; +const int ASTRAL_CONSTRUCT_OPTION_IMP_DAM_RED = 32768; +const int ASTRAL_CONSTRUCT_OPTION_MUSCLE = 65536; +const int ASTRAL_CONSTRUCT_OPTION_POISON_TOUCH = 131072; +const int ASTRAL_CONSTRUCT_OPTION_BLINDFIGHT = 262144; +const int ASTRAL_CONSTRUCT_OPTION_CONCUSSION = 524288; +const int ASTRAL_CONSTRUCT_OPTION_DIMENSION_SLIDE = 1048576; +const int ASTRAL_CONSTRUCT_OPTION_ENERGY_BOLT = 2097152; +const int ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF = 4194304; +const int ASTRAL_CONSTRUCT_OPTION_EXTREME_DAM_RED = 8388608; +const int ASTRAL_CONSTRUCT_OPTION_EXTREME_DEFLECT = 16777216; +const int ASTRAL_CONSTRUCT_OPTION_NATURAL_INVIS = 33554432; +const int ASTRAL_CONSTRUCT_OPTION_POWER_RESIST = 67108864; +const int ASTRAL_CONSTRUCT_OPTION_REND = 134217728; +const int ASTRAL_CONSTRUCT_OPTION_SPRING_ATTACK = 268435456; +const int ASTRAL_CONSTRUCT_OPTION_WHIRLWIND = 536870912; + + +const int ELEMENT_ACID = 1; +const int ELEMENT_COLD = 2; +const int ELEMENT_ELECTRICITY = 4; +const int ELEMENT_FIRE = 8; +const int ELEMENT_SONIC = 16; + + +const int TEST_FLAG = 1; + +const int MENU_A_COST = 1; +const int MENU_B_COST = 2; +const int MENU_C_COST = 4; + +const int MENU_A_MASK = 511; +const int MENU_B_MASK = 261632; +const int MENU_C_MASK = 1073479680; + + +const int AC_APPEARANCE_CHECK_LOW = 10; +const int AC_APPEARANCE_CHECK_MEDIUM = 20; +const int AC_APPEARANCE_CHECK_HIGH = 30; + + +// For convenience. Compiler will complain if these are typoed :D +const string ASTRAL_CONSTRUCT_LEVEL = "AstralConstructLevel"; +const string ASTRAL_CONSTRUCT_OPTION_FLAGS = "AstralConstructOptionFlags"; +const string ASTRAL_CONSTRUCT_RESISTANCE_FLAGS = "AstralConstructResistanceFlags"; +const string ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS = "AstralConstructEnergyTouchFlags"; +const string ASTRAL_CONSTRUCT_ENERGY_BOLT_FLAGS = "AstralConstructEnergyBoltFlags"; +const string ASTRAL_CONSTRUCT_POISON_TOUCH = "AstralConstructPoisonTouch"; +const string ASTRAL_CONSTRUCT_CONCUSSION = "AstralConstructConcussion"; + +const string ASTRAL_CONSTRUCT_CLAW = "psi_ast_con_claw"; +const string ASTRAL_CONSTRUCT_SLAM = "psi_ast_con_slam"; + +const string CURRENT_SLOT = "CurrentAstralConstructSlot"; +const string EDIT = "Edit"; + +const string MANIFESTED_SLOT = "ManifestedAstralConstructSlot"; + + +// Strref constants +const int STRREF_INVALID_CONSTRUCT_IN_SLOT = 16824742; diff --git a/src/include/psi_inc_ac_convo.nss b/src/include/psi_inc_ac_convo.nss new file mode 100644 index 0000000..f34c501 --- /dev/null +++ b/src/include/psi_inc_ac_convo.nss @@ -0,0 +1,424 @@ +//:://///////////////////////////////////////////// +//:: Astral Construct conversation include +//:: psi_inc_ac_convo +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 25.01.2005 +//::////////////////////////////////////////////// + +#include "psi_inc_ac_const" +#include "prc_feat_const" + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + + +int GetNumberOfFlagsRaised(int nFlagSet); +int GetTotalNumberOfSlotsUsed(object oPC); +int GetMaxFlagsForLevel(int nLevel); +int GetHasBuff(int nFlags); + +// Custom token stuff +string GetSizeAsString(int nLevel); +string GetHPAsString(int nLevel, int nFlags); +string GetSpeedAsString(int nLevel, int nFlags); +string StringAdder(string sOriginal, string sAdd, int bFirst); +string GetMenuASelectionsAsString(object oPC); +string GetMenuBSelectionsAsString(object oPC); +string GetMenuCSelectionsAsString(object oPC); + + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + + +int GetNumberOfFlagsRaised(int nFlagSet) +{ + int i, nReturn = 0; + + for(i = 0; i < 32; i++) + { + if((nFlagSet >>> i) & TEST_FLAG) + nReturn++; + } + + return nReturn; +} + + + +int GetTotalNumberOfSlotsUsed(object oPC) +{ + int nFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_OPTION_FLAGS + EDIT); + + int nFlagTotal = 0; + + // Handle Menu A flags. Each flag costs 1 slot + nFlagTotal += MENU_A_COST * GetNumberOfFlagsRaised(nFlags & MENU_A_MASK); + if(nFlags & ASTRAL_CONSTRUCT_OPTION_RESISTANCE) + { + nFlagTotal -= MENU_A_COST; //We don't want to count the flag twice + nFlagTotal += MENU_A_COST * GetNumberOfFlagsRaised(GetLocalInt(oPC, ASTRAL_CONSTRUCT_RESISTANCE_FLAGS + EDIT)); + } + + // Handle Menu B flags. Each flag costs 2 slots + nFlagTotal += MENU_B_COST * GetNumberOfFlagsRaised(nFlags & MENU_B_MASK); + if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_TOUCH) + { + nFlagTotal -= MENU_B_COST; //We don't want to count the flag twice + nFlagTotal += MENU_B_COST * GetNumberOfFlagsRaised(GetLocalInt(oPC, ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS + EDIT)); + } + + // Handle Menu C flags. Each flag costs 4 slots + nFlagTotal += MENU_C_COST * GetNumberOfFlagsRaised(nFlags & MENU_C_MASK); + + + return nFlagTotal; +} + + +int GetMaxSlotsForLevel(int nLevel, object oManifester) +{ + int multiplier = 1; + + if (GetHasFeat(FEAT_BOOST_CONSTRUCT, oManifester)) + { + multiplier = 2; + } + + switch(nLevel) + { + case 1: case 2: case 3: + return 1 * multiplier; + case 4: case 5: case 6: + return 2 * multiplier; + case 7: case 8: case 9: + return 4 * multiplier; + + default: + WriteTimestampedLogEntry("Invalid nLevel value passed to GetMaxFlagsForLevel"); + } + + return 0; +} + + + +int GetHasBuff(int nFlags) +{ + if((nFlags & ASTRAL_CONSTRUCT_OPTION_BUFF) || + (nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF) || + (nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF)) + return TRUE; + else + return FALSE; +} + + +string GetSizeAsString(int nLevel) +{ + switch(nLevel) + { + case 1: + return "Small"; + case 2: case 3: case 4: + return "Medium"; + case 5: case 6: case 7: case 8: + return "Large"; + case 9: + return "Huge"; + + default: + WriteTimestampedLogEntry("Invalid nLevel value passed to GetSizeAsString"); + } + + return "ERROR!"; +} + + +string GetHPAsString(int nLevel, int nFlags) +{ + int nBaseHP; + + switch(nLevel) + { + case 1: nBaseHP = 15; break; + case 2: nBaseHP = 31; break; + case 3: nBaseHP = 36; break; + case 4: nBaseHP = 47; break; + case 5: nBaseHP = 68; break; + case 6: nBaseHP = 85; break; + case 7: nBaseHP = 101; break; + case 8: nBaseHP = 118; break; + case 9: nBaseHP = 144; break; + + default: + WriteTimestampedLogEntry("Invalid nLevel value passed to GetHPAsString"); + return "ERROR!"; + } + + if(nFlags & ASTRAL_CONSTRUCT_OPTION_BUFF) + nBaseHP += 5; + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF) + nBaseHP += 15; + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF) + nBaseHP += 30; + + return IntToString(nBaseHP); +} + + +string GetSpeedAsString(int nLevel, int nFlags) +{ + int nSpeed; + switch(nLevel) + { + case 1: + nSpeed = 30; + break; + case 2: case 3: case 4: + case 5: case 6: case 7: case 8: + nSpeed = 40; + break; + case 9: + nSpeed = 50; + break; + + default: + WriteTimestampedLogEntry("Invalid nLevel value passed to GetSizeAsString"); + return "ERROR!"; + } + + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CELERITY) + nSpeed += 10; + + return IntToString(nSpeed); +} + + +string StringAdder(string sOriginal, string sAdd, int bFirst) +{ + if(bFirst) return sOriginal + sAdd; + else return sOriginal + ", " + sAdd; +} + + +string GetMenuASelectionsAsString(object oPC) +{ + string sReturn = ""; + int bFirst = TRUE; + + int nFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_OPTION_FLAGS + EDIT); + + if(nFlags & ASTRAL_CONSTRUCT_OPTION_BUFF){ + sReturn = StringAdder(sReturn, "Buff", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CELERITY){ + sReturn = StringAdder(sReturn, "Celerity", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CLEAVE){ + sReturn = StringAdder(sReturn, "Cleave", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMPROVED_SLAM){ + sReturn = StringAdder(sReturn, "Improved Slam Attack", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_DEFLECTION){ + sReturn = StringAdder(sReturn, "Deflection", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_MOBILITY){ + sReturn = StringAdder(sReturn, "Mobility", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POWER_ATTACK){ + sReturn = StringAdder(sReturn, "Power Attack", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_RESISTANCE){ + int nElemFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_RESISTANCE_FLAGS + EDIT); + + if(nElemFlags & ELEMENT_ACID){ + sReturn = StringAdder(sReturn, "Resistance - Acid", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_COLD){ + sReturn = StringAdder(sReturn, "Resistance - Cold", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_ELECTRICITY){ + sReturn = StringAdder(sReturn, "Resistance - Electricity", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_FIRE){ + sReturn = StringAdder(sReturn, "Resistance - Fire", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_SONIC){ + sReturn = StringAdder(sReturn, "Resistance - Sonic", bFirst); + bFirst = FALSE; + } + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_KNOCKDOWN){ + sReturn = StringAdder(sReturn, "Knockdown", bFirst); + bFirst = FALSE; + } + + return sReturn; +} + + +string GetMenuBSelectionsAsString(object oPC) +{ + string sReturn = ""; + int bFirst = TRUE; + + int nFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_OPTION_FLAGS + EDIT); + + if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_TOUCH){ + int nElemFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS + EDIT); + + if(nElemFlags & ELEMENT_ACID){ + sReturn = StringAdder(sReturn, "Energy Touch - Acid", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_COLD){ + sReturn = StringAdder(sReturn, "Energy Touch - Cold", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_ELECTRICITY){ + sReturn = StringAdder(sReturn, "Energy Touch - Electricity", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_FIRE){ + sReturn = StringAdder(sReturn, "Energy Touch - Fire", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_SONIC){ + sReturn = StringAdder(sReturn, "Energy Touch - Sonic", bFirst); + bFirst = FALSE; + } + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_ATTACK){ + sReturn = StringAdder(sReturn, "Extra Attack", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_FAST_HEALING){ + sReturn = StringAdder(sReturn, "Fast Healing", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_HEAVY_DEFLECT){ + sReturn = StringAdder(sReturn, "Heavy Deflection", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF){ + sReturn = StringAdder(sReturn, "Improved Buff", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_CRIT){ + sReturn = StringAdder(sReturn, "Improved Critical", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_DAM_RED){ + sReturn = StringAdder(sReturn, "Improved Damage Reduction", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_MUSCLE){ + sReturn = StringAdder(sReturn, "Muscle", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POISON_TOUCH){ + sReturn = StringAdder(sReturn, "Poison Touch", bFirst); + bFirst = FALSE; + } + + + return sReturn; +} + + +string GetMenuCSelectionsAsString(object oPC) +{ + string sReturn = ""; + int bFirst = TRUE; + + int nFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_OPTION_FLAGS + EDIT); + + if(nFlags & ASTRAL_CONSTRUCT_OPTION_BLINDFIGHT){ + sReturn = StringAdder(sReturn, "Blindfight", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CONCUSSION){ + sReturn = StringAdder(sReturn, "Concussion", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_DIMENSION_SLIDE){ + sReturn = StringAdder(sReturn, "Dimension Slide", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_BOLT){ + int nElemFlags = GetLocalInt(oPC, ASTRAL_CONSTRUCT_ENERGY_BOLT_FLAGS + EDIT); + + if(nElemFlags & ELEMENT_ACID){ + sReturn = StringAdder(sReturn, "Energy Bolt - Acid", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_COLD){ + sReturn = StringAdder(sReturn, "Energy Bolt - Cold", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_ELECTRICITY){ + sReturn = StringAdder(sReturn, "Energy Bolt - Electricity", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_FIRE){ + sReturn = StringAdder(sReturn, "Energy Bolt - Fire", bFirst); + bFirst = FALSE; + } + if(nElemFlags & ELEMENT_SONIC){ + sReturn = StringAdder(sReturn, "Energy Bolt - Sonic", bFirst); + bFirst = FALSE; + } + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF){ + sReturn = StringAdder(sReturn, "Extra Buff", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DAM_RED){ + sReturn = StringAdder(sReturn, "Extreme Damage Reduction", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DEFLECT){ + sReturn = StringAdder(sReturn, "Extreme Deflection", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_NATURAL_INVIS){ + sReturn = StringAdder(sReturn, "Natural Invisibility", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POWER_RESIST){ + sReturn = StringAdder(sReturn, "Power Resistance", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_REND){ + sReturn = StringAdder(sReturn, "Rend", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_SPRING_ATTACK){ + sReturn = StringAdder(sReturn, "Spring Attack", bFirst); + bFirst = FALSE; + } + if(nFlags & ASTRAL_CONSTRUCT_OPTION_WHIRLWIND){ + sReturn = StringAdder(sReturn, "Whirlwind", bFirst); + bFirst = FALSE; + } + + return sReturn; +} \ No newline at end of file diff --git a/src/include/psi_inc_ac_manif.nss b/src/include/psi_inc_ac_manif.nss new file mode 100644 index 0000000..db7bc45 --- /dev/null +++ b/src/include/psi_inc_ac_manif.nss @@ -0,0 +1,410 @@ +//:://///////////////////////////////////////////// +//:: Astral Construct manifestation include +//:: psi_inc_ac_manif +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 23.01.2005 +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "psi_inc_psifunc" +#include "psi_inc_ac_const" +#include "inc_utility" + + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +const int TEMP_HENCH_COUNT = 150; + + +////////////////////////////////////////////////// +/* Structure definitions */ +////////////////////////////////////////////////// + +// A structure containing appearance references +struct ac_forms{ + int Appearance1, Appearance1Alt; + int Appearance2, Appearance2Alt; + int Appearance3, Appearance3Alt; + int Appearance4, Appearance4Alt; +}; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +void DoAstralConstructCreation(struct manifestation manif, location locTarget, int nACLevel, + int nOptionFlags, int nResElemFlags, int nETchElemFlags, int nEBltElemFlags); + +void DoDespawn(object oConstruct, int bDoVFX = TRUE); +void DoDespawnAux(object oManifester, float fDur); +void DoSummonVFX(location locTarget, int nACLevel); +void DoUnsummonVFX(location locTarget, int nACLevel); + +struct ac_forms GetAppearancessForLevel(int nLevel); +int GetAppearanceForConstruct(int nACLevel, int nOptionFlags, int nCheck); +int GetUseAltAppearances(int nOptionFlags); +string GetResRefForConstruct(int nACLevel, int nOptionFlags); +int GetHighestCraftSkillValue(object oCreature); + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +// Summons the specified Astral Construct at the given location +// Handling of the flags (other than the Buff series) is done +// in the creature's OnSpawn eventscript +void DoAstralConstructCreation(struct manifestation manif, location locTarget, int nACLevel, + int nOptionFlags, int nResElemFlags, int nETchElemFlags, int nEBltElemFlags) +{ + // Get the resref for the AC + string sResRef = GetResRefForConstruct(nACLevel, nOptionFlags); + + // Get the constructs duration. 1 round / level. Metapsionic Extend can be applied. + float fDur = 6.0 * GetManifesterLevel(manif.oManifester); + if(GetPRCSwitch(PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD) > 0) + fDur *= GetPRCSwitch(PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD); + if(manif.bExtend) + fDur *= 2; + + // Add in 1 round of duration to account for AI disorientation in the beginning + fDur += 6.0f; + + /* Until Bioware "fixes" Jasperre's multisummon trick, AC are added as genuine summons instead of henchies + // We need to make sure that we can add the new construct as henchman + int nMaxHenchmen = GetMaxHenchmen(); + SetMaxHenchmen(TEMP_HENCH_COUNT); + + // Add the AC as henchman + object oConstruct = CreateObject(OBJECT_TYPE_CREATURE, sResRef, locTarget); + AddHenchman(oManifester, oConstruct); + + // And set the max henchmen count back to original, so we won't mess up the module + SetMaxHenchmen(nMaxHenchmen); + + */ + + + // Do multisummon trick + int bMultisummon = GetPRCSwitch(PRC_MULTISUMMON); + int i = 1; + object oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, manif.oManifester, i); + while(GetIsObjectValid(oCheck)) + { + //DoDebug("DoAstralConstructCreation: Handling associate " + DebugObject2Str(oCheck)); + // If multisummon is active, make all summons indestructible. If not, only make astral constructs + if(bMultisummon || GetStringLeft(GetTag(oCheck), 14) == "psi_astral_con") + { + AssignCommand(oCheck, SetIsDestroyable(FALSE, FALSE, FALSE)); + AssignCommand(oCheck, DelayCommand(1.0, SetIsDestroyable(TRUE, FALSE, FALSE))); + oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, manif.oManifester, ++i); + } + } + + // Do actual summon effect + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectSummonCreature(sResRef), locTarget, fDur + 0.5); + + /* For use if need to return to henchman setup + + // Add the locals to the construct + SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_LEVEL, nACLevel); + SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_OPTION_FLAGS, nOptionFlags); + SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_RESISTANCE_FLAGS, nResElemFlags); + SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS, nETchElemFlags); + SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_ENERGY_BOLT_FLAGS, nEBltElemFlags); + + + // Do appearance switching + int nCraft = GetHighestCraftSkillValue(oManifester); + int nCheck = d20() + nCraft; + + int nAppearance = GetAppearanceForConstruct(nACLevel, nOptionFlags, nCheck); + SetCreatureAppearanceType(oConstruct, nAppearance); + */ + + // Do VFX + DoSummonVFX(locTarget, nACLevel); + + // Schedule unsummoning. No need to hurry this one, so give it a larger delay + // in order to avoid hogging too much resources over a short span of time. + DelayCommand(2.0, DoDespawnAux(manif.oManifester, fDur)); +} + + +// A function to handle the AC's duration running out +// Some paranoia present to make sure nothing could accidentally +// make it permanent +void DoDespawn(object oConstruct, int bDoVFX = TRUE) +{ + if(GetIsObjectValid(oConstruct)){ + DestroyObject(oConstruct); + DelayCommand(0.15f, MyDestroyObject(oConstruct)); // The paranoia bit :D + if(bDoVFX) DoUnsummonVFX(GetLocation(oConstruct), GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_LEVEL)); + } +} + +// An auxiliary to be delayed so that a reference to the just created AC can be found +void DoDespawnAux(object oManifester, float fDur){ + // Find the newly added construct + object oConstruct = OBJECT_INVALID; + int i = 1; + object oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oManifester, i); + while(GetIsObjectValid(oCheck)) + { + if(!GetLocalInt(oCheck, "UnsummonScheduled") && GetStringLeft(GetTag(oCheck), 14) == "psi_astral_con") + { + oConstruct = oCheck; + break; + } + i++; + oCheck = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oManifester, i); + } + SetLocalInt(oConstruct, "UnsummonScheduled", TRUE); + + if(DEBUG) DoDebug("Found the just added astral construct: " + (GetIsObjectValid(oConstruct) ? "true":"false") + "\nSummon name: " + GetName(oConstruct) + "\nTotal summons: " + IntToString(i), oManifester); + + // Schedule unsummoning. Done this way to skip the default unsummoning VFX. + DelayCommand(fDur - 2.0, DoDespawn(oConstruct)); +} + +// Does a visual choreography that depends on the level of the construct being created. +// Higher level constructs get neater VFX :D +void DoSummonVFX(location locTarget, int nACLevel){ + DrawSpiral(0, 263, locTarget, IntToFloat(nACLevel) * 0.75 + 0.5, 0.4, 1.0, 60, 16.899999619, 3.0, 4.0); +} + +void DoUnsummonVFX(location locTarget, int nACLevel){ + DrawSpiral(0, 263, locTarget, 0.4, IntToFloat(nACLevel) * 0.75 + 0.5, 1.0, 60, 16.899999619, 3.0, 4.0); +} + + +struct ac_forms GetAppearancesForLevel(int nLevel) +{ + struct ac_forms toReturn; + + switch(nLevel) + { + case 1: + toReturn.Appearance1 = APPEARANCE_TYPE_RAT; + toReturn.Appearance1Alt = 387; //Dire Rat + + toReturn.Appearance2 = APPEARANCE_TYPE_INTELLECT_DEVOURER; + toReturn.Appearance2Alt = APPEARANCE_TYPE_WAR_DEVOURER; + + toReturn.Appearance3 = APPEARANCE_TYPE_PSEUDODRAGON; + toReturn.Appearance3Alt = APPEARANCE_TYPE_PSEUDODRAGON; + + toReturn.Appearance4 = APPEARANCE_TYPE_FAERIE_DRAGON; + toReturn.Appearance4Alt = APPEARANCE_TYPE_FAERIE_DRAGON; + + return toReturn; + case 2: + toReturn.Appearance1 = APPEARANCE_TYPE_GARGOYLE; + toReturn.Appearance1Alt = APPEARANCE_TYPE_GARGOYLE; + + toReturn.Appearance2 = APPEARANCE_TYPE_BAT_HORROR; + toReturn.Appearance2Alt = APPEARANCE_TYPE_HELMED_HORROR; + + toReturn.Appearance3 = APPEARANCE_TYPE_ASABI_WARRIOR; + toReturn.Appearance3Alt = APPEARANCE_TYPE_LIZARDFOLK_WARRIOR_B; + + toReturn.Appearance4 = APPEARANCE_TYPE_WERECAT; + toReturn.Appearance4Alt = APPEARANCE_TYPE_WERECAT; + + return toReturn; + case 3: + toReturn.Appearance1 = APPEARANCE_TYPE_FORMIAN_WORKER; + toReturn.Appearance1Alt = APPEARANCE_TYPE_FORMIAN_WORKER; + + toReturn.Appearance2 = APPEARANCE_TYPE_FORMIAN_WARRIOR; + toReturn.Appearance2Alt = APPEARANCE_TYPE_FORMIAN_WARRIOR; + + toReturn.Appearance3 = APPEARANCE_TYPE_FORMIAN_MYRMARCH; + toReturn.Appearance3Alt = APPEARANCE_TYPE_FORMIAN_MYRMARCH; + + toReturn.Appearance4 = APPEARANCE_TYPE_FORMIAN_QUEEN; + toReturn.Appearance4Alt = APPEARANCE_TYPE_FORMIAN_QUEEN; + + return toReturn; + case 4: + toReturn.Appearance1 = 416; // Deep Rothe + toReturn.Appearance1Alt = 416; + + toReturn.Appearance2 = APPEARANCE_TYPE_MANTICORE; + toReturn.Appearance2Alt = APPEARANCE_TYPE_MANTICORE; + + toReturn.Appearance3 = APPEARANCE_TYPE_BASILISK; + toReturn.Appearance3Alt = APPEARANCE_TYPE_GORGON; + + toReturn.Appearance4 = APPEARANCE_TYPE_DEVIL; + toReturn.Appearance4Alt = 468; // Golem, Demonflesh + + return toReturn; + case 5: + toReturn.Appearance1 = APPEARANCE_TYPE_GOLEM_FLESH; + toReturn.Appearance1Alt = APPEARANCE_TYPE_GOLEM_FLESH; + + toReturn.Appearance2 = APPEARANCE_TYPE_GOLEM_STONE; + toReturn.Appearance2Alt = APPEARANCE_TYPE_GOLEM_STONE; + + toReturn.Appearance3 = APPEARANCE_TYPE_GOLEM_CLAY; + toReturn.Appearance3Alt = APPEARANCE_TYPE_GOLEM_CLAY; + + toReturn.Appearance4 = 420; // Golem, Mithril + toReturn.Appearance4Alt = 420; + + return toReturn; + case 6: + toReturn.Appearance1 = APPEARANCE_TYPE_TROLL; + toReturn.Appearance1Alt = APPEARANCE_TYPE_TROLL; + + toReturn.Appearance2 = APPEARANCE_TYPE_ETTERCAP; + toReturn.Appearance2Alt = APPEARANCE_TYPE_ETTERCAP; + + toReturn.Appearance3 = APPEARANCE_TYPE_UMBERHULK; + toReturn.Appearance3Alt = APPEARANCE_TYPE_UMBERHULK; + + toReturn.Appearance4 = APPEARANCE_TYPE_MINOTAUR_SHAMAN; + toReturn.Appearance4Alt = APPEARANCE_TYPE_MINOGON; + + return toReturn; + case 7: + toReturn.Appearance1 = APPEARANCE_TYPE_SPIDER_DIRE; + toReturn.Appearance1Alt = APPEARANCE_TYPE_SPIDER_DIRE; + + toReturn.Appearance2 = APPEARANCE_TYPE_SPIDER_SWORD; + toReturn.Appearance2Alt = APPEARANCE_TYPE_SPIDER_SWORD; + + toReturn.Appearance3 = 446; // Drider, Female + toReturn.Appearance3Alt = 446; + + toReturn.Appearance4 = 407; // Drider, Chief + toReturn.Appearance4Alt = 407; + + return toReturn; + case 8: + toReturn.Appearance1 = APPEARANCE_TYPE_HOOK_HORROR; + toReturn.Appearance1Alt = APPEARANCE_TYPE_VROCK; + + toReturn.Appearance2 = 427; // Slaad, White + toReturn.Appearance2Alt = 427; + + toReturn.Appearance3 = APPEARANCE_TYPE_GREY_RENDER; + toReturn.Appearance3Alt = APPEARANCE_TYPE_GREY_RENDER; + + toReturn.Appearance4 = APPEARANCE_TYPE_GREY_RENDER; + toReturn.Appearance4Alt = APPEARANCE_TYPE_GREY_RENDER; + + return toReturn; + case 9: + toReturn.Appearance1 = APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER; + toReturn.Appearance1Alt = APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER; + + toReturn.Appearance2 = APPEARANCE_TYPE_GIANT_FROST_FEMALE; + toReturn.Appearance2Alt = APPEARANCE_TYPE_GIANT_FROST_FEMALE; + + toReturn.Appearance3 = 418; // Dragon, Shadow + toReturn.Appearance3Alt = 418; + + toReturn.Appearance4 = 471; // Mephisto, Normal + toReturn.Appearance4Alt = 471; + + return toReturn; + + default:{ + string sErr = "psi_inc_ac_manif: GetAppearancesForLevel(): ERROR: Erroneous value for nLevel: " + IntToString(nLevel); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return toReturn; +} + + +int GetAppearanceForConstruct(int nACLevel, int nOptionFlags, int nCheck) +{ + int bUse2da = GetPRCSwitch(PRC_PSI_ASTRAL_CONSTRUCT_USE_2DA); + int bUseAlt = GetUseAltAppearances(nOptionFlags); + int nNum = nCheck < AC_APPEARANCE_CHECK_HIGH ? + nCheck < AC_APPEARANCE_CHECK_MEDIUM ? + nCheck < AC_APPEARANCE_CHECK_LOW ? 1 + : 2 + : 3 + : 4; + // If we use 2da, get the data from there + if(bUse2da) + { + nNum += (nACLevel - 1) * 4 - 1; + + return StringToInt(Get2DACache("ac_appearances", bUseAlt ? "AltAppearance" : "NormalAppearance", nNum)); + } + + // We don't so get it from GetAppearancesForLevel + + struct ac_forms appearancelist = GetAppearancesForLevel(nACLevel); + + switch(nNum) + { + case 1: return bUseAlt ? appearancelist.Appearance1Alt : appearancelist.Appearance1; + case 2: return bUseAlt ? appearancelist.Appearance2Alt : appearancelist.Appearance2; + case 3: return bUseAlt ? appearancelist.Appearance3Alt : appearancelist.Appearance3; + case 4: return bUseAlt ? appearancelist.Appearance4Alt : appearancelist.Appearance4; + + default:{ + string sErr = "psi_inc_ac_manif: GetAppearanceForConstruct(): ERROR: Erroneous value for nNum: " + IntToString(nNum); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return -1; +} + + +int GetUseAltAppearances(int nOptionFlags) +{ // Buff series + return nOptionFlags & ASTRAL_CONSTRUCT_OPTION_BUFF || + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF || + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF || + // Deflection series + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_DEFLECTION || + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_HEAVY_DEFLECT || + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DEFLECT || + // Damage Reduction Series + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_IMP_DAM_RED || + nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DAM_RED; +} + + +string GetResRefForConstruct(int nACLevel, int nOptionFlags) +{ + string sResRef = "psi_astral_con" + IntToString(nACLevel); + string sSuffix; + + // Check whether we need a resref with buff applied + if(nOptionFlags & ASTRAL_CONSTRUCT_OPTION_BUFF) + sSuffix += "a"; + else if(nOptionFlags & ASTRAL_CONSTRUCT_OPTION_IMP_BUFF) + sSuffix += "b"; + else if(nOptionFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_BUFF) + sSuffix += "c"; + + return sResRef + sSuffix; +} + + +int GetHighestCraftSkillValue(object oCreature) +{ + int nArmor = GetSkillRank(SKILL_CRAFT_ARMOR, oCreature); + int nTrap = GetSkillRank(SKILL_CRAFT_TRAP, oCreature); + int nWeapon = GetSkillRank(SKILL_CRAFT_WEAPON, oCreature); + + return nArmor > nTrap ? + nArmor > nWeapon ? nArmor : nWeapon + : nTrap > nWeapon ? nTrap : nWeapon; +} diff --git a/src/include/psi_inc_ac_spawn.nss b/src/include/psi_inc_ac_spawn.nss new file mode 100644 index 0000000..5b3947e --- /dev/null +++ b/src/include/psi_inc_ac_spawn.nss @@ -0,0 +1,528 @@ +//:://///////////////////////////////////////////// +//:: Astral Construct spawn include +//:: psi_inc_ac_spawn +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 21.01.2005 +//::////////////////////////////////////////////// + +#include "psi_inc_ac_const" +#include "prc_ipfeat_const" +#include "prc_feat_const" +#include "inc_vfx_const" + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// +void HandleAstralConstructSpawn(object oConstruct); +void DoEffect(object oConstruct, effect eEffect); +void DoFeat(object oHide, int nIPFeatID); +void DoDeflect(object oConstruct, int nACVal); +void DoResistance(object oHide, int nElementFlags); +void DoEnergyTouch(object oWeapon, int nElementFlags, object oCreator); +int GetConstructSizeFromTag(object oConstruct); +int GetBaseDamageFromSize(int nSize); +int GetNextBaseDamage(int nDamVal); +int GetDamageReduction(object oConstruct); +int GetDamageReductionConstantFromAmount(int nDamRed); + + +//void ActionUseInstantPower( invalid ); + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +// Applies the chosen Astral Construct options to the creature. +// +// Note! Buff series is handled separately +void HandleAstralConstructSpawn(object oConstruct) +{ + // Get the flag set + int nFlags = GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_OPTION_FLAGS); + // Get the construct's size and use it to determine the base attack damage + int nSize = GetConstructSizeFromTag(oConstruct); + + if (nSize < CREATURE_SIZE_SMALL) + { + WriteTimestampedLogEntry("Invalid construct size: " + IntToString(nSize) + " for appearance " + IntToString(GetAppearanceType(oConstruct))); + } + + if (DEBUG) WriteTimestampedLogEntry("Creating construct " + GetTag(oConstruct) + " of size " + IntToString(nSize)); + + string sWeaponResRef = ASTRAL_CONSTRUCT_SLAM; + object oHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oConstruct); + + /* Check through the flags and take appropriate action */ + + /// The Deflect series + int nDeflect = 0; + // Add +1 deflection AC + if(nFlags & ASTRAL_CONSTRUCT_OPTION_DEFLECTION) { nDeflect += 1; } + // Add +4 deflection AC + if(nFlags & ASTRAL_CONSTRUCT_OPTION_HEAVY_DEFLECT) { nDeflect += 4; } + // Add +8 deflection AC + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DEFLECT) { nDeflect += 8; } + + if(nDeflect) DoDeflect(oConstruct, nDeflect); + + if (DEBUG) WriteTimestampedLogEntry(" Construct deflection AC: " + IntToString(nDeflect)); + + /// The Damage reductions + // First, get the base damage reduction + int nDamRed = GetDamageReduction(oConstruct); + // Increases the damage reduction variable by 3 + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_DAM_RED) { nDamRed += 3; } + // Increases the damage reduction variable by 6 + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTREME_DAM_RED) { nDamRed += 6; } + + if (DEBUG) WriteTimestampedLogEntry(" Construct damage reduction: " + IntToString(nDamRed)); + + if(nDamRed) + { + nDamRed = GetDamageReductionConstantFromAmount(nDamRed); + itemproperty ipDamRed = ItemPropertyDamageReduction(IP_CONST_DAMAGEREDUCTION_1, nDamRed); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamRed, oHide); + } + + /// Add various feats to the hide + // Add the Cleave feat to slam + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CLEAVE) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Cleave"); + DoFeat(oHide, IP_CONST_FEAT_CLEAVE); + } + // Add the Mobility feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_MOBILITY) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Mobility"); + DoFeat(oHide, IP_CONST_FEAT_MOBILITY); + } + // Add the Power Attack feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POWER_ATTACK) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Power Attack"); + DoFeat(oHide, IP_CONST_FEAT_POWERATTACK); + } + // Add the Knockdown feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_KNOCKDOWN) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Knockdown"); + DoFeat(oHide, IP_CONST_FEAT_KNOCKDOWN); + } + // Add the Improved Critical (Creature) feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMP_CRIT) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Improved Critical"); + DoFeat(oHide, IP_CONST_FEAT_ImpCritCreature); + } + // Add the Blindsight feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_BLINDFIGHT) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Blind Fight"); + DoFeat(oHide, IP_CONST_FEAT_BLINDFIGHT); + } + // Add feat for spell/power resistance 10+HD to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POWER_RESIST) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Power Resistance"); + DoFeat(oHide, IP_CONST_FEAT_SPELL10); + } + // Add the Rend feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_REND) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Rend"); + DoFeat(oHide, IP_CONST_FEAT_REND); + sWeaponResRef = ASTRAL_CONSTRUCT_CLAW; + } + // Add the Spring Attack feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_SPRING_ATTACK) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Spring Attack"); + DoFeat(oHide, IP_CONST_FEAT_SPRINGATTACK); + } + // Add the Whirlwind feat to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_WHIRLWIND) + { + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Whirlwind"); + DoFeat(oHide, IP_CONST_FEAT_WHIRLWIND); + } + + + /// Add various itemproperties to the hide + // Adds regeneration 2 to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_FAST_HEALING) + { + itemproperty ipRegen = ItemPropertyRegeneration(2); + AddItemProperty(DURATION_TYPE_PERMANENT, ipRegen, oHide); + if (DEBUG) WriteTimestampedLogEntry(" Added fast healing 2"); + } + // Give a +4 STR bonus to hide + if(nFlags & ASTRAL_CONSTRUCT_OPTION_MUSCLE) + { + itemproperty ipSTR = ItemPropertyAbilityBonus(IP_CONST_ABILITY_STR, 4); + AddItemProperty(DURATION_TYPE_PERMANENT, ipSTR, oHide); + if (DEBUG) WriteTimestampedLogEntry(" Added muscle (+4 STR)"); + } + // Add energy resistance 5 to chosen elements + if(nFlags & ASTRAL_CONSTRUCT_OPTION_RESISTANCE) + { + DoResistance(oHide, GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_RESISTANCE_FLAGS)); + if (DEBUG) WriteTimestampedLogEntry(" Added energy resistance 5"); + } + + /// Handle effects + + // Add the transparency effect + effect eVis = EffectVisualEffect(VFX_DUR_GHOSTLY_PULSE_QUICK);//VFX_DUR_GHOST_TRANSPARENT); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eVis, oConstruct); + + // Increase speed by 10 feet per round + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CELERITY) + { + int nSpeedIncrease; + switch(nSize) + { + case CREATURE_SIZE_SMALL: + nSpeedIncrease = 33; + break; + case CREATURE_SIZE_MEDIUM: + case CREATURE_SIZE_LARGE: + nSpeedIncrease = 25; + break; + case CREATURE_SIZE_HUGE: + nSpeedIncrease = 20; + break; + default: + if (DEBUG) WriteTimestampedLogEntry("Invalid size value for an Astral Construct encountered when processing Celerity"); + nSpeedIncrease = 0; + break; + } + + effect eSpeed = EffectMovementSpeedIncrease(nSpeedIncrease); + DoEffect(oConstruct, eSpeed); + if (DEBUG) WriteTimestampedLogEntry(" Added celerity (" + IntToString(nSpeedIncrease) + "% increase)"); + } + // Add one attack + if(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_ATTACK) + { + effect eAttack = EffectModifyAttacks(1); + DoEffect(oConstruct, eAttack); + if (DEBUG) WriteTimestampedLogEntry(" Added extra attack"); + } + // Applies 50% concealement and a normal invisibility effect to the construct to + // simulate permanent invisibility + if(nFlags & ASTRAL_CONSTRUCT_OPTION_NATURAL_INVIS) + { + effect eConceal = EffectConcealment(50); + effect eInvis = EffectInvisibility(INVISIBILITY_TYPE_NORMAL); + DoEffect(oConstruct, eConceal); + DoEffect(oConstruct, eInvis); + if (DEBUG) WriteTimestampedLogEntry(" Added natural invisibility"); + } + + + + + /* Start handling the creature weapon stuff */ + + // First, actually create the weapon and equip it + object oWeapon = CreateItemOnObject(sWeaponResRef, oConstruct); + AssignCommand(oConstruct, ActionEquipItem(oWeapon, INVENTORY_SLOT_CWEAPON_B)); + + /// Handle the damage that it will deal + + // Get the base damage + int nAttackBaseDamage = GetBaseDamageFromSize(nSize); + + if (DEBUG) WriteTimestampedLogEntry(" Set base damage to " + IntToString(nAttackBaseDamage)); + // Check if the damage needs to be increased + if(nFlags & ASTRAL_CONSTRUCT_OPTION_IMPROVED_SLAM) + { + nAttackBaseDamage = GetNextBaseDamage(nAttackBaseDamage); + if (DEBUG) WriteTimestampedLogEntry(" Added feat: Improved Slam"); + } + + // Apply the monster damage iprop to the weapon + itemproperty ipDam = ItemPropertyMonsterDamage(nAttackBaseDamage); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDam, oWeapon); + + // Add damage bonus equal to 0.5 times the creature's strength modifier. + // The Astral Constructs should have it since they only have one natural attack (and no other attacks) + int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oConstruct) / 2; + nStrBon = nStrBon < 0 ? 0 : nStrBon; + nStrBon = nStrBon > 10 ? 10: nStrBon; + + // The Extra Attacks modifier cancels 1.5x STR bonus, so check for it's presence + if(nStrBon && !(nFlags & ASTRAL_CONSTRUCT_OPTION_EXTRA_ATTACK) && nSize < CREATURE_SIZE_LARGE) + { + // The following is a *wrong* way of doing this and will break the moment BW decides to modify + // the actual values of the IP_CONST_DAMAGEBONUS_* constants, but I'm lazy :p + int nDamBon = nStrBon > 5 ? nStrBon + 10 : nStrBon; + int nDamageType = sWeaponResRef == ASTRAL_CONSTRUCT_SLAM ? + IP_CONST_DAMAGETYPE_BLUDGEONING : + IP_CONST_DAMAGETYPE_SLASHING; + itemproperty ipDamBon = ItemPropertyDamageBonus(nDamageType, nDamBon); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamBon, oWeapon); + } + + + // Apply OnHitCast power if necessary + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POISON_TOUCH || + nFlags & ASTRAL_CONSTRUCT_OPTION_REND) + { + itemproperty ipOnHitCast = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1); + AddItemProperty(DURATION_TYPE_PERMANENT, ipOnHitCast, oWeapon); + } + + // Handle Energy Touch option + if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_TOUCH) + { + DoEnergyTouch(oWeapon, GetLocalInt(oConstruct, ASTRAL_CONSTRUCT_ENERGY_TOUCH_FLAGS), GetMaster(oConstruct)); + } + + + /// Handle options that set local ints + + // Add OnHit: CastSpell - Unique Power to the slam + if(nFlags & ASTRAL_CONSTRUCT_OPTION_POISON_TOUCH) { SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_POISON_TOUCH, TRUE); } + // Make the construct capable of using Concussion Blast power as a free action every round + // Will probably be implemented via heartbeat + if(nFlags & ASTRAL_CONSTRUCT_OPTION_CONCUSSION) { SetLocalInt(oConstruct, ASTRAL_CONSTRUCT_CONCUSSION, TRUE); } + + + + /********************* UNFINISHED POWERS *********************/ + + // Allow the construct to use Dimension Slide as a move action every round + // COMPLETELY UNIMPLEMENTED AS OF YET + if(nFlags & ASTRAL_CONSTRUCT_OPTION_DIMENSION_SLIDE) + { + WriteTimestampedLogEntry("Astral Construct created with flag ASTRAL_CONSTRUCT_OPTION_DIMENSION_SLIDE. This option shouldn't be available yet."); + } + // Allow the construct to use Energy Bolt as a standard action every round + // COMPLETELY UNIMPLEMENTED AS OF YET + if(nFlags & ASTRAL_CONSTRUCT_OPTION_ENERGY_BOLT) + { + WriteTimestampedLogEntry("Astral Construct created with flag ASTRAL_CONSTRUCT_OPTION_ENERGY_BOLT. This option shouldn't be available yet."); + } +} + + + +// Extraordinaries and applies the given effect +void DoEffect(object oConstruct, effect eEffect) +{ + eEffect = ExtraordinaryEffect(eEffect); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oConstruct); +} + + +// Adds the given feat to the AC's hide. Nothing special, just +// a convenience so I needn't typ(e/o) it several times +void DoFeat(object oHide, int nIPFeatID) +{ + itemproperty ipFeat = PRCItemPropertyBonusFeat(nIPFeatID); + AddItemProperty(DURATION_TYPE_PERMANENT, ipFeat, oHide); +} + + +// Does the work of the Deflect series of effects +void DoDeflect(object oConstruct, int nACVal) +{ + effect eAC = EffectACIncrease(nACVal, AC_DEFLECTION_BONUS); + DoEffect(oConstruct, eAC); +} + + +// Adds the given resistances to the construct's hide +void DoResistance(object oHide, int nElementFlags) +{ + itemproperty ipResist; + + if(nElementFlags & ELEMENT_ACID) + { + ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ACID, IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); + } + if(nElementFlags & ELEMENT_COLD) + { + ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_COLD, IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); + } + if(nElementFlags & ELEMENT_ELECTRICITY) + { + ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_ELECTRICAL, IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); + } + if(nElementFlags & ELEMENT_FIRE) + { + ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_FIRE, IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); + } + if(nElementFlags & ELEMENT_SONIC) + { + ipResist = ItemPropertyDamageResistance(IP_CONST_DAMAGETYPE_SONIC, IP_CONST_DAMAGERESIST_5); + AddItemProperty(DURATION_TYPE_PERMANENT, ipResist, oHide); + } +} + + +// Adds 1d4 (or 1d6 if oCreator is a kineticist) of the chosen types of elemental damage +// to the construct's weapon +void DoEnergyTouch(object oWeapon, int nElementFlags, object oCreator) +{ + itemproperty ipDamage; + int nDamage = GetHasFeat(FEAT_PSION_DIS_KINETICIST, oCreator) ? IP_CONST_DAMAGEBONUS_1d6 : IP_CONST_DAMAGEBONUS_1d4; + + if(nElementFlags & ELEMENT_ACID) + { + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID, nDamage); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); + } + if(nElementFlags & ELEMENT_COLD) + { + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_COLD, nDamage); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); + } + if(nElementFlags & ELEMENT_ELECTRICITY) + { + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ELECTRICAL, nDamage); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); + } + if(nElementFlags & ELEMENT_FIRE) + { + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_FIRE, nDamage); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); + } + if(nElementFlags & ELEMENT_SONIC) + { + ipDamage = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_SONIC, nDamage); + AddItemProperty(DURATION_TYPE_PERMANENT, ipDamage, oWeapon); + } +} + + +/* Returns the base damage an AC's attacks should deal based on it's size + * Small - 1d4 + * Medium - 1d6 + * Large - 1d8 + * Huge - 2d6 + */ +int GetBaseDamageFromSize(int nSize) +{ + switch(nSize) + { + case CREATURE_SIZE_SMALL: + return IP_CONST_MONSTERDAMAGE_1d4; + case CREATURE_SIZE_MEDIUM: + return IP_CONST_MONSTERDAMAGE_1d6; + case CREATURE_SIZE_LARGE: + return IP_CONST_MONSTERDAMAGE_1d8; + case CREATURE_SIZE_HUGE: + return IP_CONST_MONSTERDAMAGE_2d6; + default: + WriteTimestampedLogEntry("Invalid size value for an Astral Construct passed to GetBaseDamageFromSize: " + IntToString(nSize)); + } + + return IP_CONST_MONSTERDAMAGE_1d4; +} + +// Returns the next bigger damage value +int GetNextBaseDamage(int nDamVal) +{ + switch(nDamVal) + { + case IP_CONST_MONSTERDAMAGE_1d4: return IP_CONST_MONSTERDAMAGE_1d6; + case IP_CONST_MONSTERDAMAGE_1d6: return IP_CONST_MONSTERDAMAGE_1d8; + case IP_CONST_MONSTERDAMAGE_1d8: return IP_CONST_MONSTERDAMAGE_2d6; + case IP_CONST_MONSTERDAMAGE_2d6: return IP_CONST_MONSTERDAMAGE_3d6; + default: + WriteTimestampedLogEntry("Invalid monster damage value passed to GetNextBaseDamage: " + IntToString(nDamVal)); + } + + return IP_CONST_MONSTERDAMAGE_1d4; +} + + +// Returns the size of the construct based on its level +int GetConstructSizeFromTag(object oConstruct) +{ + string sTag = GetTag(oConstruct); + int nLevel = StringToInt(GetSubString(sTag, 14, 1)); + + switch(nLevel) + { + case 1: return CREATURE_SIZE_SMALL; + case 2: + case 3: + case 4: return CREATURE_SIZE_MEDIUM; + case 5: + case 6: + case 7: + case 8: return CREATURE_SIZE_LARGE; + case 9: return CREATURE_SIZE_HUGE; + + default: + WriteTimestampedLogEntry("Erroneous value for nLevel in GetDamageReduction: " + IntToString(nLevel) + ", tag: " + sTag); + } + + return 0; +} + +// Returns the damage reduction that the level of the construct entitles it to +int GetDamageReduction(object oConstruct) +{ + string sTag = GetTag(oConstruct); + int nLevel = StringToInt(GetSubString(sTag, 14, 1)); + + switch(nLevel) + { + case 1: + case 2: + case 3: + case 4: return 0; + case 5: return 5; + case 6: + case 7: return 10; + case 8: + case 9: return 15; + + default: + WriteTimestampedLogEntry("Erroneous value for nLevel in GetDamageReduction: " + IntToString(nLevel) + ", tag: " + sTag); + } + + return 0; +} + + +// Returns the IP_CONST_DAMAGESOAK_*_HP constant that does the given +// amount of damage reduction +int GetDamageReductionConstantFromAmount(int nDamRed) +{ + switch(nDamRed) + { + case 3: return IP_CONST_DAMAGESOAK_3_HP; + case 5: return IP_CONST_DAMAGESOAK_5_HP; + case 6: return IP_CONST_DAMAGESOAK_6_HP; + case 8: return IP_CONST_DAMAGESOAK_8_HP; + case 9: return IP_CONST_DAMAGESOAK_9_HP; + case 10: return IP_CONST_DAMAGESOAK_10_HP; + case 11: return IP_CONST_DAMAGESOAK_11_HP; + case 13: return IP_CONST_DAMAGESOAK_13_HP; + case 14: return IP_CONST_DAMAGESOAK_14_HP; + case 15: return IP_CONST_DAMAGESOAK_15_HP; + case 16: return IP_CONST_DAMAGESOAK_16_HP; + case 19: return IP_CONST_DAMAGESOAK_19_HP; + case 18: return IP_CONST_DAMAGESOAK_18_HP; + case 21: return IP_CONST_DAMAGESOAK_21_HP; + case 24: return IP_CONST_DAMAGESOAK_24_HP; + + default: + WriteTimestampedLogEntry("Erroneous value for nDamRed in GetDamageReductionAmountToConstant: " + IntToString(nDamRed)); + } + + return -1; +} \ No newline at end of file diff --git a/src/include/psi_inc_augment.nss b/src/include/psi_inc_augment.nss new file mode 100644 index 0000000..6bbac45 --- /dev/null +++ b/src/include/psi_inc_augment.nss @@ -0,0 +1,717 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Augmentation +//:: psi_inc_augment +//:://///////////////////////////////////////////// +/** @file + Defines structs and functions for handling + psionic power augmentation. + + @author Ornedan + @date Created - 2005.11.04 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Constants are provided via psi_inc_core + +/// Prefix of the local variable names used for storing an user's profiles +const string PRC_AUGMENT_PROFILE = "PRC_Augment_Profile_"; +/// Index of the currently used profile. +const string PRC_CURRENT_AUGMENT_PROFILE = "PRC_Current_Augment_Profile_Index"; +/// Name of local variable where override is stored +const string PRC_AUGMENT_OVERRIDE = "PRC_Augment_Override"; +/// Name of local variable where the value of maximal augmentation switch is stored +const string PRC_AUGMENT_MAXAUGMENT = "PRC_Augment_MaxAugment"; + +/// The lowest valid value of PRC_CURRENT_AUGMENT_PROFILE +const int PRC_AUGMENT_PROFILE_INDEX_MIN = 1; +/// The highest valid value of PRC_CURRENT_AUGMENT_PROFILE +const int PRC_AUGMENT_PROFILE_INDEX_MAX = 49; +/// Prefix of the local variable names used for storing quickselections +const string PRC_AUGMENT_QUICKSELECTION = "PRC_Augment_Quickselection_"; +/// The lowest value the quickslot index can have +const int PRC_AUGMENT_QUICKSELECTION_MIN = 1; +/// The highest value the quickslot index can have +const int PRC_AUGMENT_QUICKSELECTION_MAX = 7; +/// An index that should never contain a profile +const int PRC_AUGMENT_PROFILE_NONE = 0; + +/// The value of an empty profile. Also known as zero +const int PRC_AUGMENT_NULL_PROFILE = 0x00000000; + +/// The special value for nGenericAugCost in power augmentation profile that means the power has no generic augmentation. +const int PRC_NO_GENERIC_AUGMENTS = -1; +/// The special value for nMaxAugs_* that means there is no limit to the times that option may be used. +const int PRC_UNLIMITED_AUGMENTATION = -1; + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure used for defining how a particular power may be augmented. + * Use PowerAugmentationProfile() to create. + */ +struct power_augment_profile{ + /** + * Many powers specify several augmentation options and in addition a + * "for each N PP spent augmenting this power, something happens". + * This value is that N. + */ + int nGenericAugCost; + + /** + * How many PP the first augmentation option of the power will cost per + * times used. + */ + int nAugCost_1; + /** + * How many times, at most, can the first augmentation option be used. + */ + int nMaxAugs_1; + + /** + * How many PP the second augmentation option of the power will cost per + * times used. + */ + int nAugCost_2; + /** + * How many times, at most, can the second augmentation option be used. + */ + int nMaxAugs_2; + + /** + * How many PP the third augmentation option of the power will cost per + * times used. + */ + int nAugCost_3; + /** + * How many times, at most, can the third augmentation option be used. + */ + int nMaxAugs_3; + + /** + * How many PP the fourth augmentation option of the power will cost per + * times used. + */ + int nAugCost_4; + /** + * How many times, at most, can the fourth augmentation option be used. + */ + int nMaxAugs_4; + + /** + * How many PP the fifth augmentation option of the power will cost per + * times used. + */ + int nAugCost_5; + /** + * How many times, at most, can the fifth augmentation option be used. + */ + int nMaxAugs_5; +}; + +/** + * Users define how much PP they want to use for each augmentation option or + * how many times they want to use each option. This structure is for transferring + * that data. + */ +struct user_augment_profile{ + int nOption_1; + int nOption_2; + int nOption_3; + int nOption_4; + int nOption_5; + + /// Whether the values in this structure are to be interpreted as augmentation levels + /// or as amounts of PP. + int bValueIsPP; +}; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Constructs an augmentation profile for a power. + * The default values for each parameter specify that the power in question + * does not have that augmentation feature. + * + * @param nGenericAugCost Many powers have an augmentation clause saying "for + * each N power points used to augment this power, + * X happens". This parameter is used to define the + * value N. + * Valid values: {x = -1 OR x > 0} + * Default: -1, which means that there is no generic + * augmentation for this power. + * + * @param nAugCost_1 Cost to use the first augmentation option of this + * power. + * Valid values: {x >= 0} + * Default: 0 + * @param nMaxAugs_1 Number of times the first augmentation option may at + * most be used. Value of -1 means the option may be used + * an unlimited number of times. + * Valid values: {x >= -1} + * Default: 0 + * + * @param nAugCost_2 Cost to use the second augmentation option of this + * power. + * Valid values: {x >= 0} + * Default: 0 + * @param nMaxAugs_2 Number of times the second augmentation option may at + * most be used. Value of -1 means the option may be used + * an unlimited number of times. + * Valid values: {x >= -1} + * Default: 0 * + * + * @param nAugCost_3 Cost to use the third augmentation option of this + * power. + * Valid values: {x >= 0} + * Default: 0 + * @param nMaxAugs_3 Number of times the third augmentation option may at + * most be used. Value of -1 means the option may be used + * an unlimited number of times. + * Valid values: {x >= -1} + * Default: 0 + * + * @param nAugCost_4 Cost to use the fourth augmentation option of this + * power. + * Valid values: {x >= 0} + * Default: 0 + * @param nMaxAugs_4 Number of times the fourth augmentation option may at + * most be used. Value of -1 means the option may be used + * an unlimited number of times. + * Valid values: {x >= -1} + * Default: 0 + * + * @param nAugCost_5 Cost to use the fifth augmentation option of this + * power. + * Valid values: {x >= 0} + * Default: 0 + * @param nMaxAugs_5 Number of times the fifth augmentation option may at + * most be used. Value of -1 means the option may be used + * an unlimited number of times. + * Valid values: {x >= -1} + * Default: 0 + * + * @return The parameters compiled into a power_augment_profile + * structure. + */ +struct power_augment_profile PowerAugmentationProfile(int nGenericAugCost = PRC_NO_GENERIC_AUGMENTS, + int nAugCost_1 = 0, int nMaxAugs_1 = 0, + int nAugCost_2 = 0, int nMaxAugs_2 = 0, + int nAugCost_3 = 0, int nMaxAugs_3 = 0, + int nAugCost_4 = 0, int nMaxAugs_4 = 0, + int nAugCost_5 = 0, int nMaxAugs_5 = 0 + ); + +/** + * Reads an augmentation profile from a user and compiles it into + * a structure. + * + * @param oUser A creature that has power augmentation profiles set up. + * @param nIndex The number of the profile to retrieve. + * @param bQuickSelection Whether the index is a quickselection or a normal profile. + * @return The retrieved profile, compiled into a structure + */ +struct user_augment_profile GetUserAugmentationProfile(object oUser, int nIndex, int bQuickSelection = FALSE); + +/** + * Gets the user's current augmentation profile. + * + * @param oUser A creature that has power augmentation profiles set up. + * @return The retrieved profile, compiled into a structure + */ +struct user_augment_profile GetCurrentUserAugmentationProfile(object oUser); + +/** + * Stores a user-specified augmentation profile. + * + * @param oUser The user for whose use to store the profile for. + * @param nIndex The index number to store the profile under. + * @param bQuickSelection Whether the index is a quickselection or a normal profile. + * @param uap A structure containing the profile to store. + */ +void StoreUserAugmentationProfile(object oUser, int nIndex, struct user_augment_profile uap, int bQuickSelection = FALSE); + +/** + * Converts the given augmentation profile into a string. + * + * @param uap The augmentation profile to convert to a string. + * @return A string of format: + * Option 1: N [times|PP], Option 2: N [times|PP], Option 2: N [times|PP], Option 4: N [times|PP], Option 5: N [times|PP] + */ +string UserAugmentationProfileToString(struct user_augment_profile uap); + +/** + * Calculates how many times each augmentation option of a power is used and + * the increased PP cost caused by augmentation. + * In addition to basic augmentation, currently accounts for: + * - Wild Surge + * + * + * @param manif The manifestation data related to an ongoing manifestation attempt. + * @param pap The power's augmentation profile. + * @return The manifestation data with augmentation's effects added in. + */ +struct manifestation EvaluateAugmentation(struct manifestation manif, struct power_augment_profile pap); + +/** + * Overrides the given creature's augmentation settings during it's next + * manifestation with the given settings. + * + * NOTE: These values are assumed to be augmentation levels. + * + * @param oCreature Creature whose augmentation to override + * @param uap The profile to use as override + */ +void SetAugmentationOverride(object oCreature, struct user_augment_profile uap); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "psi_inc_core" + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * @param nToDecode Integer from which to extract an user augmentation profile + * @return An user augmentation profile created based on nToDecode + */ +struct user_augment_profile _DecodeProfile(int nToDecode) +{ + struct user_augment_profile uap; + + // The augmentation profile is stored in one integer, with 6 bits per option. + // MSB -> [xx555555444444333333222222111111] <- LSB + int nMask = 0x3F; // 6 LSB + uap.nOption_1 = nToDecode & nMask; + uap.nOption_2 = (nToDecode >>> 6) & nMask; + uap.nOption_3 = (nToDecode >>> 12) & nMask; + uap.nOption_4 = (nToDecode >>> 18) & nMask; + uap.nOption_5 = (nToDecode >>> 24) & nMask; + + return uap; +} + +/** Internal function. + * @param uapToEncode An user augmentation profile to encode into a single integer + * @return Integer built from values in uapToEncode + */ +int _EncodeProfile(struct user_augment_profile uapToEncode) +{ + // The augmentation profile is stored in one integer, with 6 bits per option. + // MSB -> [xx555555444444333333222222111111] <- LSB + int nProfile = PRC_AUGMENT_NULL_PROFILE; + int nMask = 0x3F; // 6 LSB + + nProfile |= (uapToEncode.nOption_1 & nMask); + nProfile |= (uapToEncode.nOption_2 & nMask) << 6; + nProfile |= (uapToEncode.nOption_3 & nMask) << 12; + nProfile |= (uapToEncode.nOption_4 & nMask) << 18; + nProfile |= (uapToEncode.nOption_5 & nMask) << 24; + + return nProfile; +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + + +struct power_augment_profile PowerAugmentationProfile(int nGenericAugCost = PRC_NO_GENERIC_AUGMENTS, + int nAugCost_1 = 0, int nMaxAugs_1 = 0, + int nAugCost_2 = 0, int nMaxAugs_2 = 0, + int nAugCost_3 = 0, int nMaxAugs_3 = 0, + int nAugCost_4 = 0, int nMaxAugs_4 = 0, + int nAugCost_5 = 0, int nMaxAugs_5 = 0 + ) +{ + struct power_augment_profile pap; + + pap.nGenericAugCost = nGenericAugCost; + pap.nAugCost_1 = nAugCost_1; + pap.nMaxAugs_1 = nMaxAugs_1; + pap.nAugCost_2 = nAugCost_2; + pap.nMaxAugs_2 = nMaxAugs_2; + pap.nAugCost_3 = nAugCost_3; + pap.nMaxAugs_3 = nMaxAugs_3; + pap.nAugCost_4 = nAugCost_4; + pap.nMaxAugs_4 = nMaxAugs_4; + pap.nAugCost_5 = nAugCost_5; + pap.nMaxAugs_5 = nMaxAugs_5; + + return pap; +} + +struct user_augment_profile GetUserAugmentationProfile(object oUser, int nIndex, int bQuickSelection = FALSE) +{ + int nProfile = GetPersistantLocalInt(oUser, (bQuickSelection ? PRC_AUGMENT_QUICKSELECTION : PRC_AUGMENT_PROFILE) + IntToString(nIndex)); + struct user_augment_profile uap = _DecodeProfile(nProfile); + + // Get the augmentation levels / PP switch + uap.bValueIsPP = GetPersistantLocalInt(oUser, PRC_PLAYER_SWITCH_AUGMENT_IS_PP); + + // Validity check on the index + if(bQuickSelection ? + (nIndex < PRC_AUGMENT_QUICKSELECTION_MIN || + nIndex > PRC_AUGMENT_QUICKSELECTION_MAX + ): + (nIndex < PRC_AUGMENT_PROFILE_INDEX_MIN || + nIndex > PRC_AUGMENT_PROFILE_INDEX_MAX + ) + ) + { + // Null the profile, just in case + uap.nOption_1 = 0; + uap.nOption_2 = 0; + uap.nOption_3 = 0; + uap.nOption_4 = 0; + uap.nOption_5 = 0; + } + + return uap; +} + +/** + * Gets the user's current augmentation profile. + * + * @param oUser A creature that has power augmentation profiles set up. + * @return The retrieved profile, compiled into a structure + */ +struct user_augment_profile GetCurrentUserAugmentationProfile(object oUser) +{ + struct user_augment_profile uap_ret; + + // Is augmentation override in effect? + if(GetLocalInt(oUser, PRC_AUGMENT_OVERRIDE)) + { + uap_ret = _DecodeProfile(GetLocalInt(oUser, PRC_AUGMENT_OVERRIDE) - 1); + uap_ret.bValueIsPP = FALSE; // Override is always considered to be augmentation levels + } + // It wasn't, so get normally + else + { + int nIndex = GetLocalInt(oUser, PRC_CURRENT_AUGMENT_PROFILE); + int bQuick = nIndex < 0 ? TRUE : FALSE; + + if(bQuick) nIndex = -nIndex; + + uap_ret = GetUserAugmentationProfile(oUser, nIndex, bQuick); + } + + return uap_ret; +} + +void StoreUserAugmentationProfile(object oUser, int nIndex, struct user_augment_profile uap, int bQuickSelection = FALSE) +{ + // Validity check on the index + if(bQuickSelection ? + (nIndex < PRC_AUGMENT_QUICKSELECTION_MIN || + nIndex > PRC_AUGMENT_QUICKSELECTION_MAX + ): + (nIndex < PRC_AUGMENT_PROFILE_INDEX_MIN || + nIndex > PRC_AUGMENT_PROFILE_INDEX_MAX + ) + ) + { + if(DEBUG) DoDebug("StoreUserAugmentationProfile(): Attempt to store outside valid range: " + IntToString(nIndex)); + return; + } + + SetPersistantLocalInt(oUser, (bQuickSelection ? PRC_AUGMENT_QUICKSELECTION : PRC_AUGMENT_PROFILE) + IntToString(nIndex), _EncodeProfile(uap)); +} + +string UserAugmentationProfileToString(struct user_augment_profile uap) +{ + string sBegin = GetStringByStrRef(16823498) + " "; // "Option" + string sEnd = " " + (uap.bValueIsPP ? "PP" : GetStringByStrRef(16823499)); // "times" + + return sBegin + "1: " + IntToString(uap.nOption_1) + sEnd + "; " + + sBegin + "2: " + IntToString(uap.nOption_2) + sEnd + "; " + + sBegin + "3: " + IntToString(uap.nOption_3) + sEnd + "; " + + sBegin + "4: " + IntToString(uap.nOption_4) + sEnd + "; " + + sBegin + "5: " + IntToString(uap.nOption_5) + sEnd; +} + +struct manifestation EvaluateAugmentation(struct manifestation manif, struct power_augment_profile pap) +{ + // Get the user's augmentation profile - will be all zeroes if no profile is active + struct user_augment_profile uap = GetCurrentUserAugmentationProfile(manif.oManifester); + int nSurge = GetWildSurge(manif.oManifester); + int nAugPPCost = 0; + int nAugPPCostReductions = 0; + int bMaxAugment = GetLocalInt(manif.oManifester, PRC_AUGMENT_MAXAUGMENT) && + !GetLocalInt(manif.oManifester, PRC_AUGMENT_OVERRIDE); // Override profile also overrides max augment + + // Initialise the augmentation data in the manifestation structure to zero + /* Probably unnecessary due to auto-init + manif.nTimesAugOptUsed_1 = 0; + manif.nTimesAugOptUsed_2 = 0; + manif.nTimesAugOptUsed_3 = 0; + manif.nTimesAugOptUsed_4 = 0; + manif.nTimesAugOptUsed_5 = 0; + manif.nTimesGenericAugUsed = 0; + */ + + /* Bloody duplication follows. Real arrays would be nice */ + // Make sure the option is available for use and the user wants to do something with it + if(pap.nMaxAugs_1 && uap.nOption_1) + { + // Determine how many times the augmentation option has been used + manif.nTimesAugOptUsed_1 = uap.bValueIsPP ? // The user can determine whether their settings are interpreted + uap.nOption_1 / pap.nAugCost_1 : // as static PP costs + uap.nOption_1; // or as number of times to use + // Make sure the number of times the option is used does not exceed the maximum + if(pap.nMaxAugs_1 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_1 > pap.nMaxAugs_1) + manif.nTimesAugOptUsed_1 = pap.nMaxAugs_1; + // Calculate the amount of PP the augmentation will cost + nAugPPCost += manif.nTimesAugOptUsed_1 * pap.nAugCost_1; + } + // Make sure the option is available for use and the user wants to do something with it + if(pap.nMaxAugs_2 && uap.nOption_2) + { + // Determine how many times the augmentation option has been used + manif.nTimesAugOptUsed_2 = uap.bValueIsPP ? // The user can determine whether their settings are interpreted + uap.nOption_2 / pap.nAugCost_2 : // as static PP costs + uap.nOption_2; // or as number of times to use + // Make sure the number of times the option is used does not exceed the maximum + if(pap.nMaxAugs_2 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_2 > pap.nMaxAugs_2) + manif.nTimesAugOptUsed_2 = pap.nMaxAugs_2; + // Calculate the amount of PP the augmentation will cost + nAugPPCost += manif.nTimesAugOptUsed_2 * pap.nAugCost_2; + } + // Make sure the option is available for use and the user wants to do something with it + if(pap.nMaxAugs_3 && uap.nOption_3) + { + // Determine how many times the augmentation option has been used + manif.nTimesAugOptUsed_3 = uap.bValueIsPP ? // The user can determine whether their settings are interpreted + uap.nOption_3 / pap.nAugCost_3 : // as static PP costs + uap.nOption_3; // or as number of times to use + // Make sure the number of times the option is used does not exceed the maximum + if(pap.nMaxAugs_3 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_3 > pap.nMaxAugs_3) + manif.nTimesAugOptUsed_3 = pap.nMaxAugs_3; + // Calculate the amount of PP the augmentation will cost + nAugPPCost += manif.nTimesAugOptUsed_3 * pap.nAugCost_3; + } + // Make sure the option is available for use and the user wants to do something with it + if(pap.nMaxAugs_4 && uap.nOption_4) + { + // Determine how many times the augmentation option has been used + manif.nTimesAugOptUsed_4 = uap.bValueIsPP ? // The user can determine whether their settings are interpreted + uap.nOption_4 / pap.nAugCost_4 : // as static PP costs + uap.nOption_4; // or as number of times to use + // Make sure the number of times the option is used does not exceed the maximum + if(pap.nMaxAugs_4 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_4 > pap.nMaxAugs_4) + manif.nTimesAugOptUsed_4 = pap.nMaxAugs_4; + // Calculate the amount of PP the augmentation will cost + nAugPPCost += manif.nTimesAugOptUsed_4 * pap.nAugCost_4; + } + // Make sure the option is available for use and the user wants to do something with it + if(pap.nMaxAugs_5 && uap.nOption_5) + { + // Determine how many times the augmentation option has been used + manif.nTimesAugOptUsed_5 = uap.bValueIsPP ? // The user can determine whether their settings are interpreted + uap.nOption_5 / pap.nAugCost_5 : // as static PP costs + uap.nOption_5; // or as number of times to use + // Make sure the number of times the option is used does not exceed the maximum + if(pap.nMaxAugs_5 != PRC_UNLIMITED_AUGMENTATION && manif.nTimesAugOptUsed_5 > pap.nMaxAugs_5) + manif.nTimesAugOptUsed_5 = pap.nMaxAugs_5; + // Calculate the amount of PP the augmentation will cost + nAugPPCost += manif.nTimesAugOptUsed_5 * pap.nAugCost_5; + } + + // Calculate number of times a generic augmentation happens with this number of PP + if(pap.nGenericAugCost != PRC_NO_GENERIC_AUGMENTS) + manif.nTimesGenericAugUsed = nAugPPCost / pap.nGenericAugCost; + + + /*/ Various effects modifying the augmentation go below /*/ + + // Account for wild surge + nAugPPCostReductions += nSurge; + + // Midnight Augmentation feat from incarnum + int bMidAug = GetLocalInt(manif.oManifester, "MidnightAugPower"); + int nMidnightAugReduction = 0; + //if (GetHasFeat(FEAT_MIDNIGHT_AUGMENTATION, manif.oManifester)) + if(bMidAug > 0) + { + if(DEBUG) DoDebug("Checking Midnight Augumentation"); + int bMidAugInvestment = GetLocalInt(manif.oManifester, "MidnightAugPowerInvestment"); + + // Make sure the power is the correct one, and that you can expend your psionic focus + if (manif.nSpellID == GetLocalInt(manif.oManifester, "MidnightAugPower") && UsePsionicFocus(manif.oManifester)) + { + if(DEBUG) DoDebug("Midnight Augumentation power invested Essentia is: "+IntToString(bMidAugInvestment)+"."); + //nAugPPCostReductions = nAugPPCostReductions + bMidAugInvestment; + nMidnightAugReduction = bMidAugInvestment; + + if(DEBUG) DoDebug("Final Midnight Augumentation power adjustment "+IntToString(nMidnightAugReduction)+"."); + } + } + + /*/ Various effects modifying the augmentation go above /*/ + + // Auto-distribution, if modifying effects provided more PP than has been used so far or + // the maximal augmentation switch is active + if((nAugPPCost - nAugPPCostReductions) < 0 || bMaxAugment) + { + int nToAutodistribute = 0, nAutodistributed; + + // Calculate autodistribution amount + if(bMaxAugment) + { + // Maximal augmentation ignores PP cost reductions here. They are instead handled at the end + if(((manif.nManifesterLevel - manif.nPPCost) - nAugPPCost) > 0) + nToAutodistribute = manif.nManifesterLevel // Maximum usable + - manif.nPPCost // Reduced by what's already been used for other things + - nAugPPCost; // Reduced by what's already been used for augmentation + } + // No maximal augmentation, instead more PP cost reduction provided than PP has been used + else if((nAugPPCost - nAugPPCostReductions) < 0) + nToAutodistribute = -(nAugPPCost - nAugPPCostReductions); // The amount of PP cost reduction that's in excess of what's already been used for augmentation + + // Store the value for use in cost calculations + nAutodistributed = nToAutodistribute; + + // Start the distribution + int nTimesCanAug; + int nTimesAugd; + if(nToAutodistribute > 0 && pap.nMaxAugs_1) + { + // Determine how many times this option could be used + if(pap.nMaxAugs_1 == PRC_UNLIMITED_AUGMENTATION) + nTimesCanAug = PRC_UNLIMITED_AUGMENTATION; + else + nTimesCanAug = pap.nMaxAugs_1 - manif.nTimesAugOptUsed_1; + + if(nTimesCanAug) + { + // Determine how many times it can be used and how much it costs + nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ? + nToAutodistribute / pap.nAugCost_1 : + PRCMin(nToAutodistribute / pap.nAugCost_1, nTimesCanAug); + nToAutodistribute -= nTimesAugd * pap.nAugCost_1; + + manif.nTimesAugOptUsed_1 += nTimesAugd; + } + } + if(nToAutodistribute > 0 && pap.nMaxAugs_2) + { + // Determine how many times this option can be used + if(pap.nMaxAugs_2 == PRC_UNLIMITED_AUGMENTATION) + nTimesCanAug = PRC_UNLIMITED_AUGMENTATION; + else + nTimesCanAug = pap.nMaxAugs_2 - manif.nTimesAugOptUsed_2; + + if(nTimesCanAug) + { + // Determine how many times it can be used and how much it costs + nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ? + nToAutodistribute / pap.nAugCost_2 : + PRCMin(nToAutodistribute / pap.nAugCost_2, nTimesCanAug); + nToAutodistribute -= nTimesAugd * pap.nAugCost_2; + + manif.nTimesAugOptUsed_2 += nTimesAugd; + } + } + if(nToAutodistribute > 0 && pap.nMaxAugs_3) + { + // Determine how many times this option can be used + if(pap.nMaxAugs_3 == PRC_UNLIMITED_AUGMENTATION) + nTimesCanAug = PRC_UNLIMITED_AUGMENTATION; + else + nTimesCanAug = pap.nMaxAugs_3 - manif.nTimesAugOptUsed_3; + + if(nTimesCanAug) + { + // Determine how many times it can be used and how much it costs + nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ? + nToAutodistribute / pap.nAugCost_3 : + PRCMin(nToAutodistribute / pap.nAugCost_3, nTimesCanAug); + nToAutodistribute -= nTimesAugd * pap.nAugCost_3; + + manif.nTimesAugOptUsed_3 += nTimesAugd; + } + } + if(nToAutodistribute > 0 && pap.nMaxAugs_4) + { + // Determine how many times this option can be used + if(pap.nMaxAugs_4 == PRC_UNLIMITED_AUGMENTATION) + nTimesCanAug = PRC_UNLIMITED_AUGMENTATION; + else + nTimesCanAug = pap.nMaxAugs_4 - manif.nTimesAugOptUsed_4; + + if(nTimesCanAug) + { + // Determine how many times it can be used and how much it costs + nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ? + nToAutodistribute / pap.nAugCost_4 : + PRCMin(nToAutodistribute / pap.nAugCost_4, nTimesCanAug); + nToAutodistribute -= nTimesAugd * pap.nAugCost_4; + + manif.nTimesAugOptUsed_4 += nTimesAugd; + } + } + if(nToAutodistribute > 0 && pap.nMaxAugs_5) + { + // Determine how many times this option can be used + if(pap.nMaxAugs_5 == PRC_UNLIMITED_AUGMENTATION) + nTimesCanAug = PRC_UNLIMITED_AUGMENTATION; + else + nTimesCanAug = pap.nMaxAugs_5 - manif.nTimesAugOptUsed_5; + + if(nTimesCanAug) + { + // Determine how many times it can be used and how much it costs + nTimesAugd = nTimesCanAug == PRC_UNLIMITED_AUGMENTATION ? + nToAutodistribute / pap.nAugCost_5 : + PRCMin(nToAutodistribute / pap.nAugCost_5, nTimesCanAug); + nToAutodistribute -= nTimesAugd * pap.nAugCost_5; + + manif.nTimesAugOptUsed_5 += nTimesAugd; + } + } + + // Calculate amount actually autodistributed. nToAutoDistribute now contains the amount of PP that could not be auto-distributed + nAutodistributed = (nAutodistributed - nToAutodistribute); + + // Calculate increase to generic augmentation + if(pap.nGenericAugCost != PRC_NO_GENERIC_AUGMENTS) + manif.nTimesGenericAugUsed += nAutodistributed / pap.nGenericAugCost; + + // Determine new augmentation PP cost + nAugPPCost += nAutodistributed; + } + + // Add in cost reduction + //nAugPPCost = PRCMax(0, nAugPPCost - nAugPPCostReductions); + nAugPPCost = nAugPPCost - nAugPPCostReductions; + // Store the PP cost increase + manif.nPPCost += nAugPPCost; + manif.nPPCost -= nMidnightAugReduction; //:: Had to apply this directly, wouldn't work otherwise. -Jaysyn + + return manif; +} + +void SetAugmentationOverride(object oCreature, struct user_augment_profile uap) +{ + SetLocalInt(oCreature, PRC_AUGMENT_OVERRIDE, _EncodeProfile(uap) + 1); +} + +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/psi_inc_const.nss b/src/include/psi_inc_const.nss new file mode 100644 index 0000000..5256beb --- /dev/null +++ b/src/include/psi_inc_const.nss @@ -0,0 +1,119 @@ +//////////////////////////////////// +// +// Global Constants for +// Psionics Functions +// +/////////////////////////////////// + + + +const string PSIONIC_FOCUS = "PRC_PsionicFocus"; + +const string PRC_WILD_SURGE = "PRC_WildSurge_Level"; +const string PRC_OVERCHANNEL = "PRC_Overchannel_Level"; + +const string POWER_POINT_VARNAME = "PRC_PowerPoints"; + + + +/// Special power lists. Powers gained via Expanded Knowledge, Psychic Chirurgery and similar sources +const int POWER_LIST_EXP_KNOWLEDGE = -1; +const int POWER_LIST_EPIC_EXP_KNOWLEDGE = -2; +const int POWER_LIST_MISC = -3; + +const string _POWER_LIST_NAME_BASE = "PRC_PsionicsPowerList_"; +const string _POWER_LIST_TOTAL_KNOWN = "_TotalKnown"; +const string _POWER_LIST_MODIFIER = "_KnownModifier"; +const string _POWER_LIST_MISC_ARRAY = "_PowersKnownMiscArray"; +const string _POWER_LIST_LEVEL_ARRAY = "_PowersKnownLevelArray_"; +const string _POWER_LIST_GENERAL_ARRAY = "_PowersKnownGeneralArray"; + +///////////////////////////////////////////// +// Manifest + +const string PRC_MANIFESTING_CLASS = "PRC_CurrentManifest_ManifestingClass"; +const string PRC_POWER_LEVEL = "PRC_CurrentManifest_PowerLevel"; +const string PRC_IS_PSILIKE = "PRC_CurrentManifest_IsPsiLikeAbility"; +const string PRC_DEBUG_IGNORE_CONSTRAINTS = "PRC_Debug_Ignore_Constraints"; + +/** + * The variable in which the manifestation token is stored. If no token exists, + * the variable is set to point at the manifester itself. That way OBJECT_INVALID + * means the variable is unitialised. + */ +const string PRC_MANIFESTATION_TOKEN_VAR = "PRC_ManifestationToken"; +const string PRC_MANIFESTATION_TOKEN_NAME = "PRC_MANIFTOKEN"; +const float PRC_MANIFESTATION_HB_DELAY = 0.5f; + +// +///////////////////////////////////////////// + +///////////////////////////////////////////// +// Metapsi + +/// No metapsionics +const int METAPSIONIC_NONE = 0x0; +/// Chain Power +const int METAPSIONIC_CHAIN = 0x2; +/// Empower Power +const int METAPSIONIC_EMPOWER = 0x4; +/// Extend Power +const int METAPSIONIC_EXTEND = 0x8; +/// Maximize Power +const int METAPSIONIC_MAXIMIZE = 0x10; +/// Split Psionic Ray +const int METAPSIONIC_SPLIT = 0x20; +/// Twin Power +const int METAPSIONIC_TWIN = 0x40; +/// Widen Power +const int METAPSIONIC_WIDEN = 0x80; +/// Quicken Power +const int METAPSIONIC_QUICKEN = 0x100; + +/// How much PP Chain Power costs to use +const int METAPSIONIC_CHAIN_COST = 6; +/// How much PP Empower Power costs to use +const int METAPSIONIC_EMPOWER_COST = 2; +/// How much PP Extend Power costs to use +const int METAPSIONIC_EXTEND_COST = 2; +/// How much PP Maximize Power costs to use +const int METAPSIONIC_MAXIMIZE_COST = 4; +/// How much PP Split Psionic Ray costs to use +const int METAPSIONIC_SPLIT_COST = 2; +/// How much PP Twin Power costs to use +const int METAPSIONIC_TWIN_COST = 6; +/// How much PP Widen Power costs to use +const int METAPSIONIC_WIDEN_COST = 4; +/// How much PP Quicken Power costs to use +const int METAPSIONIC_QUICKEN_COST = 6; + +/// Internal constant. Value is equal to the lowest metapsionic constant. Used when looping over metapsionic flag variables +const int METAPSIONIC_MIN = 0x2; +/// Internal constant. Value is equal to the highest metapsionic constant. Used when looping over metapsionic flag variables +const int METAPSIONIC_MAX = 0x100; + +/// Chain Power variable name +const string METAPSIONIC_CHAIN_VAR = "PRC_PsiMeta_Chain"; +/// Empower Power variable name +const string METAPSIONIC_EMPOWER_VAR = "PRC_PsiMeta_Empower"; +/// Extend Power variable name +const string METAPSIONIC_EXTEND_VAR = "PRC_PsiMeta_Extend"; +/// Maximize Power variable name +const string METAPSIONIC_MAXIMIZE_VAR = "PRC_PsiMeta_Maximize"; +/// Split Psionic Ray variable name +const string METAPSIONIC_SPLIT_VAR = "PRC_PsiMeta_Split"; +/// Twin Power variable name +const string METAPSIONIC_TWIN_VAR = "PRC_PsiMeta_Twin"; +/// Widen Power variable name +const string METAPSIONIC_WIDEN_VAR = "PRC_PsiMeta_Widen"; +/// Quicken Power variable name +const string METAPSIONIC_QUICKEN_VAR = "PRC_PsiMeta_Quicken"; + +/// The name of the array targets returned by EvaluateChainPower will be stored in +const string PRC_CHAIN_POWER_ARRAY = "PRC_ChainPowerTargets"; + +/// The name of a marker variable that tells that the power being manifested had Quicken Power used on it +const string PRC_POWER_IS_QUICKENED = "PRC_PowerIsQuickened"; + +// +///////////////////////////////////////////// \ No newline at end of file diff --git a/src/include/psi_inc_core.nss b/src/include/psi_inc_core.nss new file mode 100644 index 0000000..9083bee --- /dev/null +++ b/src/include/psi_inc_core.nss @@ -0,0 +1,1668 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Psionic Core Files +//:: psi_inc_core +//:://///////////////////////////////////////////// +/** @file + Core functions removed from + + psi_inc_psifunc + psi_inc_focus (depreciated) + + as they are required by many of the other psi groups + + @author Ornedan/ElgarL + @date Created - 2005.11.10/23.07.2010 + */ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/10 + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Included here to provide the values for the constants below +#include "prc_class_const" + +const int POWER_LIST_PSION = CLASS_TYPE_PSION; +const int POWER_LIST_WILDER = CLASS_TYPE_WILDER; +const int POWER_LIST_PSYWAR = CLASS_TYPE_PSYWAR; +const int POWER_LIST_PSYROG = CLASS_TYPE_PSYCHIC_ROGUE; +const int POWER_LIST_FIST_OF_ZUOKEN = CLASS_TYPE_FIST_OF_ZUOKEN; +const int POWER_LIST_WARMIND = CLASS_TYPE_WARMIND; + +#include "psi_inc_const" + +//:: Test Main +//:: void main (){} + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Attempts to use psionic focus. If the creature was focused, it + * loses the focus. If it has Epic Psionic Focus feats, it will + * be able to use the focus for a number of times equal to the + * number of those feats it has during the next 0.5s + * + * @param oUser Creature expending it's psionic focus + * @return TRUE if the creature was psionically focus or had + * Epic Psionic Focus uses remaining. FALSE otherwise. + */ +int UsePsionicFocus(object oUser = OBJECT_SELF); + +/** + * Sets psionic focus active and triggers feats dependant + * on it. + * + * Feats currently keyed to activity of psionic focus: + * Psionic Dodge + * Speed of Thought + * + * @param oGainee Creature gaining psionic focus. + */ +void GainPsionicFocus(object oGainee = OBJECT_SELF); + +/** + * Gets the number of psionic focus uses the creature has available + * to it at this moment. If the creature is psionically focused, + * the number equal to GetPsionicFocusUsesPerExpenditure(), otherwise + * it is however many focus uses the creature still has remaining of + * that number. + * + * @param oCreature Creaute whose psionic focus use count to evaluate + * @return The total number of times UsePsionicFocus() will + * return TRUE if called at this moment. + */ +int GetPsionicFocusesAvailable(object oCreature = OBJECT_SELF); + +/** + * Calculates the number of times a creature may use it's psionic focus when expending it. + * Base is 1. + * In addition, 1 more use for each Epic Psionic Focus feat the creature has. + * + * @param oCreature Creaute whose psionic focus use count to evaluate + * @return The total number of times UsePsionicFocus() will return + * TRUE for a single expending of psionic focus. + */ +int GetPsionicFocusUsesPerExpenditure(object oCreature = OBJECT_SELF); + +/** + * Sets the given creature's psionic focus off and deactivates all feats keyed to it. + * + * @param oLoser Creature losing it's psionic focus + */ +void LosePsionicFocus(object oLoser = OBJECT_SELF); + +/** + * Checks whether the given creature is psionically focused. + * + * @param oCreature Creature whose psionic focus's state to examine + * @return TRUE if the creature is psionically focused, FALSE + * otherwise. + */ +int GetIsPsionicallyFocused(object oCreature = OBJECT_SELF); + +/** + * Determines the number of feats that would use psionic focus + * when triggered the given creature has active. + * + * Currently accounts for: + * Talented + * Power Specialization + * Power Penetration + * Psionic Endowment + * Chain Power + * Empower Power + * Extend Power + * Maximize Power + * Split Psionic Ray + * Twin Power + * Widen Power + * Quicken Power + * + * @param oCreature Creature whose feats to examine + * @return How many of the listed feats are active + */ +int GetPsionicFocusUsingFeatsActive(object oCreature = OBJECT_SELF); + +/** + * Calculates the DC of the power being currently manifested. + * Base value is 10 + power level + ability modifier in manifesting stat + * + * WARNING: Return value is not defined when a power is not being manifested. + * + */ +int GetManifesterDC(object oManifester = OBJECT_SELF); + +/** + * Determines the spell school matching a discipline according to the + * standard transparency rules. + * Disciplines which have no matching spell school are matched with + * SPELL_SCHOOL_GENERAL. + * + * @param nDiscipline Discipline to find matching spell school for + * @return SPELL_SCHOOL_* of the match + */ +int DisciplineToSpellSchool(int nDiscipline); + +/** + * Determines the discipline matching a spell school according to the + * standard transparency rules. + * Spell schools that have no matching disciplines are matched with + * DISCIPLINE_NONE. + * + * @param nSpellSchool Spell schools to find matching discipline for + * @return DISCIPLINE_* of the match + */ +int SpellSchoolToDiscipline(int nSpellSchool); + +/** + * Determines the discipline of a power, using the School column of spells.2da. + * + * @param nSpellID The spellID of the power to determine the discipline of + * @return DISCIPLINE_* constant. DISCIPLINE_NONE if the power's + * School designation does not match any of the discipline's. + */ +int GetPowerDiscipline(int nSpellID); + +/** + * Determines whether a given power is a power of the Telepahty discipline. + * + * @param nSpellID The spells.2da row of the power. If left to default, + * PRCGetSpellId() is used. + * @return TRUE if the power's discipline is Telepathy, FALSE otherwise + */ +int GetIsTelepathyPower(int nSpellID = -1); + +/** + * Checks whether the PC possesses the feats the given feat has as it's + * prerequisites. Possession of a feat is checked using GetHasFeat(). + * + * + * @param nFeat The feat for which determine the possession of prerequisites + * @param oPC The creature whose feats to check + * @return TRUE if the PC possesses the prerequisite feats AND does not + * already posses nFeat, FALSE otherwise. + */ +int CheckPowerPrereqs(int nFeat, object oPC); + +/** + * Determines the manifester's level in regards to manifester checks to overcome + * spell resistance. + * + * WARNING: Return value is not defined when a power is not being manifested. + * + * @param oManifester A creature manifesting a power at the moment + * @return The creature's manifester level, adjusted to account for + * modifiers that affect spell resistance checks. + */ +int GetPsiPenetration(object oManifester = OBJECT_SELF); + +/** + * Determines whether a given creature possesses the Psionic subtype. + * Ways of possessing the subtype: + * - Being of a naturally psionic race + * - Having class levels in a psionic class + * - Possessing the Wild Talent feat + * + * @param oCreature Creature to test + * @return TRUE if the creature is psionic, FALSE otherwise. + */ +int GetIsPsionicCharacter(object oCreature); + +/** + * Creates the creature weapon for powers like Bite of the Wolf and Claws of the + * Beast. If a creature weapon of the correct type is already present, it is + * used instead of a new one. + * + * Preserving existing weapons may cause problems, if so, make the function instead delete the old object - Ornedan + * + * @param oCreature Creatue whose creature weapons to mess with. + * @param sResRef Resref of the creature weapon. Assumed to be one of the + * PRC creature weapons, so this considered to is also be + * the tag. + * @param nIventorySlot Inventory slot where the creature weapon is to be equipped. + * @param fDuration If a new weapon is created, it will be destroyed after + * this duration. + * + * @return The newly created creature weapon. Or an existing weapon, + * if there was one. + */ +object GetPsionicCreatureWeapon(object oCreature, string sResRef, int nInventorySlot, float fDuration); + +/** + * Applies modifications to a power's damage that depend on some property + * of the target. + * Currently accounts for: + * - Mental Resistance + * - Greater Power Specialization + * - Intellect Fortress + * + * @param oTarget A creature being dealt damage by a power + * @param oManifester The creature manifesting the damaging power + * @param nDamage The amount of damage the creature would be dealt + * + * @param bIsHitPointDamage Is the damage HP damage or something else? + * @param bIsEnergyDamage Is the damage caused by energy or something else? Only relevant if the damage is HP damage. + * + * @return The amount of damage, modified by oTarget's abilities + */ +int GetTargetSpecificChangesToDamage(object oTarget, object oManifester, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE); + +/** + * Gets the manifester level adjustment from the Practiced Manifester feats. + * + * @param oManifester The creature to check + * @param iManifestingClass The CLASS_TYPE* that the power was cast by. + * @param iManifestingLevels The manifester level for the power calculated so far + * ie. BEFORE Practiced Manifester. + */ +int PracticedManifesting(object oManifester, int iManifestingClass, int iManifestingLevels); + +/** + * Determines the given creature's manifester level. If a class is specified, + * then returns the manifester level for that class. Otherwise, returns + * the manifester level for the currently active manifestation. + * + * @param oManifester The creature whose manifester level to determine + * @param nSpecificClass The class to determine the creature's manifester + * level in. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * manifester level in regards to an ongoing manifestation + * is determined instead. + * @param nMaxPowerLevel For learning powers. Practiced Manifester is breaking things otherwise. + * @return The manifester level + */ +int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE); + +/** + * Determines the given creature's highest undmodified manifester level among it's + * manifesting classes. + * + * @param oCreature Creature whose highest manifester level to determine + * @return The highest unmodified manifester level the creature can have + */ +int GetHighestManifesterLevel(object oCreature); + +/** + * Gets the level of the power being currently manifested. + * WARNING: Return value is not defined when a power is not being manifested. + * + * @param oManifester The creature currently manifesting a power + * @return The level of the power being manifested + */ +int GetPowerLevel(object oManifester); + +/** + * Determines a creature's ability score in the manifesting ability of a given + * class. + * + * @param oManifester Creature whose ability score to get + * @param nClass CLASS_TYPE_* constant of a manifesting class + */ +int GetAbilityScoreOfClass(object oManifester, int nClass); + +/** + * Determines from what class's power list the currently being manifested + * power is manifested from. + * + * @param oManifester A creature manifesting a power at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetManifestingClass(object oManifester = OBJECT_SELF); + +/** + * Determines the manifesting ability of a class. + * + * @param nClass CLASS_TYPE_* constant of the class to determine the manifesting stat of + * @return ABILITY_* of the manifesting stat. ABILITY_CHARISMA for non-manifester + * classes. + */ +int GetAbilityOfClass(int nClass); + +/** + * Determines which of the character's classes is their highest or first psionic + * manifesting class, if any. This is the one which gains manifester level raise + * benefits from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first psionic manifesting class, + * CLASS_TYPE_INVALID if the creature does not posses any. + */ +int GetPrimaryPsionicClass(object oCreature = OBJECT_SELF); + +/** + * Calculates how many manifester levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added manifester levels for + * @param nClass Class to calculate PrC caster increase from + * @return The number of manifester levels gained + */ +int GetPsionicPRCLevels(object oCreature, int nClass = CLASS_TYPE_INVALID); + +/** + * Determines the position of a creature's first psionic manifesting class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first psionic class {1, 2, 3} or 0 if + * the creature possesses no levels in psionic classes. + */ +int GetFirstPsionicClassPosition(object oCreature = OBJECT_SELF); + +/** + * Determines whether a given class is a psionic class or not. A psionic + * class is defined as one that gives base manifesting. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a psionic class, FALSE otherwise + */ +int GetIsPsionicClass(int nClass); + +/** + * Gets the amount of manifester levels the given creature is Wild Surging by. + * + * @param oManifester The creature to test + * @return The number of manifester levels added by Wild Surge. 0 if + * Wild Surge is not active. + */ +int GetWildSurge(object oManifester); + +/** + * Gets the highest power level the creature should know + * + * @param oManifester The creature to test + * @return Power level. + */ +int GetMaxPowerLevel(object oManifester); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_unarmed" + +////////////////////////////////////////////////// +/* Global Structures */ +////////////////////////////////////////////////// + +// These are used in psi_inc_psifunc and psi_inc_augment + +/** + * A structure that contains common data used during power manifestation. + */ +struct manifestation{ + /* Generic stuff */ + /// The creature manifesting the power + object oManifester; + /// Whether the manifestation is successfull or not + int bCanManifest; + /// How much Power Points the manifestation costs + int nPPCost; + /// How many psionic focus uses the manifester would have remaining at a particular point in the manifestation + int nPsiFocUsesRemain; + /// The creature's manifester level in regards to this power + int nManifesterLevel; + /// The power's spell ID + int nSpellID; + + /* Augmentation */ + /// How many times the first augmentation option of the power is used + int nTimesAugOptUsed_1; + /// How many times the second augmentation option of the power is used + int nTimesAugOptUsed_2; + /// How many times the third augmentation option of the power is used + int nTimesAugOptUsed_3; + /// How many times the fourth augmentation option of the power is used + int nTimesAugOptUsed_4; + /// How many times the fifth augmentation option of the power is used + int nTimesAugOptUsed_5; + /// How many times the PP used for augmentation triggered the generic augmentation of the power + int nTimesGenericAugUsed; + + /* Metapsionics */ + /// Whether Chain Power was used with this manifestation + int bChain; + /// Whether Empower Power was used with this manifestation + int bEmpower; + /// Whether Extend Power was used with this manifestation + int bExtend; + /// Whether Maximize Power was used with this manifestation + int bMaximize; + /// Whether Split Psionic Ray was used with this manifestation + int bSplit; + /// Whether Twin Power was used with this manifestation + int bTwin; + /// Whether Widen Power was used with this manifestation + int bWiden; + /// Whether Quicken Power was used with this manifestation + int bQuicken; +}; + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + + + +/* Begin PSI FOCUS */ +////////////////////////////////////////////////////// + +int UsePsionicFocus(object oUser = OBJECT_SELF) +{ + int bToReturn = FALSE; + // This does not expend your psionic focus, but rather the item's + if (GetLocalInt(oUser, "SimpleBow_Focus")) + { + DeleteLocalInt(oUser, "SimpleBow_Focus"); + return TRUE; + } + // Next, check if we have focus on + else if(GetLocalInt(oUser, PSIONIC_FOCUS)) + { + SetLocalInt(oUser, "PsionicFocusUses", GetPsionicFocusUsesPerExpenditure(oUser) - 1); + DelayCommand(0.5f, DeleteLocalInt(oUser, "PsionicFocusUses")); + SendMessageToPCByStrRef(oUser, 16826414); // "You have used your Psionic Focus" + + bToReturn = TRUE; + } + // We don't. Check if there are uses remaining + else if(GetLocalInt(oUser, "PsionicFocusUses")) + { + SetLocalInt(oUser, "PsionicFocusUses", GetLocalInt(oUser, "PsionicFocusUses") - 1); + + bToReturn = TRUE; + } + + // Lose focus if it was used + if(bToReturn) LosePsionicFocus(oUser); + + return bToReturn; +} + +void GainPsionicFocus(object oGainee = OBJECT_SELF) +{ + SetLocalInt(oGainee, PSIONIC_FOCUS, TRUE); + + // Speed Of Thought + if(GetHasFeat(FEAT_SPEED_OF_THOUGHT, oGainee)) + { + // Check for heavy armor before adding the bonus now + if(GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oGainee)) < 6) + AssignCommand(oGainee, ActionCastSpellAtObject(SPELL_FEAT_SPEED_OF_THOUGHT_BONUS, oGainee, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + + // Schedule a script to remove the bonus should they equip heavy armor + AddEventScript(oGainee, EVENT_ONPLAYEREQUIPITEM, "psi_spdfthgt_oeq", TRUE, FALSE); + // Schedule another script to add the bonus back if the unequip the armor + AddEventScript(oGainee, EVENT_ONPLAYERUNEQUIPITEM, "psi_spdfthgt_ueq", TRUE, FALSE); + } + // Psionic Dodge + if(GetHasFeat(FEAT_PSIONIC_DODGE, oGainee)) + SetCompositeBonus(GetPCSkin(oGainee), "PsionicDodge", 1, ITEM_PROPERTY_AC_BONUS); + + //Strength of Two - Kalashtar racial feat + if(GetHasFeat(FEAT_STRENGTH_OF_TWO, oGainee)) + { + SetCompositeBonus(GetPCSkin(oGainee), "StrengthOfTwo", 1, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_WILL); + } + // Danger Sense abilities for Psychic Rogue + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 5) + ExecuteScript("psi_psyrog_dngr", oGainee); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 7) // Uncanny Dodge + IPSafeAddItemProperty(GetPCSkin(oGainee), PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE1), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oGainee) >= 9) // Improved Uncanny Dodge + IPSafeAddItemProperty(GetPCSkin(oGainee), PRCItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE2), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_1d6, oGainee)) + { + int nPsySneak = 1; + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_2d6, oGainee)) + nPsySneak += 2; + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_3d6, oGainee)) + nPsySneak += 3; + + SetLocalInt(oGainee, "PsyRogueSneak",nPsySneak); + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oGainee)); + } +} + +int GetPsionicFocusesAvailable(object oCreature = OBJECT_SELF) +{ + // If the creature has a psionic focus active, return the maximum + if(GetLocalInt(oCreature, PSIONIC_FOCUS)) + return GetPsionicFocusUsesPerExpenditure(oCreature); + // Otherwise, return the amount currently remaining + else + return GetLocalInt(oCreature, "PsionicFocusUses"); +} + +int GetPsionicFocusUsesPerExpenditure(object oCreature = OBJECT_SELF) +{ + int nFocusUses = 1; + int i; + for(i = FEAT_EPIC_PSIONIC_FOCUS_1; i <= FEAT_EPIC_PSIONIC_FOCUS_10; i++) + if(GetHasFeat(i, oCreature)) nFocusUses++; + + return nFocusUses; +} + +void LosePsionicFocus(object oLoser = OBJECT_SELF) +{ + // Only remove focus if it's present + if(GetLocalInt(oLoser, PSIONIC_FOCUS)) + { + SetLocalInt(oLoser, PSIONIC_FOCUS, FALSE); + + // Loss of Speed of Thought effects + PRCRemoveSpellEffects(SPELL_FEAT_SPEED_OF_THOUGHT_BONUS, oLoser, oLoser); + RemoveEventScript(oLoser, EVENT_ONPLAYEREQUIPITEM, "psi_spdfthgt_oeq", TRUE); + RemoveEventScript(oLoser, EVENT_ONPLAYERUNEQUIPITEM, "psi_spdfthgt_ueq", TRUE); + // Loss of Psionic Dodge effects + SetCompositeBonus(GetPCSkin(oLoser), "PsionicDodge", 0, ITEM_PROPERTY_AC_BONUS); + // Loss of Strength of Two effects + SetCompositeBonus(GetPCSkin(oLoser), "StrengthOfTwo", 0, ITEM_PROPERTY_SAVING_THROW_BONUS, SAVING_THROW_WILL); + + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 5) + PRCRemoveSpellEffects(POWER_DANGERSENSE, oLoser, oLoser); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 7) + RemoveItemProperty(GetPCSkin(oLoser), ItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE1)); + if(GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oLoser) >= 9) + RemoveItemProperty(GetPCSkin(oLoser), ItemPropertyBonusFeat(IP_CONST_FEAT_UNCANNY_DODGE2)); + + // Inform oLoser about the event + FloatingTextStrRefOnCreature(16826415, oLoser, FALSE); // "You have lost your Psionic Focus" + if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_1d6, oLoser)) + { + DeleteLocalInt(oLoser, "PsyRogueSneak"); + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oLoser)); + } + } +} + +int GetIsPsionicallyFocused(object oCreature = OBJECT_SELF) +{ + return GetLocalInt(oCreature, PSIONIC_FOCUS); +} + +int GetPsionicFocusUsingFeatsActive(object oCreature = OBJECT_SELF) +{ + int nFeats; + + if(GetLocalInt(oCreature, "TalentedActive")) nFeats++; + if(GetLocalInt(oCreature, "PowerSpecializationActive")) nFeats++; + if(GetLocalInt(oCreature, "PowerPenetrationActive")) nFeats++; + if(GetLocalInt(oCreature, "PsionicEndowmentActive")) nFeats++; + + if(GetLocalInt(oCreature, METAPSIONIC_CHAIN_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_EMPOWER_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_EXTEND_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_MAXIMIZE_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_SPLIT_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_TWIN_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_WIDEN_VAR)) nFeats++; + if(GetLocalInt(oCreature, METAPSIONIC_QUICKEN_VAR)) nFeats++; + + return nFeats; +} + +////////////////////////////////////////////////////// +/* END PSI FOCUS */ +////////////////////////////////////////////////////// + +int GetManifesterDC(object oManifester = OBJECT_SELF) +{ + int nClass = GetManifestingClass(oManifester); + int nDC = 10; + nDC += GetPowerLevel(oManifester); + nDC += GetAbilityModifier(GetAbilityOfClass(nClass), oManifester); + + // Stuff that applies only to powers, not psi-like abilities goes inside + if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) + { + if (GetLocalInt(oManifester, "PsionicEndowmentActive") == TRUE && UsePsionicFocus(oManifester)) + { + nDC += GetHasFeat(FEAT_GREATER_PSIONIC_ENDOWMENT, oManifester) ? 4 : 2; + } + } + + // Needed to do some adjustments here. + object oTarget = PRCGetSpellTargetObject(); + int nPower = PRCGetSpellId(); + + // Other DC adjustments + // Soul Manifester + nDC += Soulcaster(oManifester, PRCGetSpellId()); + // Charming Veil meld + if(GetHasSpellEffect(MELD_CHARMING_VEIL, oManifester) && (GetIsOfSubschool(nPower, SUBSCHOOL_CHARM) || GetIsOfSubschool(nPower, SUBSCHOOL_COMPULSION))) nDC += GetEssentiaInvested(oManifester, MELD_CHARMING_VEIL)+1; + // Soul Eater + nDC += (GetLocalInt(oManifester, "PRC_SoulEater_HasDrained") && GetLevelByClass(CLASS_TYPE_SOUL_EATER, oManifester) >= 10) ? 2 : 0; + // Closed Mind + if(GetHasFeat(FEAT_CLOSED_MIND, oTarget)) nDC -= 2; + // Strong Mind + if(GetHasFeat(FEAT_STRONG_MIND, oTarget)) nDC -= 3; + // Fist of Dal Quor + if(GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oTarget) >= 4) nDC -= 2; + + return nDC; +} + +int DisciplineToSpellSchool(int nDiscipline) +{ + int nSpellSchool = SPELL_SCHOOL_GENERAL; + + switch(nDiscipline) + { + case DISCIPLINE_CLAIRSENTIENCE: nSpellSchool = SPELL_SCHOOL_DIVINATION; break; + case DISCIPLINE_METACREATIVITY: nSpellSchool = SPELL_SCHOOL_CONJURATION; break; + case DISCIPLINE_PSYCHOKINESIS: nSpellSchool = SPELL_SCHOOL_EVOCATION; break; + case DISCIPLINE_PSYCHOMETABOLISM: nSpellSchool = SPELL_SCHOOL_TRANSMUTATION; break; + case DISCIPLINE_TELEPATHY: nSpellSchool = SPELL_SCHOOL_ENCHANTMENT; break; + + default: nSpellSchool = SPELL_SCHOOL_GENERAL; break; + } + + return nSpellSchool; +} + +int SpellSchoolToDiscipline(int nSpellSchool) +{ + int nDiscipline = DISCIPLINE_NONE; + + switch(nSpellSchool) + { + case SPELL_SCHOOL_GENERAL: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_ABJURATION: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_CONJURATION: nDiscipline = DISCIPLINE_METACREATIVITY; break; + case SPELL_SCHOOL_DIVINATION: nDiscipline = DISCIPLINE_CLAIRSENTIENCE; break; + case SPELL_SCHOOL_ENCHANTMENT: nDiscipline = DISCIPLINE_TELEPATHY; break; + case SPELL_SCHOOL_EVOCATION: nDiscipline = DISCIPLINE_PSYCHOKINESIS; break; + case SPELL_SCHOOL_ILLUSION: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_NECROMANCY: nDiscipline = DISCIPLINE_NONE; break; + case SPELL_SCHOOL_TRANSMUTATION: nDiscipline = DISCIPLINE_PSYCHOMETABOLISM; break; + + default: nDiscipline = DISCIPLINE_NONE; + } + + return nDiscipline; +} + +int GetPowerDiscipline(int nSpellID) +{ + string sSpellSchool = Get2DACache("spells", "School", nSpellID);//lookup_spell_school(nSpellID); + int nDiscipline; + + if (sSpellSchool == "A") nDiscipline = DISCIPLINE_NONE; + else if (sSpellSchool == "C") nDiscipline = DISCIPLINE_METACREATIVITY; + else if (sSpellSchool == "D") nDiscipline = DISCIPLINE_CLAIRSENTIENCE; + else if (sSpellSchool == "E") nDiscipline = DISCIPLINE_TELEPATHY; + else if (sSpellSchool == "V") nDiscipline = DISCIPLINE_PSYCHOKINESIS; + else if (sSpellSchool == "I") nDiscipline = DISCIPLINE_NONE; + else if (sSpellSchool == "N") nDiscipline = DISCIPLINE_NONE; + else if (sSpellSchool == "T") nDiscipline = DISCIPLINE_PSYCHOMETABOLISM; + else if (sSpellSchool == "G") nDiscipline = DISCIPLINE_PSYCHOPORTATION; + + return nDiscipline; +} + +int GetIsTelepathyPower(int nSpellID = -1) +{ + if(nSpellID == -1) nSpellID = PRCGetSpellId(); + + return GetPowerDiscipline(nSpellID) == DISCIPLINE_TELEPATHY; +} + +int CheckPowerPrereqs(int nFeat, object oPC) +{ + // Having the power already automatically disqualifies one from taking it again + if(GetHasFeat(nFeat, oPC)) + return FALSE; + // We assume that the 2da is correctly formatted, and as such, a prereq slot only contains + // data if the previous slots in order also contains data. + // ie, no PREREQFEAT2 if PREREQFEAT1 is empty + if(Get2DACache("feat", "PREREQFEAT1", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "PREREQFEAT1", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "PREREQFEAT2", nFeat) != "" + && !GetHasFeat(StringToInt(Get2DACache("feat", "PREREQFEAT2", nFeat)), oPC)) + return FALSE; + } + + if(Get2DACache("feat", "OrReqFeat0", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat0", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat1", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat1", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat2", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat2", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat3", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat3", nFeat)), oPC)) + return FALSE; + if(Get2DACache("feat", "OrReqFeat4", nFeat) != "") + { + if(!GetHasFeat(StringToInt(Get2DACache("feat", "OrReqFeat4", nFeat)), oPC)) + return FALSE; + } } } } } + + //if youve reached this far then return TRUE + return TRUE; +} + +int GetPsiPenetration(object oManifester = OBJECT_SELF) +{ + int nPen = GetManifesterLevel(oManifester); + + // The stuff inside applies only to normal manifestation, not psi-like abilities + if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) + { + // Check for Power Pen feats being used + if(GetLocalInt(oManifester, "PowerPenetrationActive") == TRUE && UsePsionicFocus(oManifester)) + { + nPen += GetHasFeat(FEAT_GREATER_POWER_PENETRATION, oManifester) ? 8 : 4; + } + } + + return nPen; +} + +int GetIsPsionicCharacter(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_PSION, oCreature) || + GetLevelByClass(CLASS_TYPE_PSYWAR, oCreature) || + GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oCreature) || + GetLevelByClass(CLASS_TYPE_WILDER, oCreature) || + GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature) || + GetLevelByClass(CLASS_TYPE_WARMIND, oCreature) || + GetHasFeat(FEAT_WILD_TALENT, oCreature) || + GetHasFeat(FEAT_KALASHTAR_PP, oCreature) || + GetHasFeat(FEAT_NATPSIONIC_1, oCreature) || + GetHasFeat(FEAT_NATPSIONIC_2, oCreature) || + GetHasFeat(FEAT_NATPSIONIC_3, oCreature) + // Racial psionicity signifying feats go here + ); +} + +int IsHiddenTalent(object oPC = OBJECT_SELF) +{ + if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC) || + //GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) + { + return TRUE; + } + else + { + return FALSE; + } +} + + +void LocalCleanExtraFists(object oCreature) +{ + int iIsCWeap, iIsEquip; + + object oClean = GetFirstItemInInventory(oCreature); + + while (GetIsObjectValid(oClean)) + { + iIsCWeap = GetIsPRCCreatureWeapon(oClean); + + iIsEquip = oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_L) || + oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_R) || + oClean == GetItemInSlot(INVENTORY_SLOT_CWEAPON_B); + + if (iIsCWeap && !iIsEquip) + { + DestroyObject(oClean); + } + + oClean = GetNextItemInInventory(oCreature); + } +} + +object GetPsionicCreatureWeapon(object oCreature, string sResRef, int nInventorySlot, float fDuration) +{ + int bCreatedWeapon = FALSE; + object oCWeapon = GetItemInSlot(nInventorySlot, oCreature); + + RemoveUnarmedAttackEffects(oCreature); + // Make sure they can actually equip them + UnarmedFeats(oCreature); + + // Determine if a creature weapon of the proper type already exists in the slot + if(!GetIsObjectValid(oCWeapon) || + GetStringUpperCase(GetTag(oCWeapon)) != GetStringUpperCase(sResRef) // Hack: The resref's and tags of the PRC creature weapons are the same + ) + { + if (GetHasItem(oCreature, sResRef)) + { + oCWeapon = GetItemPossessedBy(oCreature, sResRef); + SetIdentified(oCWeapon, TRUE); + //AssignCommand(oCreature, ActionEquipItem(oCWeapon, INVENTORY_SLOT_CWEAPON_L)); + ForceEquip(oCreature, oCWeapon, nInventorySlot); + } + else + { + oCWeapon = CreateItemOnObject(sResRef, oCreature); + SetIdentified(oCWeapon, TRUE); + //AssignCommand(oCreature, ActionEquipItem(oCWeapon, INVENTORY_SLOT_CWEAPON_L)); + ForceEquip(oCreature, oCWeapon, nInventorySlot); + bCreatedWeapon = TRUE; + } + } + + + // Clean up the mess of extra fists made on taking first level. + DelayCommand(6.0f, LocalCleanExtraFists(oCreature)); + + // Weapon finesse or intuitive attack? + SetLocalInt(oCreature, "UsingCreature", TRUE); + ExecuteScript("prc_intuiatk", oCreature); + DelayCommand(1.0f, DeleteLocalInt(oCreature, "UsingCreature")); + + // Add OnHitCast: Unique if necessary + if(GetHasFeat(FEAT_REND, oCreature)) + IPSafeAddItemProperty(oCWeapon, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + + // This adds creature weapon finesse + ApplyUnarmedAttackEffects(oCreature); + + // Destroy the weapon if it was created by this function + if(bCreatedWeapon) + DestroyObject(oCWeapon, (fDuration + 6.0)); + + return oCWeapon; +} + +int GetTargetSpecificChangesToDamage(object oTarget, object oManifester, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE) +{ + // Greater Power Specialization - +2 damage on all HP-damaging powers when target is within 30ft + if(bIsHitPointDamage && + GetHasFeat(FEAT_GREATER_POWER_SPECIALIZATION, oManifester) && + GetDistanceBetween(oTarget, oManifester) <= FeetToMeters(30.0f) + ) + nDamage += 2; + // Intellect Fortress - Halve damage dealt by powers that allow PR. Goes before DR (-like) reductions + if(GetLocalInt(oTarget, "PRC_Power_IntellectFortress_Active") && + Get2DACache("spells", "ItemImmunity", PRCGetSpellId()) == "1" + ) + nDamage /= 2; + // Mental Resistance - 3 damage less for all non-energy damage and ability damage + if(GetHasFeat(FEAT_MENTAL_RESISTANCE, oTarget) && !bIsEnergyDamage) + nDamage -= 3; + + // Reasonable return values only + if(nDamage < 0) nDamage = 0; + + if (GetIsMeldBound(oManifester, MELD_PSYCHIC_FOCUS) == CHAKRA_THROAT && nDamage > 0 && !GetLocalInt(oManifester, "PsychicFocusMeld") && bIsHitPointDamage) + { + SetLocalInt(oManifester, "PsychicFocusMeld", TRUE); + DelayCommand(6.0, DeleteLocalInt(oManifester, "PsychicFocusMeld")); + int nClass = GetMeldShapedClass(oManifester, MELD_PSYCHIC_FOCUS); + int nDC = GetMeldshaperDC(oManifester, nClass, MELD_PSYCHIC_FOCUS); + if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oTarget, 6.0); + } + + return nDamage; +} + +int PracticedManifesting(object oManifester, int iManifestingClass, int iManifestingLevels) +{ + int nFeat; + int iAdjustment = GetHitDice(oManifester) - iManifestingLevels; + iAdjustment = iAdjustment > 4 ? 4 : iAdjustment < 0 ? 0 : iAdjustment; + + switch(iManifestingClass) + { + case CLASS_TYPE_PSION: nFeat = FEAT_PRACTICED_MANIFESTER_PSION; break; + case CLASS_TYPE_PSYWAR: nFeat = FEAT_PRACTICED_MANIFESTER_PSYWAR; break; + case CLASS_TYPE_PSYCHIC_ROGUE: nFeat = FEAT_PRACTICED_MANIFESTER_PSYROG; break; + case CLASS_TYPE_WILDER: nFeat = FEAT_PRACTICED_MANIFESTER_WILDER; break; + case CLASS_TYPE_WARMIND: nFeat = FEAT_PRACTICED_MANIFESTER_WARMIND; break; + case CLASS_TYPE_FIST_OF_ZUOKEN: nFeat = FEAT_PRACTICED_MANIFESTER_FIST_OF_ZUOKEN; break; + default: nFeat = -1; + } + + if(GetHasFeat(nFeat, oManifester)) + return iAdjustment; + + return 0; +} + +int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE) +{ + int nLevel; + int nAdjust = GetLocalInt(oManifester, PRC_CASTERLEVEL_ADJUSTMENT); + nAdjust -= GetLocalInt(oManifester, "WoLManifPenalty"); + + if(DEBUG) DoDebug("GetManifesterLevel Specific Class: " + IntToString(nSpecificClass), oManifester); + + // The function user needs to know the character's manifester level in a specific class + // instead of whatever the character last manifested a power as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + if(GetIsPsionicClass(nSpecificClass)) + { + nLevel = GetLevelByClass(nSpecificClass, oManifester); + // Add levels from +ML PrCs only for the nSpecificClass + nLevel += GetPsionicPRCLevels(oManifester, nSpecificClass); + +/* if(nSpecificClass == GetPrimaryPsionicClass(oManifester)) + nLevel += GetPsionicPRCLevels(oManifester); */ + + // Psionic vestiges are tucked in here to override things. + // This assumes that there will never be a psion with this spell effect manifesting things + if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AretePsion"); + nMaxPowerLevel = TRUE; + } + if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "TheTriadPsion"); + nMaxPowerLevel = TRUE; + } + if (nSpecificClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AbysmPsion"); + nMaxPowerLevel = TRUE; + } + if(DEBUG) DoDebug("GetManifesterLevel Specific Class Manif Level: " + IntToString(nLevel), oManifester); + // This is for learning powers, we need to ignore some adjustments + if (nMaxPowerLevel) return nLevel; + + nLevel += PracticedManifesting(oManifester, nSpecificClass, nLevel); //gotta be the last one + + return nLevel + nAdjust; + } + // A character's manifester level gained from non-manifesting classes is always a nice, round zero + else + return 0; + } + + // Item Spells + if(GetItemPossessor(GetSpellCastItem()) == oManifester) + { + if(DEBUG) SendMessageToPC(oManifester, "Item casting at level " + IntToString(GetCasterLevel(oManifester))); + + return GetCasterLevel(oManifester) + nAdjust; + } + + // For when you want to assign the caster level. + else if(GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE) != 0) + { + if(DEBUG) SendMessageToPC(oManifester, "Forced-level manifesting at level " + IntToString(GetCasterLevel(oManifester))); + + DelayCommand(1.0, DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE)); + nLevel = GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE); + } + else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID) + { + //Gets the manifesting class + int nManifestingClass = GetManifestingClass(oManifester); + if(DEBUG) DoDebug("Manifesting Class #2: " + IntToString(nManifestingClass), oManifester); + nLevel = GetLevelByClass(nManifestingClass, oManifester); + // Add levels from +ML PrCs only for the first manifesting class + nLevel += GetPsionicPRCLevels(oManifester, nManifestingClass); + //nLevel += nManifestingClass == GetPrimaryPsionicClass(oManifester) ? GetPsionicPRCLevels(oManifester) : 0; + + // Psionic vestiges are tucked in here to override things. + // This assumes that there will never be a psion with this spell effect manifesting things + if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AretePsion"); + nMaxPowerLevel = TRUE; + } + if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "TheTriadPsion"); + nMaxPowerLevel = TRUE; + } + if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && !nMaxPowerLevel) + { + nLevel = GetLocalInt(oManifester, "AbysmPsion"); + nMaxPowerLevel = TRUE; + } + if(DEBUG) DoDebug("GetManifesterLevel Unspecific Class Manif Level: " + IntToString(nLevel), oManifester); + // This is for learning powers, we need to ignore some adjustments + if (nMaxPowerLevel) return nLevel; + + nLevel += PracticedManifesting(oManifester, nManifestingClass, nLevel); //gotta be the last one +// if(DEBUG) DoDebug("Level gotten via GetLevelByClass: " + IntToString(nLevel), oManifester); + } + + // If you have a primary psionic class and no manifester level yet, get levels based on that + if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) + { + int nClass = GetPrimaryPsionicClass(oManifester); + nLevel = GetLevelByClass(nClass, oManifester); + nLevel += GetPsionicPRCLevels(oManifester, nClass); + nLevel += PracticedManifesting(oManifester, nClass, nLevel); //gotta be the last one + } + + // If everything else fails, you are not a manifester + if(nLevel == 0) + { + if(DEBUG) DoDebug("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + //else WriteTimestampedLogEntry("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + + return 0; + } + + + // The bonuses inside only apply to normal manifestation + if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) + { + //Adding wild surge + int nSurge = GetWildSurge(oManifester); + if (nSurge > 0) nLevel += nSurge; + + // Adding overchannel + int nOverchannel = GetLocalInt(oManifester, PRC_OVERCHANNEL); + if(nOverchannel > 0) nLevel += nOverchannel; + + // Adding Soul Manifester + nLevel += Soulcaster(oManifester, PRCGetSpellId()); + } + + nLevel += nAdjust; + + // This spam is technically no longer necessary once the manifester level getting mechanism has been confirmed to work + if(DEBUG) FloatingTextStringOnCreature("Manifester Level: " + IntToString(nLevel), oManifester, FALSE); + + return nLevel; +} + +int GetHighestManifesterLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetManifesterLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestManifesterLevel(object oCreature) +{ + return PRCMax(PRCMax(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetManifesterLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetPowerLevel(object oManifester) +{ + return GetLocalInt(oManifester, PRC_POWER_LEVEL); +} + +int GetAbilityScoreOfClass(object oManifester, int nClass) +{ + return GetAbilityScore(oManifester, GetAbilityOfClass(nClass)); +} + +int GetManifestingClass(object oManifester = OBJECT_SELF) +{ + return GetLocalInt(oManifester, PRC_MANIFESTING_CLASS) - 1; +} + +int GetAbilityOfClass(int nClass) +{ + switch(nClass) + { + case CLASS_TYPE_DIAMOND_DRAGON: + case CLASS_TYPE_PSION: + case CLASS_TYPE_PSYCHIC_ROGUE: + return ABILITY_INTELLIGENCE; + case CLASS_TYPE_PSYWAR: + case CLASS_TYPE_FIST_OF_ZUOKEN: + case CLASS_TYPE_WARMIND: + return ABILITY_WISDOM; + case CLASS_TYPE_WILDER: + return ABILITY_CHARISMA; + } + + // If there's no class, it's racial. Use Charisma + return ABILITY_CHARISMA; +} + +int GetPrimaryPsionicClass(object oCreature = OBJECT_SELF) +{ + int nClass; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int iPsionicPos = GetFirstPsionicClassPosition(oCreature); + if (!iPsionicPos) return CLASS_TYPE_INVALID; // no Psionic casting class + + nClass = GetClassByPosition(iPsionicPos, oCreature); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsPsionicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsPsionicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsPsionicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsPsionicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsPsionicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsPsionicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsPsionicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsPsionicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetPsionicPRCLevels(object oCreature, int nClass = CLASS_TYPE_INVALID) +{ + int nLevel = 0; + + if(nClass == CLASS_TYPE_FIST_OF_ZUOKEN) + { + if((GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature) > 0 ) && GetHasFeat(FEAT_CEREBREMANCER_MANIFEST_FOZ, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) > 0 ) && GetHasFeat(FEAT_DIAMOND_DRAGON_MANIFEST_FOZ, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Diamond Dragon + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) > 0 ) && GetHasFeat(FEAT_IRONMIND_MANIFEST_FOZ, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Iron Mind + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature) > 0 ) && GetHasFeat(FEAT_PSYCHIC_THEURGE_MANIFEST_FOZ, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) > 0 ) && GetHasFeat(FEAT_SANCTIFIED_MIND_MANIFEST_FOZ, oCreature)) + { + //:: No manifester level boost at level 1 for Sanctified Mind + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + if((GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) > 0 ) && GetHasFeat(FEAT_SHADOWMIND_MANIFEST_FOZ, oCreature)) + { + //:: No manifester level boost at levels 2, 5 and 8 for Shadow Mind + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature) > 0 ) && GetHasFeat(FEAT_SOULCASTER_MANIFEST_FOZ, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + } + } + + else if(nClass == CLASS_TYPE_PSION) + { + if((GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature) > 0 ) && GetHasFeat(FEAT_CEREBREMANCER_MANIFEST_PSION, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) > 0 ) && GetHasFeat(FEAT_DIAMOND_DRAGON_MANIFEST_PSION, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Diamond Dragon + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) > 0 ) && GetHasFeat(FEAT_IRONMIND_MANIFEST_PSION, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Iron Mind + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature) > 0 ) && GetHasFeat(FEAT_PSYCHIC_THEURGE_MANIFEST_PSION, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) > 0 ) && GetHasFeat(FEAT_SANCTIFIED_MIND_MANIFEST_PSION, oCreature)) + { + //:: No manifester level boost at level 1 for Sanctified Mind + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + if((GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) > 0 ) && GetHasFeat(FEAT_SHADOWMIND_MANIFEST_PSION, oCreature)) + { + //:: No manifester level boost at levels 2, 5 and 8 for Shadow Mind + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature) > 0 ) && GetHasFeat(FEAT_SOULCASTER_MANIFEST_PSION, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) > 0 ) && GetHasFeat(FEAT_THRALLHERD_MANIFEST_PSION, oCreature)) + { + //:: No manifester level boost at level 1 and 10 for Thrallherd + nLevel += GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) >= 10) nLevel -= 1; + } + } + + else if(nClass == CLASS_TYPE_PSYCHIC_ROGUE) + { + if((GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature) > 0 ) && GetHasFeat(FEAT_CEREBREMANCER_MANIFEST_PSYROUGE, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) > 0 ) && GetHasFeat(FEAT_DIAMOND_DRAGON_MANIFEST_PSYROUGE, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Diamond Dragon + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) > 0 ) && GetHasFeat(FEAT_IRONMIND_MANIFEST_PSYROUGE, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Iron Mind + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature) > 0 ) && GetHasFeat(FEAT_PSYCHIC_THEURGE_MANIFEST_PSYROUGE, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) > 0 ) && GetHasFeat(FEAT_SANCTIFIED_MIND_MANIFEST_PSYROUGE, oCreature)) + { + //:: No manifester level boost at level 1 for Sanctified Mind + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + if((GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) > 0 ) && GetHasFeat(FEAT_SHADOWMIND_MANIFEST_PSYROUGE, oCreature)) + { + //:: No manifester level boost at levels 2, 5 and 8 for Shadow Mind + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature) > 0 ) && GetHasFeat(FEAT_SOULCASTER_MANIFEST_PSYROUGE, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + } + } + + else if(nClass == CLASS_TYPE_PSYWAR) + { + if((GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature) > 0 ) && GetHasFeat(FEAT_CEREBREMANCER_MANIFEST_PSYWAR, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) > 0 ) && GetHasFeat(FEAT_DIAMOND_DRAGON_MANIFEST_PSYWAR, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Diamond Dragon + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) > 0 ) && GetHasFeat(FEAT_IRONMIND_MANIFEST_PSYWAR, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Iron Mind + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature) > 0 ) && GetHasFeat(FEAT_PSYCHIC_THEURGE_MANIFEST_PSYWAR, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) > 0 ) && GetHasFeat(FEAT_SANCTIFIED_MIND_MANIFEST_PSYWAR, oCreature)) + { + //:: No manifester level boost at level 1 for Sanctified Mind + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + if((GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) > 0 ) && GetHasFeat(FEAT_SHADOWMIND_MANIFEST_PSYWAR, oCreature)) + { + //:: No manifester level boost at levels 2, 5 and 8 for Shadow Mind + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature) > 0 ) && GetHasFeat(FEAT_SOULCASTER_MANIFEST_PSYWAR, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + } + } + + else if(nClass == CLASS_TYPE_WARMIND) + { + if((GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature) > 0 ) && GetHasFeat(FEAT_CEREBREMANCER_MANIFEST_WARMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) > 0 ) && GetHasFeat(FEAT_DIAMOND_DRAGON_MANIFEST_WARMIND, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Diamond Dragon + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) > 0 ) && GetHasFeat(FEAT_IRONMIND_MANIFEST_WARMIND, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Iron Mind + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature) > 0 ) && GetHasFeat(FEAT_PSYCHIC_THEURGE_MANIFEST_WARMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) > 0 ) && GetHasFeat(FEAT_SANCTIFIED_MIND_MANIFEST_WARMIND, oCreature)) + { + //:: No manifester level boost at level 1 for Sanctified Mind + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + if((GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) > 0 ) && GetHasFeat(FEAT_SHADOWMIND_MANIFEST_WARMIND, oCreature)) + { + //:: No manifester level boost at levels 2, 5 and 8 for Shadow Mind + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature) > 0 ) && GetHasFeat(FEAT_SOULCASTER_MANIFEST_WARMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + } + } + + else if(nClass == CLASS_TYPE_WILDER) + { + if((GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature) > 0 ) && GetHasFeat(FEAT_CEREBREMANCER_MANIFEST_WILDER, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) > 0 ) && GetHasFeat(FEAT_DIAMOND_DRAGON_MANIFEST_WILDER, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Diamond Dragon + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) > 0 ) && GetHasFeat(FEAT_IRONMIND_MANIFEST_WILDER, oCreature)) + { + //:: No manifester level boost at level 1 and 6 for Iron Mind + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature) > 0 ) && GetHasFeat(FEAT_PSYCHIC_THEURGE_MANIFEST_WILDER, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + } + + if((GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) > 0 ) && GetHasFeat(FEAT_SANCTIFIED_MIND_MANIFEST_WILDER, oCreature)) + { + //:: No manifester level boost at level 1 for Sanctified Mind + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } + + if((GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) > 0 ) && GetHasFeat(FEAT_SHADOWMIND_MANIFEST_WILDER, oCreature)) + { + //:: No manifester level boost at levels 2, 5 and 8 for Shadow Mind + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + + if((GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature) > 0 ) && GetHasFeat(FEAT_SOULCASTER_MANIFEST_WILDER, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + } + } + + else { } + +/* // Cerebremancer and Psychic Theurge add manifester levels on each level + nLevel += GetLevelByClass(CLASS_TYPE_CEREBREMANCER, oCreature); + nLevel += GetLevelByClass(CLASS_TYPE_PSYCHIC_THEURGE, oCreature); + if (GetFirstPsionicClassPosition(oCreature)) nLevel += GetLevelByClass(CLASS_TYPE_SOULCASTER, oCreature); + + // No manifester level boost at level 1 and 10 for Thrallherd + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_THRALLHERD, oCreature) >= 10) nLevel -= 1; + } + // No manifester level boost at levels 2, 5 and 8 for Shadow Mind + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature); + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 2) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 5) nLevel -= 1; + if(GetLevelByClass(CLASS_TYPE_SHADOWMIND, oCreature) >= 8) nLevel -= 1; + } + // No manifester level boost at level 1 and 6 for Iron Mind + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_IRONMIND, oCreature) >= 6) nLevel -= 1; + } + // No manifester level boost at level 1 and 6 for Diamond Dragon + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) - 1; + if(GetLevelByClass(CLASS_TYPE_DIAMOND_DRAGON, oCreature) >= 6) nLevel -= 1; + } + // No manifester level boost at level 1 for Sanctified Mind + if(GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature)) + { + nLevel += GetLevelByClass(CLASS_TYPE_SANCTIFIED_MIND, oCreature) - 1; + } */ + + return nLevel; +} + +int GetFirstPsionicClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsPsionicClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsPsionicClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsPsionicClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsPsionicClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsPsionicClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsPsionicClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsPsionicClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsPsionicClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int GetIsPsionicClass(int nClass) +{ + return (nClass==CLASS_TYPE_PSION + || nClass==CLASS_TYPE_PSYWAR + || nClass==CLASS_TYPE_PSYCHIC_ROGUE + || nClass==CLASS_TYPE_WILDER + || nClass==CLASS_TYPE_FIST_OF_ZUOKEN + || nClass==CLASS_TYPE_WARMIND + ); +} + +int GetWildSurge(object oManifester) +{ + int nWildSurge = GetLocalInt(oManifester, PRC_IS_PSILIKE) ? + 0 : // Wild Surge does not apply to psi-like abilities + GetLocalInt(oManifester, PRC_WILD_SURGE); + + if(DEBUG) DoDebug("GetWildSurge():\n" + + "oManifester = " + DebugObject2Str(oManifester) + "\n" + + "nWildSurge = " + IntToString(nWildSurge) + "\n" + ); + + return nWildSurge; +} + +int GetMaxPowerLevel(object oManifester) +{ + int nClass = GetPrimaryPsionicClass(oManifester); + string sFile = GetAMSKnownFileName(nClass); + int nLevel = GetHighestManifesterLevel(oManifester); + int nMax = StringToInt(Get2DACache(sFile, "MaxPowerLevel", nLevel)); + if (DEBUG) DoDebug("GetMaxPowerLevel is "+IntToString(nMax)); + return nMax; +} \ No newline at end of file diff --git a/src/include/psi_inc_enrgypow.nss b/src/include/psi_inc_enrgypow.nss new file mode 100644 index 0000000..ad1663c --- /dev/null +++ b/src/include/psi_inc_enrgypow.nss @@ -0,0 +1,150 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Energy powers +//:: psi_inc_enrgypow +//:://///////////////////////////////////////////// +/** @file + Defines function and structure for determining + energy-type dependent features of the Energy X + line of powers. + + + @author Ornedan + @date Created - 2005.12.12 + */ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure for passing the Energy type modification + * data back to the power script. + */ +struct energy_adjustments{ + int nBonusPerDie; + int nSaveType; + int nDamageType; + int nDCMod; + int nPenMod; + int nVFX1; + int nVFX2; +}; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the energy-dependent adjustments to damage per die, DC and + * manifester level in regards to Power Resistance. Also does VFX, saving + * throw type and damage type. + * + * @param nSpellID SpellID of the power being manifested + * @param nSpellID_Cold SpellID of the cold version of the power + * @param nSpellID_Elec SpellID of the electricity version of the power + * @param nSpellID_Fire SpellID of the fire version of the power + * @param nSpellID_Sonic SpellID of the sonic version of the power + * @param nVFX2_Cold Power specific VFX for cold + * @param nVFX2_Elec Power specific VFX for electricity + * @param nVFX2_Fire Power specific VFX for fire + * @param nVFX2_Sonic Power specific VFX for sonic + */ +struct energy_adjustments EvaluateEnergy(int nSpellID, int nSpellID_Cold, int nSpellID_Elec, int nSpellID_Fire, int nSpellID_Sonic, + int nVFX2_Cold = 0, int nVFX2_Elec = 0, int nVFX2_Fire = 0, int nVFX2_Sonic = 0); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_utility" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct energy_adjustments EvaluateEnergy(int nSpellID, int nSpellID_Cold, int nSpellID_Elec, int nSpellID_Fire, int nSpellID_Sonic, + int nVFX2_Cold = 0, int nVFX2_Elec = 0, int nVFX2_Fire = 0, int nVFX2_Sonic = 0) +{ + struct energy_adjustments eaRet; + + if(nSpellID == nSpellID_Cold) + { + eaRet.nBonusPerDie = 1; + eaRet.nSaveType = SAVING_THROW_TYPE_COLD; + eaRet.nDamageType = DAMAGE_TYPE_COLD; + eaRet.nDCMod = 0; + eaRet.nPenMod = 0; + eaRet.nVFX1 = VFX_IMP_FROST_S; + eaRet.nVFX2 = nVFX2_Cold; + } + else if(nSpellID == nSpellID_Elec) + { + eaRet.nBonusPerDie = 0; + eaRet.nSaveType = SAVING_THROW_TYPE_ELECTRICITY; + eaRet.nDamageType = DAMAGE_TYPE_ELECTRICAL; + eaRet.nDCMod = 2; + eaRet.nPenMod = 2; + eaRet.nVFX1 = VFX_IMP_LIGHTNING_S; + eaRet.nVFX2 = nVFX2_Elec; + } + else if(nSpellID == nSpellID_Fire) + { + eaRet.nBonusPerDie = 1; + eaRet.nSaveType = SAVING_THROW_TYPE_FIRE; + eaRet.nDamageType = DAMAGE_TYPE_FIRE; + eaRet.nDCMod = 0; + eaRet.nPenMod = 0; + eaRet.nVFX1 = VFX_IMP_FLAME_S; + eaRet.nVFX2 = nVFX2_Fire; + } + else if(nSpellID == nSpellID_Sonic) + { + eaRet.nBonusPerDie = -1; + eaRet.nSaveType = SAVING_THROW_TYPE_SONIC; + eaRet.nDamageType = DAMAGE_TYPE_SONIC; + eaRet.nDCMod = 0; + eaRet.nPenMod = 0; + eaRet.nVFX1 = VFX_IMP_SONIC; + eaRet.nVFX2 = nVFX2_Sonic; + } + else + { + string sErr = "EvaluateEnergy(): ERROR: SpellID does not match any of the given IDs\n" + + "Given ID: " + IntToString(nSpellID) + "\n" + + "Cold ID: " + IntToString(nSpellID_Cold) + "\n" + + "Electricity ID: " + IntToString(nSpellID_Elec) + "\n" + + "Fire ID: " + IntToString(nSpellID_Fire) + "\n" + + "Sonic ID: " + IntToString(nSpellID_Sonic) + "\n" + ; + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + + //Energy Draconic Aura boosts + if (eaRet.nDamageType == DAMAGE_TYPE_FIRE && (GetLocalInt(OBJECT_SELF, "FireEnergyAura") > 0)) + { + eaRet.nDCMod += GetLocalInt(OBJECT_SELF, "FireEnergyAura"); + } + else if (eaRet.nDamageType == DAMAGE_TYPE_COLD && (GetLocalInt(OBJECT_SELF, "ColdEnergyAura") > 0)) + { + eaRet.nDCMod += GetLocalInt(OBJECT_SELF, "ColdEnergyAura"); + } + else if (eaRet.nDamageType == DAMAGE_TYPE_ELECTRICAL && (GetLocalInt(OBJECT_SELF, "ElecEnergyAura") > 0)) + { + eaRet.nDCMod += GetLocalInt(OBJECT_SELF, "ElecEnergyAura"); + } + else if (eaRet.nDamageType == DAMAGE_TYPE_ACID && (GetLocalInt(OBJECT_SELF, "AcidEnergyAura") > 0)) + { + eaRet.nDCMod += GetLocalInt(OBJECT_SELF, "AcidEnergyAura"); + } + + return eaRet; +} + +// Test main +//void main(){} diff --git a/src/include/psi_inc_metapsi.nss b/src/include/psi_inc_metapsi.nss new file mode 100644 index 0000000..c005f0c --- /dev/null +++ b/src/include/psi_inc_metapsi.nss @@ -0,0 +1,550 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Metapsionics +//:: psi_inc_metapsi +//:://///////////////////////////////////////////// +/** @file + Defines functions for handling metapsionics. + + @author Ornedan + @date Created - 2005.11.18 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Constants are provided via psi_inc_core + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the metapsionics used in this manifestation of a power + * and the cost added by their use. + * + * @param manif The manifestation data related to this particular manifesation + * @param nMetaPsiFlags An integer containing a set of bitflags that determine + * which metapsionic powers may be used with the power being manifested + * + * @return The manifestation data, modified to account for the metapsionics + */ +struct manifestation EvaluateMetapsionics(struct manifestation manif, int nMetaPsiFlags); + +/** + * Calls UsePsionicFocus() on the manifester to pay for psionics focus + * expenditure caused by metapsionics use in the power being currently + * manifested. + * Also informs the manifester which metapsionics were actually used + * if the manifestation was successfull. + * + * @param manif The manifestation data related to this particular manifesation + * @return The manifestation data, modified to turn off those metapsionics + * the manifester could not pay focus for + */ +struct manifestation PayMetapsionicsFocuses(struct manifestation manif); + +/** + * Calculates a power's damage based on the given dice and metapsionics. + * + * @param nDieSize Size of the dice to use + * @param nNumberOfDice Amount of dice to roll + * @param manif The manifestation data related to this particular manifesation + * @param nBonus A bonus amount of damage to add into the total once + * @param nBonusPerDie A bonus amount of damage to add into the total for each die rolled + * @param bDoesHPDamage Whether the power deals hit point damage, or some other form of point damage + * @param bIsRayOrRangedTouch Whether the power's use involves a ranged touch attack roll or not + * @return The amount of damage the power should deal + */ +int MetaPsionicsDamage(struct manifestation manif, int nDieSize, int nNumberOfDice, int nBonus = 0, + int nBonusPerDie = 0, int bDoesHPDamage = FALSE, int bIsRayOrRangedTouch = FALSE); + +/** + * Accounts for the effects of Widen Power on the area size variables + * of a power, if it is active. + * + * @param fBase The base value of the power's area size variable + * @param manif The manifestation data related to this particular manifesation + * @return The base modified by whether Widen Power was active. + * If it was, the returned value is twice fBase, otherwise + * it's fBase + */ +float EvaluateWidenPower(struct manifestation manif, float fBase); + +/** + * Builds the list of a power's secondary targets for Chain Power. + * The list will be empty if Chain Power is not active. + * The list is stored in a local array on the manifester named + * PRC_CHAIN_POWER_ARRAY. It will be automatically deleted at the end of + * current script unless otherwise specified. + * + * NOTE: This only builds the list of targets, all effects have to be + * applied by the powerscript. + * + * @param manif The manifestation data related to this particular manifesation + * @param oPrimaryTarget The main target of the power + * @param bAutoDelete Whether the targets array should be automatically cleared via a + * DelayCommand(0.0f) call. + */ +void EvaluateChainPower(struct manifestation manif, object oPrimaryTarget, int bAutoDelete = TRUE); + +/** + * Determines the target the second ray fired by a split psionic ray strikes. + * The target is the one with the highest HD among eligible targets, ie, ones + * withing 30' of the main target. + * + * @param manif The manifestation data related to this particular manifesation + * @param oPrimaryTarget The target of the main ray + * + * @return The secondary target the power should affect. OBJECT_INVALID, if + * none were found, or if Split Psionic Ray was not active. + */ +object GetSplitPsionicRayTarget(struct manifestation manif, object oPrimaryTarget); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "psi_inc_core" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * @param nCost The base cost of the metapsionic power to calculate the actual cost for + * @param nIMPsiRed The amount the PP cost might be reduced by due to Improved Metapsionics feats + * @param bUseSum Whether to account for Improved Metapsionics here or not + * @return Either nCost or the greater of nCost - nIMPsiRed and 1 + */ +int _GetMetaPsiPPCost(int nCost, int nIMPsiRed, int bUseSum) +{ + return nCost <= 2 ? // Metapsionic powers costing 2 or less are not affected by Improved Metapsionics + nCost : + bUseSum ? nCost : + // When calculating Improved Metapsionics separately, it cannot make the cost of a single metapsionic use go below 1 + PRCMax(nCost - nIMPsiRed, 1); +} + +/** Internal function. + * A void wrapper for array_delete. + * + * @param oManifester A creature that just manifested a power that determined + * it's targets using EvaluateChainPower + */ +void _DeleteChainArray(object oManifester) +{ + array_delete(oManifester, PRC_CHAIN_POWER_ARRAY); +} + +/** Internal function. + * Calculates whether adding a metapsi with the given cost would cause ML cap to be exceeded. + */ +int _GetWillExceedCap(struct manifestation manif, int nTotal, int nCost, int nIMPsiRed, int bUseSum) +{ + return (manif.nManifesterLevel + - (manif.nPPCost + + (bUseSum ? (_GetMetaPsiPPCost(nTotal + nCost, nIMPsiRed, FALSE)) : (nTotal + _GetMetaPsiPPCost(nCost, nIMPsiRed, FALSE))) + ) + ) + >= 0; +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct manifestation EvaluateMetapsionics(struct manifestation manif, int nMetaPsiFlags) +{ + // Total PP cost of metapsionics used + int nMetaPsiPP = 0; + // A debug variable to make a power ignore normal use constraints + int bIgnoreConstr = (DEBUG) ? GetLocalInt(manif.oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // A switch value that governs how Improved Metapsionics epic feat works + int bUseSum = GetPRCSwitch(PRC_PSI_IMP_METAPSIONICS_USE_SUM); + // A personal switch values that determines whether we should make an effort to attempt not to exceed PP cap + int bAvoidCap = GetPersistantLocalInt(manif.oManifester, PRC_PLAYER_SWITCH_AUTOMETAPSI) && !bIgnoreConstr; + // Epic feat Improved Metapsionics - 2 PP per. + int nImpMetapsiReduction, i = FEAT_IMPROVED_METAPSIONICS_1; + while(i <= FEAT_IMPROVED_METAPSIONICS_10 && GetHasFeat(i, manif.oManifester)) + { + nImpMetapsiReduction += 2; + i++; + } + + /* Calculate the added cost from metapsionics and set the use markers for the powers used */ + + // Quicken Power - special handling + if(GetLocalInt(manif.oManifester, PRC_POWER_IS_QUICKENED)) + { + // If Quicken could not have been used, the manifestation fails + if(!(manif.nPsiFocUsesRemain > 0 || bIgnoreConstr)) + manif.bCanManifest = FALSE; + + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_QUICKEN_COST, nImpMetapsiReduction, bUseSum); + manif.bQuicken = TRUE; + manif.nPsiFocUsesRemain--; + + // Delete the marker var + DeleteLocalInt(manif.oManifester, PRC_POWER_IS_QUICKENED); + } + + if((nMetaPsiFlags & METAPSIONIC_CHAIN) && // The power allows this metapsionic to apply + GetLocalInt(manif.oManifester, METAPSIONIC_CHAIN_VAR) && // The manifester is using the metapsionic + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && // The manifester can pay the psionic focus expenditure + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_CHAIN_COST, nImpMetapsiReduction, bUseSum)) // ML cap won't be exceeded. Or we don't care about exceeding it + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_CHAIN_COST, nImpMetapsiReduction, bUseSum); + manif.bChain = TRUE; + manif.nPsiFocUsesRemain--; + } + if((nMetaPsiFlags & METAPSIONIC_EMPOWER) && + GetLocalInt(manif.oManifester, METAPSIONIC_EMPOWER_VAR) && + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_EMPOWER_COST, nImpMetapsiReduction, bUseSum)) + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_EMPOWER_COST, nImpMetapsiReduction, bUseSum); + manif.bEmpower = TRUE; + manif.nPsiFocUsesRemain--; + } + if((nMetaPsiFlags & METAPSIONIC_EXTEND) && + GetLocalInt(manif.oManifester, METAPSIONIC_EXTEND_VAR) && + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_EXTEND_COST, nImpMetapsiReduction, bUseSum)) + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_EXTEND_COST, nImpMetapsiReduction, bUseSum); + manif.bExtend = TRUE; + manif.nPsiFocUsesRemain--; + } + if((nMetaPsiFlags & METAPSIONIC_MAXIMIZE) && + GetLocalInt(manif.oManifester, METAPSIONIC_MAXIMIZE_VAR) && + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_MAXIMIZE_COST, nImpMetapsiReduction, bUseSum)) + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_MAXIMIZE_COST, nImpMetapsiReduction, bUseSum); + manif.bMaximize = TRUE; + manif.nPsiFocUsesRemain--; + } + if((nMetaPsiFlags & METAPSIONIC_SPLIT) && + GetLocalInt(manif.oManifester, METAPSIONIC_SPLIT_VAR) && + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_SPLIT_COST, nImpMetapsiReduction, bUseSum)) + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_SPLIT_COST, nImpMetapsiReduction, bUseSum); + manif.bSplit = TRUE; + manif.nPsiFocUsesRemain--; + } + if((nMetaPsiFlags & METAPSIONIC_TWIN) && + GetLocalInt(manif.oManifester, METAPSIONIC_TWIN_VAR) && + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_TWIN_COST, nImpMetapsiReduction, bUseSum)) + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_TWIN_COST, nImpMetapsiReduction, bUseSum); + manif.bTwin = TRUE; + manif.nPsiFocUsesRemain--; + } + if((nMetaPsiFlags & METAPSIONIC_WIDEN) && + GetLocalInt(manif.oManifester, METAPSIONIC_WIDEN_VAR) && + (manif.nPsiFocUsesRemain > 0 || bIgnoreConstr) && + (!bAvoidCap || _GetWillExceedCap(manif, nMetaPsiPP, METAPSIONIC_WIDEN_COST, nImpMetapsiReduction, bUseSum)) + ) + { + nMetaPsiPP += _GetMetaPsiPPCost(METAPSIONIC_WIDEN_COST, nImpMetapsiReduction, bUseSum); + manif.bWiden = TRUE; + manif.nPsiFocUsesRemain--; + } + + // Add in the cost of the metapsionics uses + manif.nPPCost += _GetMetaPsiPPCost(nMetaPsiPP, nImpMetapsiReduction, !bUseSum); // A somewhat hacky use of the function, but eh, it works + + return manif; +} + + +struct manifestation PayMetapsionicsFocuses(struct manifestation manif) +{ + // A debug variable to make a power ignore normal use constraints + int bIgnoreConstraints = (DEBUG) ? GetLocalInt(manif.oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + + // The string to send to the user + string sInform = ""; + + /* Check each of the metapsionics and pay focus for each active one. If for some reason the + * manifester cannot pay focus, deactivate the metapsionic. No PP refunds, though, since + * the system attempts to keep track of how many focuses the user has available + * and shouldn't allow them to exceed that count. It happening is therefore a bug. + */ + + // Quicken Power, special handling + if(manif.bQuicken) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Quicken Power!"); + // If Quicken could not have been used, the manifestation fails + manif.bCanManifest = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826650); // "Quickened" + } + if(manif.bChain) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Chain Power!"); + manif.bChain = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826631); // "Chained" + } + if(manif.bEmpower) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Empower Power!"); + manif.bEmpower = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826632); // "Empowered" + } + if(manif.bExtend) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Extend Power!"); + manif.bExtend = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826633); // "Extended" + } + if(manif.bMaximize) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Maximize Power!"); + manif.bMaximize = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826634); // "Maximized" + } + if(manif.bSplit) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Split Psionic Ray!"); + manif.bSplit = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826635); // "Split" + } + if(manif.bTwin) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Twin Power!"); + manif.bTwin = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826636); // "Twinned" + } + if(manif.bWiden) + { + if(!UsePsionicFocus(manif.oManifester) && !bIgnoreConstraints) + { + if(DEBUG) DoDebug(DebugObject2Str(manif.oManifester) + " unable to pay psionic focus for Widen Power!"); + manif.bWiden = FALSE; + } + else + sInform += (sInform == "" ? "": ", ") + GetStringByStrRef(16826637); // "Widened" + } + + // Finalise and display the information string if the manifestation was successfull + if(manif.bCanManifest && sInform != "") + { + // Determine the index of the last comma + /// @todo This is badly structured, rewrite + string sTemp = sInform; + int nComma, + nTemp = FindSubString(sTemp, ", "); + if(nTemp != -1) + { + sTemp = GetSubString(sTemp, + nTemp + 2, // Crop off the comma and the following space + GetStringLength(sTemp) // I'm lazy + ); + nComma += nTemp +2; + while((nTemp = FindSubString(sTemp, ", ")) != -1) + { + nComma += nTemp + 2; + sTemp = GetSubString(sTemp, nTemp + 2, GetStringLength(sTemp)); + } + + // Replace the last comma with an "and" + sInform = GetStringLeft(sInform, nComma - 2) + + " " + GetStringByStrRef(16826638) + " " // " and " + + GetSubString(sInform, nComma, GetStringLength(sInform) - nComma); + } + + + // Finish the information string + sInform += " " + GetStringByStrRef(16826639); // "Power" + + FloatingTextStringOnCreature(sInform, manif.oManifester, FALSE); + } + + return manif; +} + + +int MetaPsionicsDamage(struct manifestation manif, int nDieSize, int nNumberOfDice, int nBonus = 0, + int nBonusPerDie = 0, int bDoesHPDamage = FALSE, int bIsRayOrRangedTouch = FALSE) +{ + int nBaseDamage = 0, + nBonusDamage = nBonus + (nNumberOfDice * nBonusPerDie); + + // Calculate the base damage + int i; + for (i = 0; i < nNumberOfDice; i++) + nBaseDamage += Random(nDieSize) + 1; + + + // Apply general modifying effects + if(bDoesHPDamage) + { + if(bIsRayOrRangedTouch && + GetHasFeat(FEAT_POWER_SPECIALIZATION, manif.oManifester)) + { + if(GetLocalInt(manif.oManifester, "PowerSpecializationActive") && UsePsionicFocus(manif.oManifester)) + nBonusDamage += GetAbilityModifier(GetAbilityOfClass(GetManifestingClass(manif.oManifester))); + else + nBonusDamage += 2; + } + if(GetHasSpellEffect(MELD_PSYCHIC_FOCUS, manif.oManifester)) nBonusDamage += GetEssentiaInvested(manif.oManifester, MELD_PSYCHIC_FOCUS) + 1; + } + + // Apply metapsionics + // Both empower & maximize + if(manif.bEmpower && manif.bMaximize) + nBaseDamage = nBaseDamage / 2 + nDieSize * nNumberOfDice; + // Just empower + else if(manif.bEmpower) + nBaseDamage += nBaseDamage / 2; + // Just maximize + else if(manif.bMaximize) + nBaseDamage = nDieSize * nNumberOfDice; + + + return nBaseDamage + nBonusDamage; +} + + +float EvaluateWidenPower(struct manifestation manif, float fBase) +{ + return manif.bWiden ? // Is Widen Power active + fBase * 2 : // Yes + fBase; // No +} + + +void EvaluateChainPower(struct manifestation manif, object oPrimaryTarget, int bAutoDelete = TRUE) +{ + // Delete the array if, for some reason, one exists already + if(array_exists(manif.oManifester, PRC_CHAIN_POWER_ARRAY)) + array_delete(manif.oManifester, PRC_CHAIN_POWER_ARRAY); + // Create the array + if(!array_create(manif.oManifester, PRC_CHAIN_POWER_ARRAY)) + if(DEBUG) DoDebug("EvaluateChainPower(): ERROR: Cannot create target array!\n" + + "manif = " + DebugManifestation2Str(manif) + "\n" + + "oPrimaryTarget = " + DebugObject2Str(oPrimaryTarget) + "\n" + + "bAutoDelete = " + DebugBool2String(bAutoDelete) + "\n" + ); + + // Add the primary target to the array + //array_set_object(manif.oManifester, PRC_CHAIN_POWER_ARRAY, 0, oPrimaryTarget); Bad idea, on a second though - Ornedan + + // Determine if Chain Power is active at all + if(manif.bChain) + { + // It is, determine amount of secondary targets and range to look for the over + int nMaxTargets = PRCMin(manif.nManifesterLevel, 20); // Chain Power maxes out at 20 secondary targets + float fRange = FeetToMeters(30.0f); + location lTarget = GetLocation(oPrimaryTarget); + object oSecondaryTarget; + + // Build the target list as a linked list + oSecondaryTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRange, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oSecondaryTarget)) + { + if(oSecondaryTarget != manif.oManifester && // Not the manifester + oSecondaryTarget != oPrimaryTarget && // Not the main target + spellsIsTarget(oSecondaryTarget, // Chain Power allows one to avoid hitting friendlies + SPELL_TARGET_SELECTIVEHOSTILE, // and we assume the manifester does so + manif.oManifester)) + { + AddToTargetList(oSecondaryTarget, manif.oManifester, INSERTION_BIAS_HD, TRUE); + }// end if - target is valid for this + + oSecondaryTarget = GetNextObjectInShape(SHAPE_SPHERE, fRange, lTarget, TRUE, OBJECT_TYPE_CREATURE); + }// end while - loop through all potential targets + + // Extract the linked list into the array + while(GetIsObjectValid(oSecondaryTarget = GetTargetListHead(manif.oManifester))) + array_set_object(manif.oManifester, PRC_CHAIN_POWER_ARRAY, + array_get_size(manif.oManifester, PRC_CHAIN_POWER_ARRAY), + oSecondaryTarget + ); + }// end if - is Chain Power active in this manifestation + + // Schedule array deletion if so specified + if(bAutoDelete) + DelayCommand(0.0f, _DeleteChainArray(manif.oManifester)); +} + +object GetSplitPsionicRayTarget(struct manifestation manif, object oPrimaryTarget) +{ + object oReturn = OBJECT_INVALID; + + // Determine if Split Psionic Ray is active at all + if(manif.bSplit) + { + // It is, determine amount of secondary targets and range to look for the over + float fRange = FeetToMeters(30.0f); + location lTarget = GetLocation(oPrimaryTarget); + object oPotentialTarget; + + // Build the target list as a linked list + oPotentialTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRange, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oPotentialTarget)) + { + if(oPotentialTarget != manif.oManifester && // Not the manifester + oPotentialTarget != oPrimaryTarget && // Not the main target + spellsIsTarget(oPotentialTarget, // Split Psionic Ray allows one to avoid hitting friendlies + SPELL_TARGET_SELECTIVEHOSTILE, // and we assume the manifester does so + manif.oManifester)) + { + AddToTargetList(oPotentialTarget, manif.oManifester, INSERTION_BIAS_HD, TRUE); + }// end if - target is valid for this + + oPotentialTarget = GetNextObjectInShape(SHAPE_SPHERE, fRange, lTarget, TRUE, OBJECT_TYPE_CREATURE); + }// end while - loop through all potential targets + + // The chosen target is the first in the list + oReturn = GetTargetListHead(manif.oManifester); + }// end if - is Split Psionic Ray active in this manifestation + + return oReturn; +} + +// Test main +//void main(){} diff --git a/src/include/psi_inc_onhit.nss b/src/include/psi_inc_onhit.nss new file mode 100644 index 0000000..56f68b2 --- /dev/null +++ b/src/include/psi_inc_onhit.nss @@ -0,0 +1,79 @@ +/* + Psionic OnHit. + This scripts holds all functions used for psionics on hit powers and abilities. + + Stratovarius +*/ + +// Include Files: +#include "psi_inc_psifunc" +// +#include "psi_inc_pwresist" +#include "prc_inc_combat" + + +// Swings at the target closest to the one hit +void SweepingStrike(object oCaster, object oTarget); + +// Shadow Mind 10th level ability. Manifests Cloud Mind +void MindStab(object oPC, object oTarget); + +// --------------- +// BEGIN FUNCTIONS +// --------------- + +void SweepingStrike(object oCaster, object oTarget) +{ + int nValidTarget = FALSE; + int nRandom = Random(9999); + location lTarget = GetLocation(oTarget); + string sRandom = IntToString(nRandom); + string sTimeStamp = IntToString(GetTimeHour()) + IntToString(GetTimeMinute()) + IntToString(GetTimeSecond()) + IntToString(GetTimeMillisecond()); + string sKillSwitch = "SweepingStrikeDelay" + ObjectToString(oCaster) + sRandom + sTimeStamp; + // Use the function to get the closest creature as a target + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget) && !nValidTarget && !GetLocalInt(oCaster, sKillSwitch) && !GetLocalInt(oTarget, "SweepingStrikeTarget")) + { + // Don't hit yourself + // Make sure the target is both next to the one struck and within melee range of the caster + // Don't hit the one already struck + if(oAreaTarget != oCaster && + GetIsInMeleeRange(oAreaTarget, oCaster) && + GetIsInMeleeRange(oAreaTarget, oTarget) && + GetIsReactionTypeHostile(oAreaTarget, oCaster) && + !GetLocalInt(oTarget, "SweepingStrikeTarget") && //Redundant with while conditionals, but what the heck + oAreaTarget != oTarget) + { + // Perform the Attack + effect eVis = EffectVisualEffect(VFX_IMP_STUN); + object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster); + SetLocalInt(oAreaTarget, "SweepingStrikeTarget", TRUE); + DelayCommand(0.2, PerformAttack(oAreaTarget, oCaster, eVis, 0.0, 0, 0, GetWeaponDamageType(oWeap), "Sweeping Strike Hit", "Sweeping Strike Miss")); + if(DEBUG) DoDebug("psi_onhit: Sweeping Strike Loop Running"); + DelayCommand(1.0, DeleteLocalInt(oAreaTarget, "SweepingStrikeTarget")); + // End the loop, and prevent the death attack + SetLocalInt(oCaster, sKillSwitch, TRUE); + nValidTarget = TRUE; //This and the previous line now do the same thing I think + } + + //Select the next target within the spell shape. + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE); + }// end while - Target loop + DelayCommand(1.0, DeleteLocalInt(oCaster, sKillSwitch)); +} + +void MindStab(object oPC, object oTarget) +{ + SetLocalInt(oPC, "ShadowCloudMind", TRUE); + SetLocalObject(oPC, "PsionicTarget", oTarget); + FloatingTextStringOnCreature("Mind Stab triggered", oPC, FALSE); + SetLocalInt(oPC, "MindStabDur", TRUE); + int nClass = GetPrimaryPsionicClass(oPC); + UsePower(POWER_CLOUD_MIND, nClass); + // Trying this for now + //ActionCastSpell(POWER_CLOUD_MIND); + DelayCommand(1.0, DeleteLocalInt(oPC, "ShadowCloudMind")); + DelayCommand(1.0, DeleteLocalObject(oPC, "PsionicTarget")); + DeleteLocalInt(oPC, "MindStabDur"); + //DelayCommand(0.33, AssignCommand(oPC, ActionAttack(oTarget))); +} \ No newline at end of file diff --git a/src/include/psi_inc_powknown.nss b/src/include/psi_inc_powknown.nss new file mode 100644 index 0000000..51e304f --- /dev/null +++ b/src/include/psi_inc_powknown.nss @@ -0,0 +1,718 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Powers known +//:: psi_inc_powknown +//:://///////////////////////////////////////////// +/** @file + Defines functions for adding & removing + powers known. + + Data stored: + + - For each power list + -- Total number of powers known + -- A modifier value to maximum powers known on this list to account for stuff like + Apopsi, Expanded Knowledge and Psychic Chirurgery that add or remove powers + -- An array related to powers the knowledge of which is not dependent on character level + --- Each array entry specifies the spells.2da row of the known power's class-specific entry + -- For each character level on which powers have been gained from this list + --- An array of powers gained on this level + ---- Each array entry specifies the spells.2da row of the known power's class-specific entry + + @author Ornedan + @date Created - 2006.01.06 + @date Modified - 2006.04.21 + Changed the stored value from cls_psipw_*.2da row to spells.2da row of the + class-specific entry because the adding policy to cls_psipw_*.2da means that the rows + after the added entry are moved. Which would break compatibility with old characters. + On the other hand, if the spells.2da entries are moved, the result would likely be a + backwards-compatibility loss anyway. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Constants are provided via psi_inc_core + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gives the creature the control feats for the given power and marks the power + * in a powers known array. + * If the power's data is already stored in one of the powers known arrays for + * the list or adding the power's data to the array fails, the function aborts. + * + * @param oCreature The creature to gain the power + * @param nList The list the power comes from. One of POWER_LIST_* + * @param n2daRow The 2da row in the lists's 2da file that specifies the power. + * @param bLevelDependent If this is TRUE, the power is tied to a certain level and can + * be lost via level loss. If FALSE, the power is not dependent + * of a level and cannot be lost via level loss. + * @param nLevelToTieTo If bLevelDependent is TRUE, this specifies the level the power + * is gained on. Otherwise, it's ignored. + * The default value (-1) means that the current level of oCreature + * will be used. + * + * @return TRUE if the power was successfully stored and control feats added. + * FALSE otherwise. + */ +int AddPowerKnown(object oCreature, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1, int nExpandedKnowledge = 0); + +/** + * + */ +//int RemoveSpecificPowerKnown(object oCreature, int nList, int n2daRow); + +/** + * Removes all powers gained from each list on the given level. + * + * @param oCreature The creature whose powers to remove + * @param nLevel The level to clear + */ +void RemovePowersKnownOnLevel(object oCreature, int nLevel); + +/** + * Gets the value of the powers known modifier, which is a value that is added + * to the 2da-specified maximum powers known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to get + * @param nList The list the maximum powers known from which the modifier + * modifies. One of POWER_LIST_* + */ +int GetKnownPowersModifier(object oCreature, int nList); + +/** + * Sets the value of the powers known modifier, which is a value that is added + * to the 2da-specified maximum powers known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to set + * @param nList The list the maximum powers known from which the modifier + * modifies. One of POWER_LIST_* + */ +void SetKnownPowersModifier(object oCreature, int nList, int nNewValue); + +/** + * Gets the number of powers a character character possesses from a + * specific list. + * + * @param oCreature The creature whose powers to check + * @param nList The list to check. One of POWER_LIST_* + * @return The number of powers known oCreature has from nList + */ +int GetPowerCount(object oCreature, int nList); + +/** + * Gets the maximum number of powers a character may posses from a given list + * at this time. Calculated based on class levels, feats and a misceallenous + * modifier. + * + * @param oCreature Character to determine maximum powers for + * @param nList POWER_LIST_* of the list to determine maximum powers for + * @return Maximum number of powers that oCreature may know from the given list. + */ +int GetMaxPowerCount(object oCreature, int nList); + +/** + * Determines whether a character has a given power, gained via some power list. + * Psi-like abilities do not count. + * + * @param nPower POWER_* of the power to test + * @param oCreature Character to test for the possession of the power + * @return TRUE if the character has the power, FALSE otherwise + */ +int GetHasPower(int nPower, object oCreature = OBJECT_SELF); + +/** + * Converts a CLASS_TYPE_* constant to a corresponding POWER_LIST_* constant. + * Special: CLASS_TYPE_INVALID corresponds to POWER_LIST_EXP_KNOWLEDGE. + * + * @param nClassType CLASS_TYPE_* to determine POWER_LIST_* for + * @return POWER_LIST_* constant for nClassType. 0 if there is no + * power list for the given CLASS_TYPE_* + */ +//int ClassTypeToPowerList(int nClassType); + +/** + * Converts a POWER_LIST_* constant to a corresponding CLASS_TYPE_* constant. + * Special: POWER_LIST_EXP_KNOWLEDGE corresponds to CLASS_TYPE_INVALID. + * + * @param nPowerList POWER_LIST_* to determine CLASS_TYPE_* for + * @return CLASS_TYPE_* constant for nPowerList. 0 if there is no + * class type for the given POWER_LIST_* + */ +//int PowerListToClassType(int nPowerList); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "psi_inc_core" +//#include "prc_inc_spells" // Access to lookup and 2da + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _RecurseRemoveArray(object oCreature, string sArrayName, string sPowerFile, int nArraySize, int nCurIndex) +{ + if(DEBUG) DoDebug("_RecurseRemoveArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "sArrayName = '" + sArrayName + "'\n" + + "sPowerFile = '" + sPowerFile + "'\n" + + "nArraySize = " + IntToString(nArraySize) + "\n" + + "nCurIndex = " + IntToString(nCurIndex) + "\n" + ); + + // Determine whether we've already parsed the whole array or not + if(nCurIndex >= nArraySize) + { + if(DEBUG) DoDebug("_RecurseRemoveArray(): Running itemproperty removal loop."); + // Loop over itemproperties on the skin and remove each match + object oSkin = GetPCSkin(oCreature); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + GetLocalInt(oCreature, "PRC_PowerFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))) + ) + { + if(DEBUG) DoDebug("_RecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + } + + ipTest = GetNextItemProperty(oSkin); + } + } + // Still parsing the array + else + { + // Set the marker + string sName = "PRC_PowerFeatRemovalMarker_" + Get2DACache(sPowerFile, "IPFeatID", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArrayName, nCurIndex)) + ); + if(DEBUG) DoDebug("_RecurseRemoveArray(): Recursing through array, marker set:\n" + sName); + + SetLocalInt(oCreature, sName, TRUE); + // Recurse to next array index + _RecurseRemoveArray(oCreature, sArrayName, sPowerFile, nArraySize, nCurIndex + 1); + // After returning, delete the local + DeleteLocalInt(oCreature, sName); + } +} + +void _RemovePowerArray(object oCreature, int nList, int nLevel) +{ + if(DEBUG) DoDebug("_RemovePowerArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + ); + + string sBase = _POWER_LIST_NAME_BASE + IntToString(nList); + string sArray = sBase + _POWER_LIST_LEVEL_ARRAY + IntToString(nLevel); + int nSize = persistant_array_get_size(oCreature, sArray); + + // Reduce the total by the array size + SetPersistantLocalInt(oCreature, sBase + _POWER_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _POWER_LIST_TOTAL_KNOWN) - nSize + ); + + // Remove each power in the array + _RecurseRemoveArray(oCreature, sArray, GetAMSDefinitionFileName(/*PowerListToClassType(*/nList/*)*/), nSize, 0); + + // Remove the array itself + persistant_array_delete(oCreature, sArray); +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int AddPowerKnown(object oCreature, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1, int nExpandedKnowledge = 0) +{ + string sBase = nExpandedKnowledge < 0 ? _POWER_LIST_NAME_BASE + IntToString(nExpandedKnowledge): + _POWER_LIST_NAME_BASE + IntToString(nList); + string sArray = sBase; + string sPowerFile = GetAMSDefinitionFileName(/*PowerListToClassType(*/nList/*)*/); + string sTestArray; + int i, j, nSize, bReturn; + + // Get the spells.2da row corresponding to the cls_psipw_*.2da row + int nSpells2daRow = StringToInt(Get2DACache(sPowerFile, "SpellID", n2daRow)); + + // Determine the array name. + if(bLevelDependent) + { + // If no level is specified, default to the creature's current level + if(nLevelToTieTo == -1) + nLevelToTieTo = GetHitDice(oCreature); + + sArray += _POWER_LIST_LEVEL_ARRAY + IntToString(nLevelToTieTo); + } + else + { + sArray += _POWER_LIST_GENERAL_ARRAY; + } + + // Make sure the power isn't already in an array. If it is, abort and return FALSE + // Loop over each level array and check that it isn't there. + for(i = 1; i <= GetHitDice(oCreature); i++) + { + sTestArray = sBase + _POWER_LIST_LEVEL_ARRAY + IntToString(i); + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + } + // Check the non-level-dependent array + sTestArray = sBase + _POWER_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + + // All checks are made, now start adding the new power + // Create the array if it doesn't exist yet + if(!persistant_array_exists(oCreature, sArray)) + persistant_array_create(oCreature, sArray); + + // Store the power in the array + if(persistant_array_set_int(oCreature, sArray, persistant_array_get_size(oCreature, sArray), nSpells2daRow) != SDL_SUCCESS) + { + if(DEBUG) DoDebug("psi_inc_powknown: AddPowerKnown(): ERROR: Unable to add power to known array\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + + "n2daRow = " + IntToString(n2daRow) + "\n" + + "bLevelDependent = " + DebugBool2String(bLevelDependent) + "\n" + + "nLevelToTieTo = " + IntToString(nLevelToTieTo) + "\n" + + "nSpells2daRow = " + IntToString(nSpells2daRow) + "\n" + + "nExpandedKnowledge = " + IntToString(nExpandedKnowledge) + "\n" + ); + return FALSE; + } + + // Increment powers known total + SetPersistantLocalInt(oCreature, sBase + _POWER_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _POWER_LIST_TOTAL_KNOWN) + 1 + ); + + // Give the power's control feats + object oSkin = GetPCSkin(oCreature); + string sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID", n2daRow); + itemproperty ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + // Second power feat, if any + sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID2", n2daRow); + if(sPowerFeatIP != "") + { + ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + + return TRUE; +} + +/** @todo If ever required +int RemoveSpecificPowerKnown(object oCreature, int nList, int n2daRow) +{ +} +*/ +void RemovePowersKnownOnLevel(object oCreature, int nLevel) +{ + if(DEBUG) DoDebug("psi_inc_powknown: RemovePowersKnownOnLevel():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + + string sPostFix = _POWER_LIST_LEVEL_ARRAY + IntToString(nLevel); + // For each power list, determine if an array exists for this level. + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_PSION) + sPostFix)) + // If one does exist, clear it + _RemovePowerArray(oCreature, POWER_LIST_PSION, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_WILDER) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_WILDER, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_PSYWAR) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_PSYWAR, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_PSYROG) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_PSYROG, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_FIST_OF_ZUOKEN) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_FIST_OF_ZUOKEN, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_WARMIND) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_WARMIND, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_EXP_KNOWLEDGE) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_EXP_KNOWLEDGE, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_EPIC_EXP_KNOWLEDGE) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_EPIC_EXP_KNOWLEDGE, nLevel); + + if(persistant_array_exists(oCreature, _POWER_LIST_NAME_BASE + IntToString(POWER_LIST_MISC) + sPostFix)) + _RemovePowerArray(oCreature, POWER_LIST_MISC, nLevel); +} + +int GetKnownPowersModifier(object oCreature, int nList) +{ + return GetPersistantLocalInt(oCreature, _POWER_LIST_NAME_BASE + IntToString(nList) + _POWER_LIST_MODIFIER); +} + +void SetKnownPowersModifier(object oCreature, int nList, int nNewValue) +{ + SetPersistantLocalInt(oCreature, _POWER_LIST_NAME_BASE + IntToString(nList) + _POWER_LIST_MODIFIER, nNewValue); +} + +int GetPowerCount(object oCreature, int nList) +{ + return GetPersistantLocalInt(oCreature, _POWER_LIST_NAME_BASE + IntToString(nList) + _POWER_LIST_TOTAL_KNOWN); +} + +int GetMaxPowerCount(object oCreature, int nList) +{ + int nMaxPowers = 0; + + switch(nList) + { + case POWER_LIST_PSION:{ + // Determine base powers known + int nLevel = GetLevelByClass(CLASS_TYPE_PSION, oCreature); + nLevel += GetPsionicPRCLevels(oCreature, CLASS_TYPE_PSION); + //nLevel += GetPrimaryPsionicClass(oCreature) == CLASS_TYPE_PSION ? GetPsionicPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxPowers = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_PSION), "PowersKnown", nLevel - 1)); + + // Calculate feats + // Apply the epic feat Power Knowledge - +2 powers known per + int nFeat = FEAT_POWER_KNOWLEDGE_PSION_1; + while(nFeat <= FEAT_POWER_KNOWLEDGE_PSION_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 2; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_WILDER:{ + // Determine base powers known + int nLevel = GetLevelByClass(CLASS_TYPE_WILDER, oCreature); + nLevel += GetPsionicPRCLevels(oCreature, CLASS_TYPE_WILDER); + //nLevel += GetPrimaryPsionicClass(oCreature) == CLASS_TYPE_WILDER ? GetPsionicPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxPowers = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_WILDER), "PowersKnown", nLevel - 1)); + + // Calculate feats + // Apply the epic feat Power Knowledge - +2 powers known per + int nFeat = FEAT_POWER_KNOWLEDGE_WILDER_1; + while(nFeat <= FEAT_POWER_KNOWLEDGE_WILDER_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 2; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_PSYWAR:{ + // Determine base powers known + int nLevel = GetLevelByClass(CLASS_TYPE_PSYWAR, oCreature); + nLevel += GetPsionicPRCLevels(oCreature, CLASS_TYPE_PSYWAR); + //nLevel += GetPrimaryPsionicClass(oCreature) == CLASS_TYPE_PSYWAR ? GetPsionicPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxPowers = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_PSYWAR), "PowersKnown", nLevel - 1)); + + // Calculate feats + // Apply the epic feat Power Knowledge - +2 powers known per + int nFeat = FEAT_POWER_KNOWLEDGE_PSYWAR_1; + while(nFeat <= FEAT_POWER_KNOWLEDGE_PSYWAR_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 2; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_PSYROG:{ + // Determine base powers known + int nLevel = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oCreature); + nLevel += GetPsionicPRCLevels(oCreature, CLASS_TYPE_PSYCHIC_ROGUE); + //nLevel += GetPrimaryPsionicClass(oCreature) == CLASS_TYPE_PSYCHIC_ROGUE ? GetPsionicPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxPowers = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_PSYCHIC_ROGUE), "PowersKnown", nLevel - 1)); + + // Calculate feats + // Apply the epic feat Power Knowledge - +2 powers known per + int nFeat = FEAT_POWER_KNOWLEDGE_PSYROG_1; + while(nFeat <= FEAT_POWER_KNOWLEDGE_PSYROG_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 2; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_FIST_OF_ZUOKEN:{ + // Determine base powers known + int nLevel = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature); + nLevel += GetPsionicPRCLevels(oCreature, CLASS_TYPE_FIST_OF_ZUOKEN); + //nLevel += GetPrimaryPsionicClass(oCreature) == CLASS_TYPE_FIST_OF_ZUOKEN ? GetPsionicPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxPowers = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_FIST_OF_ZUOKEN), "PowersKnown", nLevel - 1)); + + // Calculate feats + // Apply the epic feat Power Knowledge - +2 powers known per + int nFeat = FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_1; + while(nFeat <= FEAT_POWER_KNOWLEDGE_FIST_OF_ZUOKEN_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 2; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_WARMIND:{ + // Determine base powers known + int nLevel = GetLevelByClass(CLASS_TYPE_WARMIND, oCreature); + nLevel += GetPsionicPRCLevels(oCreature, CLASS_TYPE_WARMIND); + //nLevel += GetPrimaryPsionicClass(oCreature) == CLASS_TYPE_WARMIND ? GetPsionicPRCLevels(oCreature) : 0; + if(nLevel == 0) + break; + nMaxPowers = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_WARMIND), "PowersKnown", nLevel - 1)); + + // Calculate feats + // Apply the epic feat Power Knowledge - +2 powers known per + int nFeat = FEAT_POWER_KNOWLEDGE_WARMIND_1; + while(nFeat <= FEAT_POWER_KNOWLEDGE_WARMIND_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 2; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_EXP_KNOWLEDGE:{ + // Calculate feats + // Apply the feat Expanded Knowledge - +1 powers known per feat + int nFeat = FEAT_EXPANDED_KNOWLEDGE_1; + while(nFeat <= FEAT_EXPANDED_KNOWLEDGE_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 1; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_EPIC_EXP_KNOWLEDGE:{ + int nFeat = FEAT_EPIC_EXPANDED_KNOWLEDGE_1; + while(nFeat <= FEAT_EPIC_EXPANDED_KNOWLEDGE_10 && + GetHasFeat(nFeat, oCreature)) + { + nMaxPowers += 1; + nFeat++; + } + + // Add in the custom modifier + nMaxPowers += GetKnownPowersModifier(oCreature, nList); + break; + } + case POWER_LIST_MISC:{ + //no limits here, should not be checked + } + + default:{ + string sErr = "GetMaxPowerCount(): ERROR: Unknown power list value: " + IntToString(nList); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return nMaxPowers; +} + +int GetHasPower(int nPower, object oCreature = OBJECT_SELF) +{ + if((GetLevelByClass(CLASS_TYPE_PSION, oCreature) + && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_PSION), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_PSYWAR, oCreature) + && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_PSYWAR), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oCreature) + && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_PSYCHIC_ROGUE), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_WILDER, oCreature) + && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_WILDER), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature) + && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_FIST_OF_ZUOKEN), oCreature) + ) || + (GetLevelByClass(CLASS_TYPE_WARMIND, oCreature) + && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_WARMIND), oCreature) + ) + // add new psionic classes here + ) + return TRUE; + return FALSE; +} + +string DebugListKnownPowers(object oCreature) +{ + string sReturn = "Powers known by " + DebugObject2Str(oCreature) + ":\n"; + int i, j, k, numPowerLists = 8; + int nPowerList, nSize; + string sTemp, sArray, sArrayBase, sPowerFile; + // Loop over all power lists + for(i = 1; i <= numPowerLists; i++) + { + // Some padding + sReturn += " "; + // Get the power list for this loop + switch(i) + { + case 1: nPowerList = POWER_LIST_PSION; sReturn += "Psion"; break; + case 2: nPowerList = POWER_LIST_WILDER; sReturn += "Wilder"; break; + case 3: nPowerList = POWER_LIST_PSYWAR; sReturn += "Psychic Warrior"; break; + case 4: nPowerList = POWER_LIST_PSYROG; sReturn += "Psychic Rogue"; break; + case 5: nPowerList = POWER_LIST_FIST_OF_ZUOKEN; sReturn += "Fist of Zuoken"; break; + case 6: nPowerList = POWER_LIST_WARMIND; sReturn += "Warmind"; break; + + // This should always be last + case 7: nPowerList = POWER_LIST_EXP_KNOWLEDGE; sReturn += "Expanded Knowledge"; break; + case 8: nPowerList = POWER_LIST_EPIC_EXP_KNOWLEDGE; sReturn += "Epic Expanded Knowledge"; break; + case 9: nPowerList = POWER_LIST_MISC; sReturn += "Misceallenous"; break; + } + sReturn += " powers known:\n"; + + // Determine if the character has any powers from this list + sPowerFile = GetAMSDefinitionFileName(nPowerList); + sArrayBase = _POWER_LIST_NAME_BASE + IntToString(nPowerList); + + // Loop over levels + for(j = 1; j <= GetHitDice(oCreature); j++) + { + sArray = sArrayBase + _POWER_LIST_LEVEL_ARRAY + IntToString(j); + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Gained on level " + IntToString(j) + ":\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", + GetPowerFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + // Non-leveldependent powers + sArray = sArrayBase + _POWER_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Non-leveldependent:\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", + GetPowerFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + return sReturn; +} + +/* Probably unnecessary +int ClassTypeToPowerList(int nClassType) +{ + int nReturn = 0; + switch(nClassType) + { + case CLASS_TYPE_PSION: nReturn = POWER_LIST_PSION; break; + case CLASS_TYPE_WILDER: nReturn = POWER_LIST_WILDER; break; + case CLASS_TYPE_PSYWAR: nReturn = POWER_LIST_PSYWAR; break; + case CLASS_TYPE_FIST_OF_ZUOKEN: nReturn = POWER_LIST_FIST_OF_ZUOKEN; break; + + case CLASS_TYPE_INVALID: nReturn = POWER_LIST_EXP_KNOWLEDGE; break; + case -2: nReturn = POWER_LIST_EPIC_EXP_KNOWLEDGE; break; + case -3: nReturn = POWER_LIST_MISC; break; + + default:{ + string sErr = "ClassTypeToPowerList(): ERROR: Unknown class type value: " + IntToString(nClassType); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return nReturn; +} + +int PowerListToClassType(int nPowerList) +{ + int nReturn = 0; + switch(nPowerList) + { + case POWER_LIST_PSION: nReturn = CLASS_TYPE_PSION; break; + case POWER_LIST_WILDER: nReturn = CLASS_TYPE_WILDER; break; + case POWER_LIST_PSYWAR: nReturn = CLASS_TYPE_PSYWAR; break; + case POWER_LIST_FIST_OF_ZUOKEN: nReturn = CLASS_TYPE_FIST_OF_ZUOKEN; break; + + case POWER_LIST_EXP_KNOWLEDGE: nReturn = CLASS_TYPE_INVALID; break; + case POWER_LIST_EPIC_EXP_KNOWLEDGE: nReturn = -2; break; + case POWER_LIST_MISC: nReturn = -3; break; + + default:{ + string sErr = "PowerListToClassType(): ERROR: Unknown power list value: " + IntToString(nPowerList); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return nReturn; +} +*/ +// Test main +//void main(){} diff --git a/src/include/psi_inc_ppoints.nss b/src/include/psi_inc_ppoints.nss new file mode 100644 index 0000000..a98f0fb --- /dev/null +++ b/src/include/psi_inc_ppoints.nss @@ -0,0 +1,375 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Power Points +//:: psi_inc_ppoints +//:://///////////////////////////////////////////// +/** @file + Defines functions for handling power points. + + @author Ornedan + @date Created - 2005.11.04 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Constants are provided via psi_inc_core + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the given character's current power point count. + * + * @param oChar Character whose power points to examine + * @return The character's current power point count + */ +/* + * @param bCountTemporary If TRUE, the returned value is the sum + * of the character's real and temporary PP, + * otherwise just the real PP + */ +int GetCurrentPowerPoints(object oChar/*, int bCountTemporary = TRUE*/); + +/** + * Returns the given character's maximum power point count. + * + * @param oChar Character whose power points to examine + * @return The maximum number of power points the character + * can normally have + */ +int GetMaximumPowerPoints(object oChar); + +/** + * Returns the current power point count as a string in format: + * "[current] / [maximum]" + * + * @param oChar Character whose power points to examine + * @return The given character's power point data in a formatted string + */ +string GetPowerPointsAsString(object oChar); + +/** + * Sends the given character a message telling their current power point + * count. Format: + * "Power Points Remaining: [current] / [maximum]" + * + * @param oChar Character whom to inform about their power points + */ +void TellCharacterPowerPointStatus(object oChar); + +/** + * Resets current power point count to maximum power points. + * Any temporary power points are removed. + * + * @param oChar Character to perform power point reseting for. + */ +void ResetPowerPoints(object oChar); + +/** + * Increases the character's current power point count by up to the given + * amount, limited to the character's maximum for real power points unless + * specifically allowed to exceed the maximum. + * + * @param oChar Character whose power points to adjust + * @param nGain How many power points to add + * @param bCanExceedMax Whether the power point total can exceed the normal + * maximum as a result of this increase + * @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar + * after making the modification. + */ +void GainPowerPoints(object oChar, int nGain, int bCanExceedMax = FALSE, int bInform = TRUE); + +/** + * Gives the character an amount of temporary power points. Temporary power + * points are always used first and ignore the maximum PP limit. + * + * @param oChar Character whose power points to adjust + * @param nGain How many power points to add + * @param fDuration How long the temporary power points will last, in seconds + * @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar + * after making the modification. + */ +/* +void GainTemporaryPowerPoints(object oChar, int nGain, float fDuration, int bInform = TRUE); +*/ +/** + * Decreases the character's current power point count by up to the given + * amount, limited to not going below 0. + * Reaching 0 PP causes loss of psionic focus. + * + * @param oChar Character whose power points to adjust + * @param nLoss How many power points to remove + * @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar + * after making the modification. + */ +void LosePowerPoints(object oChar, int nLoss, int bInform = TRUE); + +/** + * Unconditionally sets the given character's power point count to 0. + * This causes psionic focus loss as normal. + * + * @param oChar Character whose power points to null + * @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar + * after making the modification. + */ +void LoseAllPowerPoints(object oChar, int bInform = TRUE); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "psi_inc_core" + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function + * @param oChar Character whose feats to evaluate + * @return The amount of Power Points gained from Feats + */ +int _GetFeatBonusPP(object oChar) +{ + int nBonusPP = 0; + +//:: Wild Talent & Hidden Talents + if(GetHasFeat(FEAT_WILD_TALENT, oChar) || IsHiddenTalent()) + nBonusPP += 2; + +//:: Psionic Feats + int i; + int nPsiTalents; + for(i = FEAT_PSIONIC_TALENT_1; i <= FEAT_PSIONIC_TALENT_10; i++) + if(GetHasFeat(i, oChar)) nPsiTalents++; + + nBonusPP += nPsiTalents * (2 + nPsiTalents + 1) / 2; + +//:: Epic feats + int nImpManifestations; + for(i = FEAT_IMPROVED_MANIFESTATION_1; i <= FEAT_IMPROVED_MANIFESTATION_10; i++) + if(GetHasFeat(i, oChar)) nImpManifestations++; + + nBonusPP += nImpManifestations * (18 + nImpManifestations); + + // Racial boni + if(GetHasFeat(FEAT_NATPSIONIC_1, oChar)) + nBonusPP += 1; + if(GetHasFeat(FEAT_NATPSIONIC_2, oChar)) + nBonusPP += 2; + if(GetHasFeat(FEAT_NATPSIONIC_3, oChar)) + nBonusPP += 3; + + if(GetRacialType(oChar) == RACIAL_TYPE_KALASHTAR) + nBonusPP += GetHitDice(oChar); + + if(GetRacialType(oChar) == RACIAL_TYPE_EMPTY_VESSEL) + nBonusPP += GetHitDice(oChar); + + if(GetHasFeat(FEAT_ABERRANT_WARPED_MIND, oChar)) + nBonusPP += GetAberrantFeatCount(oChar); + + return nBonusPP; +} + +/** Internal function + * @param oChar Character whose ability modifier to evaluate + * @param nFirstPsiClass The CLASS_TYPE_* of the character's first psionic class + * @return The amount of Bonus Power Points gained from Abilities + */ +int _GetModifierPP (object oChar, int nFirstPsiClass) +{ + int nPP = 0; + int nBonus; + + int nPsion = GetLevelByClass(CLASS_TYPE_PSION, oChar) + (GetPsionicPRCLevels(oChar, CLASS_TYPE_PSION)); + + int nPsychic = GetLevelByClass(CLASS_TYPE_PSYWAR, oChar) + (GetPsionicPRCLevels(oChar, CLASS_TYPE_PSYWAR)); + + int nRogue = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oChar) + (GetPsionicPRCLevels(oChar, CLASS_TYPE_PSYCHIC_ROGUE)); + + int nWilder = GetLevelByClass(CLASS_TYPE_WILDER, oChar) + (GetPsionicPRCLevels(oChar, CLASS_TYPE_WILDER)); + + int nZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oChar) + (GetPsionicPRCLevels(oChar, CLASS_TYPE_FIST_OF_ZUOKEN)); + + int nWarmind = GetLevelByClass(CLASS_TYPE_WARMIND, oChar) + (GetPsionicPRCLevels(oChar, CLASS_TYPE_WARMIND)); + +/* int nPsion = GetLevelByClass(CLASS_TYPE_PSION, oChar) + + (nFirstPsiClass == CLASS_TYPE_PSION ? GetPsionicPRCLevels(oChar) : 0); + int nPsychic = GetLevelByClass(CLASS_TYPE_PSYWAR, oChar) + + (nFirstPsiClass == CLASS_TYPE_PSYWAR ? GetPsionicPRCLevels(oChar) : 0); + int nRogue = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oChar) + + (nFirstPsiClass == CLASS_TYPE_PSYCHIC_ROGUE ? GetPsionicPRCLevels(oChar) : 0); + int nWilder = GetLevelByClass(CLASS_TYPE_WILDER, oChar) + + (nFirstPsiClass == CLASS_TYPE_WILDER ? GetPsionicPRCLevels(oChar) : 0); + int nZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oChar) + + (nFirstPsiClass == CLASS_TYPE_FIST_OF_ZUOKEN ? GetPsionicPRCLevels(oChar) : 0); + int nWarmind = GetLevelByClass(CLASS_TYPE_WARMIND, oChar) + + (nFirstPsiClass == CLASS_TYPE_WARMIND ? GetPsionicPRCLevels(oChar) : 0); */ + + if(nPsion > 0) + { + if(nPsion > 20) nPsion = 20; + nBonus = (nPsion * GetAbilityModifier(ABILITY_INTELLIGENCE, oChar)) / 2; + nPP += nBonus; + } + if(nPsychic > 0) + { + if(nPsychic > 20) nPsychic = 20; + nBonus = (nPsychic * GetAbilityModifier(ABILITY_WISDOM, oChar)) / 2; + nPP += nBonus; + } + if(nRogue > 0) + { + if(nRogue > 20) nRogue = 20; + nBonus = (nRogue * GetAbilityModifier(ABILITY_INTELLIGENCE, oChar)) / 2; + nPP += nBonus; + } + if(nWilder > 0) + { + if(nWilder > 20) nWilder = 20; + nBonus = (nWilder * GetAbilityModifier(ABILITY_CHARISMA, oChar)) / 2; + nPP += nBonus; + } + if(nZuoken > 0) + { + if(nZuoken > 10) nZuoken = 10; + nBonus = (nZuoken * GetAbilityModifier(ABILITY_WISDOM, oChar)) / 2; + nPP += nBonus; + } + if(nWarmind > 0) + { + if(nWarmind > 10) nWarmind = 10; + nBonus = (nWarmind * GetAbilityModifier(ABILITY_WISDOM, oChar)) / 2; + nPP += nBonus; + } + + return nPP; +} + +/** Internal function + * @param oChar Character whose classes to evaluate + * @param nClass The CLASS_TYPE_* of the specific class to evaluate + * @param nFirstPsiClass The CLASS_TYPE_* of the character's first psionic class + * @return The amount of Power Points gained from levels in the + * given class + */ +int _GetPPForClass (object oChar, int nClass, int nFirstPsiClass) +{ + int nPP; + int nLevel = GetLevelByClass(nClass, oChar) + GetPsionicPRCLevels(oChar, nClass); + + //+ (nFirstPsiClass == nClass ? GetPsionicPRCLevels(oChar) : 0); + string sPsiFile = GetAMSKnownFileName(nClass); + nPP = StringToInt(Get2DACache(sPsiFile, "PowerPoints", nLevel - 1)); // Index from 0 + + return nPP; +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetCurrentPowerPoints(object oChar/*, int bCountTemporary = TRUE*/) +{ + int nRealPP = GetLocalInt(oChar, POWER_POINT_VARNAME); + //int nTemporaryPP = 0; ///@todo If necessary + + return nRealPP; //+ nTemporaryPP; +} + +int GetMaximumPowerPoints(object oChar) +{ + int nMaxPP; + + // The character's first psionic class is considered to be the one that +ML PrCs add to + int nFirstPsiClass = GetPrimaryPsionicClass(oChar); + + nMaxPP += _GetPPForClass(oChar, CLASS_TYPE_PSION, nFirstPsiClass); + nMaxPP += _GetPPForClass(oChar, CLASS_TYPE_WILDER, nFirstPsiClass); + nMaxPP += _GetPPForClass(oChar, CLASS_TYPE_PSYWAR, nFirstPsiClass); + nMaxPP += _GetPPForClass(oChar, CLASS_TYPE_PSYCHIC_ROGUE, nFirstPsiClass); + nMaxPP += _GetPPForClass(oChar, CLASS_TYPE_FIST_OF_ZUOKEN, nFirstPsiClass); + nMaxPP += _GetPPForClass(oChar, CLASS_TYPE_WARMIND, nFirstPsiClass); + + nMaxPP += _GetModifierPP(oChar, nFirstPsiClass); + + nMaxPP += _GetFeatBonusPP(oChar); + + return nMaxPP; +} + +string GetPowerPointsAsString(object oChar) +{ + return IntToString(GetCurrentPowerPoints(oChar)) + " / " + IntToString(GetMaximumPowerPoints(oChar)); +} + +void TellCharacterPowerPointStatus(object oChar) +{ + FloatingTextStringOnCreature(GetStringByStrRef(16824181) + " " + GetPowerPointsAsString(oChar), // "Power Points Remaining:" + oChar, FALSE); +} + +void ResetPowerPoints(object oChar) +{ + SetLocalInt(oChar, POWER_POINT_VARNAME, GetMaximumPowerPoints(oChar)); +} + +void GainPowerPoints(object oChar, int nGain, int bCanExceedMax = FALSE, int bInform = TRUE) +{ + int nCurPP = GetCurrentPowerPoints(oChar/*, FALSE*/); + nCurPP += nGain; + + if(!bCanExceedMax) + { + int nMaxPP = GetMaximumPowerPoints(oChar); + if(nCurPP > nMaxPP) + nCurPP = nMaxPP; + } + + SetLocalInt(oChar, POWER_POINT_VARNAME, nCurPP); + + if(bInform) + TellCharacterPowerPointStatus(oChar); +} + +/* +void GainTemporaryPowerPoints(object oChar, int nGain, float fDuration, int bInform = TRUE) +{ +} +*/ + +void LosePowerPoints(object oChar, int nLoss, int bInform = TRUE) +{ + int nCurPP = GetCurrentPowerPoints(oChar/*, FALSE*/); + nCurPP -= nLoss; + if(nCurPP < 0) + nCurPP = 0; + + SetLocalInt(oChar, POWER_POINT_VARNAME, nCurPP); + + if(GetCurrentPowerPoints(oChar) == 0) + LosePsionicFocus(oChar); + + if(bInform) + TellCharacterPowerPointStatus(oChar); +} + +void LoseAllPowerPoints(object oChar, int bInform = TRUE) +{ + SetLocalInt(oChar, POWER_POINT_VARNAME, 0); + + LosePsionicFocus(oChar); + + if(bInform) + TellCharacterPowerPointStatus(oChar); +} + +// Test main +//void main(){} diff --git a/src/include/psi_inc_psicraft.nss b/src/include/psi_inc_psicraft.nss new file mode 100644 index 0000000..058ba59 --- /dev/null +++ b/src/include/psi_inc_psicraft.nss @@ -0,0 +1,84 @@ +//:://///////////////////////////////////////////// +//:: Psionic include: Psicraft Skill +//:: psi_inc_psicraft +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the Psicraft skill + + Functions below are called by the manifester as + he makes a power. + + @author Stratovarius + @date Created - 2008.9.17 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Constants are provided via psi_inc_core + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the power that the manifester just used + * @param oManifester The power manifester + * @param nPowerId Power to check + * + * @return nothing, uses SendMessageToPC to give results + */ +void IdentifyPower(object oManifester, int nPowerId); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +// Always access via psi_inc_psifunc. + +//#include "psi_inc_core" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _DoPsicraftCheck(object oManifester, object oCheck, int nPowerLevel, int nPowerId) +{ + // NPCs wouldn't benefit from being told the name of the maneuver + if (!GetIsPC(oCheck)) return; + + // Roll the check + if(GetIsSkillSuccessful(oCheck, SKILL_PSICRAFT, 10 + nPowerLevel)) + { // get the name of the manifester and power + FloatingTextStringOnCreature(GetName(oManifester) + " manifests " + GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nPowerId))), oCheck, FALSE); + } + else // Skill check failed + { + FloatingTextStringOnCreature(GetName(oManifester) + " manifests unknown power", oCheck, FALSE); + } +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void IdentifyPower(object oManifester, int nPowerId) +{ + int nPowerLevel = GetPowerLevel(oManifester); + + // The area to check for martial lore users + object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(oManifester), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget) && oTarget != oManifester) + { + // If the target has points in the skill + if (GetSkillRank(SKILL_PSICRAFT, oTarget) > 0) _DoPsicraftCheck(oManifester, oTarget, nPowerLevel, nPowerId); + + //Select the next target within the area. + oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(oManifester), TRUE, OBJECT_TYPE_CREATURE); + } +} \ No newline at end of file diff --git a/src/include/psi_inc_psifunc.nss b/src/include/psi_inc_psifunc.nss new file mode 100644 index 0000000..5e96a40 --- /dev/null +++ b/src/include/psi_inc_psifunc.nss @@ -0,0 +1,1078 @@ +//:://///////////////////////////////////////////// +//:: Psionics include: Manifesting +//:: psi_inc_psifunc - was psi_inc_manifest +//:://///////////////////////////////////////////// +/** @file + Defines structures and functions for handling + manifesting a power and main nexus for + Psi function access. + + Acts as inclusion nexus for the general + psionics includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Ornedan + @date Created - 2005.11.19 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +// Constants are provided via psi_inc_core + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines if the power that is currently being attempted to be manifested + * can in fact be manifested. Calculates PP cost and pays it. Determines + * augmentation and metapsionics used. + * + * @param oManifester A creature attempting to manifest a power at this moment. + * @param oTarget The target of the power, if any. For pure Area of Effect. + * powers, this should be OBJECT_INVALID. Otherwise the main + * target of the power as returned by PRCGetSpellTargetObject(). + * @param pap A power augmentation profile generated by a call to + * PowerAugmentationProfile(). This specifies how the power may + * be augmented. + * @param nMetaPsiFlags The metapsionics that may be used to modify this power. Any number + * of METAPSIONIC_* constants ORd together using the | operator. + * For example (METAPSIONIC_EMPOWER | METAPSIONIC_MAXIMIZE) + * + * @return A manifestation structure that contains the data about whether + * the power was successfully manifested, what augmentation and + * metapsionics were used and some other commonly used data, like + * the manifester's manifester level for this manifestation. + */ +struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags); + +/** + * Causes OBJECT_SELF to use the given power. + * + * @param nPower The index of the power to use in spells.2da or a POWER_* + * @param nClass The index of the class to use the power as in classes.2da or a CLASS_TYPE_* + * @param bIsPsiLike Whether the power to be used is to be a normal use or a psi-like ability, which + * acts somewhat differently. + * Default: FALSE, meaning a normal power. + * @param nLevelOverride An optional override to normal manifester level. This is necessary when + * using the power as a psi-like ability. + * Default: 0, which means the parameter is ignored. + */ +void UsePower(int nPower, int nClass, int bIsPsiLike = FALSE, int nLevelOverride = 0); + +/** + * A debugging function. Takes a manifestation structure and + * makes a string describing the contents. + * + * @param manif A set of manifestation data + * @return A string describing the contents of manif + */ +string DebugManifestation2Str(struct manifestation manif); + +/** + * Stores a manifestation structure as a set of local variables. If + * a structure was already stored with the same name on the same object, + * it is overwritten. + * + * @param oObject The object on which to store the structure + * @param sName The name under which to store the structure + * @param manif The manifestation structure to store + */ +void SetLocalManifestation(object oObject, string sName, struct manifestation manif); + +/** + * Retrieves a previously stored manifestation structure. If no structure is stored + * by the given name, the structure returned is empty. + * + * @param oObject The object from which to retrieve the structure + * @param sName The name under which the structure is stored + * @return The structure built from local variables stored on oObject under sName + */ +struct manifestation GetLocalManifestation(object oObject, string sName); + +/** + * Sets the evaluation functions to ignore constraints on manifesting. + * Call this just prior to EvaluateManifestation() in a power script. + * That evaluation will then ignore lacking manifestation ability score, + * Power Points and Psionic Focuses. + * + * @param oManifester A creature attempting to manifest a power at this moment. + */ +void DebugIgnoreConstraints(object oManifester); + +/** + * Determines if the power that is currently being attempted to be manifested + * can in fact be manifested. Calculates PP cost and pays it. Determines + * augmentation and metapsionics used. + * + * @param oManifester A creature attempting to manifest a power at this moment. + * @param oTarget The target of the power, if any. For pure Area of Effect. + * powers, this should be OBJECT_INVALID. Otherwise the main + * target of the power as returned by PRCGetSpellTargetObject(). + * @param nPowerLevel The "power level" of the ability, used for determining PP cost. + * @param pap A power augmentation profile generated by a call to + * PowerAugmentationProfile(). This specifies how the power may + * be augmented. + * @param nPowerLevel The "power level" of the ability, used for determining PP cost. + * + * @return A manifestation structure that contains the data about whether + * the ability was successfully manifested, what augmentation and + * metapsionics were used and some other commonly used data, like + * the manifester's manifester level for this manifestation. + */ +struct manifestation EvaluateDiaDragChannel(object oManifester, object oTarget, struct power_augment_profile pap, int nPowerLevel); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "psi_inc_metapsi" +#include "psi_inc_ppoints" // +#include "psi_inc_augment" // +#include "psi_inc_psicraft" // Provides Psicraft identifying +#include "psi_inc_powknown" // + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Calculates PP cost reduction from various factors. Currently accounts for: + * - Thrallherd + * - Shadowmind + * + * @param manif The manifestation data relating to this particular manifesation + * @retrun The manifestation data, possibly with modified costs + */ +struct manifestation _GetPPCostReduced(struct manifestation manif) +{ + int nSpell = PRCGetSpellId(); + int nThrall = GetLevelByClass(CLASS_TYPE_THRALLHERD, manif.oManifester); + int nShadow = GetLevelByClass(CLASS_TYPE_SHADOWMIND, manif.oManifester); + + if(nThrall > 0) + { + if(GetLocalInt(manif.oManifester, "ThrallCharm") && nSpell == POWER_CHARMPERSON) + { + DeleteLocalInt(manif.oManifester, "ThrallCharm"); + manif.nPPCost -= nThrall; + } + if(GetLocalInt(manif.oManifester, "ThrallDom") && nSpell == POWER_DOMINATE) + { + DeleteLocalInt(manif.oManifester, "ThrallDom"); + manif.nPPCost -= nThrall; + } + + // Reduced cost for augmenting the Dominate power. These do not count for the DC increase + if(nThrall >= 7 && nSpell == POWER_DOMINATE && manif.nTimesAugOptUsed_1 > 0) + { + manif.nPPCost -= 2; + manif.nTimesGenericAugUsed -= 1; + } + if(nThrall >= 9 && nSpell == POWER_DOMINATE && manif.nTimesAugOptUsed_2 > 0) + { + manif.nPPCost -= 4; + manif.nTimesGenericAugUsed -= 2; + } + + if(manif.nPPCost < 1) manif.nPPCost = 1; + } + + if (nShadow > 0) + { + if(GetLocalInt(manif.oManifester, "ShadowDistract") && nSpell == POWER_DISTRACT) + { + DeleteLocalInt(manif.oManifester, "ShadowDistract"); + manif.nPPCost -= nShadow; + } + if(GetLocalInt(manif.oManifester, "ShadowCloudMind") && nSpell == POWER_CLOUD_MIND) + { + DeleteLocalInt(manif.oManifester, "ShadowCloudMind"); + manif.nPPCost -= nShadow; + } + if(GetLocalInt(manif.oManifester, "ShadowCloudMindMass") && nSpell == POWER_CLOUD_MIND_MASS) + { + DeleteLocalInt(manif.oManifester, "ShadowCloudMindMass"); + manif.nPPCost -= nShadow; + } + + if(manif.nPPCost < 1) manif.nPPCost = 1; + } + + return manif; +} + +/** Internal function. + * A wilder that is of high enough level to posses the Volatile Mind class + * feature causes extra cost to be applied to telepathy powers manifested + * on it. + * + * @param oTarget Target of the power being manifested at the moment + * @param oManifester Creature manifesting the power + * @return Either 0 if the character does not posses the Volatile + * Mind class feature or the power is not of the telepathy + * discipline. Otherwise, a number determined by the target's + * Wilder level. + */ +int _VolatileMind(object oTarget, object oManifester) +{ + int nWilder = GetLevelByClass(CLASS_TYPE_WILDER, oTarget); + int nTelepathy = GetIsTelepathyPower(); + int nCost = 0; + + if(nTelepathy && // Only affects telepathy powers. + nWilder >= 5 && // Only wilders need apply + // Since the "As a standard action, a wilder can choose to lower this effect for 1 round." + // bit is not particularly doable in NWN, we implement it so that the class feature + // only affects powers from hostile manifesters + GetIsEnemy(oTarget, oManifester) + ) + { + nCost = ((nWilder - 5) / 4) + 1; + } + + return nCost; +} + +/** Internal function. + * Calculates the extra cost caused by the Psionic Hole feat. + * + * @param oTarget The target of a power currently being manifested + * @return If the target has the Psionic Hole feat, the greater of + * it's Wisdom modifier and 0. Otherwise, just 0. + */ +int _PsionicHole(object oTarget) +{ + int nCost = 0; + + if(GetHasFeat(FEAT_PSIONIC_HOLE, oTarget)) + // Psionic Hole will never decrease power cost, even if the target is lacking in wisdom bonus + nCost = PRCMax(GetAbilityModifier(ABILITY_WISDOM, oTarget), 0); + + return nCost; +} + +/** Internal function. + * Applies Hostile Mind damage if the target posses the feat and is being + * targeted with a telepathy power. + * + * @param oManifester A creature currently manifesting a power at oTarget + * @param oTarget The target of the power being manifested. + */ +void _HostileMind(object oManifester, object oTarget) +{ + if(GetHasFeat(FEAT_HOSTILE_MIND, oTarget) && GetIsTelepathyPower()) + { + // Save DC is 10 + HD/2 + ChaMod + int nDC = 10 + GetHitDice(oTarget) / 2 + GetAbilityModifier(ABILITY_CHARISMA, oTarget); + if(!PRCMySavingThrow(SAVING_THROW_WILL, oManifester, nDC, SAVING_THROW_TYPE_NONE)) + { + //Apply damage and some VFX + SPApplyEffectToObject(DURATION_TYPE_INSTANT, + EffectLinkEffects(EffectDamage(d6(2)), EffectVisualEffect(VFX_FNF_SPELL_FAIL_HEA)), + oManifester + ); + } + } +} + +/** Internal function. + * Applies the damage caused by use of Overchannel feat. + * + * @param oManifester The creature currently manifesting a power + * @param bIsPsiLike Whether the power being manifester is a psi-like ability or not + */ +void _DoOverchannelDamage(object oManifester, int bIsPsiLike) +{ + int nOverchannel = GetLocalInt(oManifester, PRC_OVERCHANNEL); + if(nOverchannel > 0 && !bIsPsiLike) + { + int nDam = d8(nOverchannel * 2 - 1); + // Check if Talented applies + if(GetPowerLevel(oManifester) <= 3) + { + if(GetLocalInt(oManifester, "TalentedActive") && UsePsionicFocus(oManifester)) + return; + /* Should we be merciful and let the feat be "retroactively activated" if the damage were enough to kill? + else if(GetCurrentHitPoints(oCaster) < nDam && GetHasFeat(FEAT_TALENTED, oCaster) && UsePsionicFocus(oCaster)) + return;*/ + } + effect eDam = EffectDamage(nDam); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oManifester); + } +} + +/** Internal function. + * If the manifester is a wilder who is using Wild Surge, rolls and + * applies Psychic Enervation or Surging Euphoria based on the result. + * + * @param oManifester The creature currently manifesting a power + */ +void _SurgingEuphoriaOrPsychicEnervation(object oManifester, int nWildSurge) +{ + int nWilder = GetLevelByClass(CLASS_TYPE_WILDER, oManifester); + + // Only Wilders need apply (at least so far) + if(nWilder > 0 && nWildSurge) + { + // Psychic Enervation has a 5% chance to happen per point Wild Surged by + if(nWildSurge >= d20()) + { + effect eMind = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE); + effect eDaze = EffectDazed(); + effect eLink = EffectLinkEffects(eMind, eDaze); + eLink = ExtraordinaryEffect(eLink); + + FloatingTextStrRefOnCreature(16823620, oManifester, FALSE); // "You have become psychically enervated and lost power points" + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oManifester, RoundsToSeconds(1)); + + LosePowerPoints(oManifester, nWilder); + } + // Need minimum wilder level 4 to be eligible for Surging Euphoria. And it only happens when there is no Enervation + else if(nWilder >= 4) + { + // Euphoria is 1 at levels 4 - 11, 2 at L 12 - 19, 3 at L 20 - 27, etc. + int nEuphoria = ((nWilder - 4) / 8) + 1; + + effect eBonAttack = EffectAttackIncrease(nEuphoria); + effect eBonDam = EffectDamageIncrease(nEuphoria, DAMAGE_TYPE_MAGICAL); + effect eVis = EffectVisualEffect(VFX_IMP_MAGIC_PROTECTION); + effect eSave = EffectSavingThrowIncrease(SAVING_THROW_ALL, nEuphoria, SAVING_THROW_TYPE_SPELL); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE); + effect eDur2 = EffectVisualEffect(VFX_DUR_MAGIC_RESISTANCE); + effect eLink = EffectLinkEffects(eSave, eDur); + eLink = EffectLinkEffects(eLink, eDur2); + eLink = EffectLinkEffects(eLink, eBonDam); + eLink = EffectLinkEffects(eLink, eBonAttack); + eLink = ExtraordinaryEffect(eLink); + + FloatingTextStringOnCreature(GetStringByStrRef(16823616) + ": " + IntToString(nWildSurge), oManifester, FALSE); // "Surging Euphoria: " + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oManifester, RoundsToSeconds(nWildSurge)); + } + } +} + +/** Internal function. + * Applies the PP loss caused by the target having Mind Trap active. + * + * @param oManifester A creature currently manifesting a power at oTarget + * @param oTarget The target of the power being manifested + */ +void _DoMindTrapPPLoss(object oManifester, object oTarget) +{ + if(oManifester != oTarget && // Does not apply to own powers + GetLocalInt(oTarget, "PRC_Power_MindTrap_Active") && // The target does have Mind Trap active + GetIsTelepathyPower() // And the power being used is a telepathy power + ) + { + LosePowerPoints(oManifester, d6()); + } +} + +/** Internal function. + * Handles Spellfire absorption when a power is used on a friendly spellfire + * user. + */ +struct manifestation _DoSpellfireFriendlyAbsorption(struct manifestation manif, object oTarget) +{ + if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") && + GetIsFriend(oTarget, manif.oManifester) + ) + { + if(CheckSpellfire(manif.oManifester, oTarget, TRUE)) + { + PRCShowSpellResist(manif.oManifester, oTarget, SPELL_RESIST_MANTLE); + manif.bCanManifest = FALSE; + } + } + + return manif; +} + +/** Internal function. + * Deletes manifestation-related local variables. + * + * @param oManifester The creature currently manifesting a power + */ +void _CleanManifestationVariables(object oManifester) +{ + DeleteLocalInt(oManifester, PRC_MANIFESTING_CLASS); + DeleteLocalInt(oManifester, PRC_POWER_LEVEL); + DeleteLocalInt(oManifester, PRC_IS_PSILIKE); + DeleteLocalInt(oManifester, PRC_AUGMENT_OVERRIDE); +} + +/** Internal function. + * Determines whether a manifestation token exists. If one does, returns it. + * + * @param oManifester A creature whose manifestation token to get + * @return The manifestation token if it exists, OBJECT_INVALID otherwise. + */ +object _GetManifestationToken(object oManifester) +{ + object oMfToken = GetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR); + + // Special case - variable not set up yet, so this is the characters first manifestation since entering the module +/* Obsoleted by the manifestation token chest in Limbo + if(oMfToken == OBJECT_INVALID) + { + object oSkin = GetPCSkin(oManifester); + object oTest = GetFirstItemInInventory(oSkin); + + // Seek for tokens in the creature's inventory and destroy them + while(GetIsObjectValid(oTest)) + { + if(GetTag(oTest) == PRC_MANIFESTATION_TOKEN_NAME) + DestroyObject(oTest); + oTest = GetNextItemInInventory(oSkin); + } + } +*/ + // If the token object is no longer valid, set the variable to point at manifester + if(!GetIsObjectValid(oMfToken)) + { + oMfToken = oManifester; + SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oMfToken); + } + + + // Check if there is no token + if(oMfToken == oManifester) + oMfToken = OBJECT_INVALID; + + return oMfToken; +} + +/** Internal function. + * Destroys the given manifestation token and sets the creature's manifestation token variable + * to point at itself. + * + * @param oManifester The manifester whose token to destroy + * @param oMfToken The token to destroy + */ +void _DestroyManifestationToken(object oManifester, object oMfToken) +{ + DestroyObject(oMfToken); + SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oManifester); +} + +/** Internal function. + * Destroys the previous manifestation token, if any, and creates a new one. + * + * @param oManifester A creature for whom to create a manifestation token + * @return The newly created token + */ +object _CreateManifestationToken(object oManifester) +{ + object oMfToken = _GetManifestationToken(oManifester); + object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oManifester); + + // Delete any previous tokens + if(GetIsObjectValid(oMfToken)) + _DestroyManifestationToken(oManifester, oMfToken); + + // Create new token and store a reference to it + oMfToken = CreateItemOnObject(PRC_MANIFESTATION_TOKEN_NAME, oStore); + SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oMfToken); + + Assert(GetIsObjectValid(oMfToken), "GetIsObjectValid(oMfToken)", "ERROR: Unable to create manifestation token! Store object: " + DebugObject2Str(oStore), "psi_inc_psifunc", "_CreateManifestationToken()"); + + return oMfToken; +} + +/** Internal function. + * Determines whether the given manifester is doing something that would + * interrupt manifesting a power or affected by an effect that would do + * the same. + * + * @param oManifester A creature on which _ManifestationHB() is running + * @return TRUE if the creature can continue manifesting, + * FALSE otherwise + */ +int _ManifestationStateCheck(object oManifester) +{ + int nAction = GetCurrentAction(oManifester); + // If the current action is not among those that could either be used to manifest the power or movement, the power fails + if(!(nAction || ACTION_CASTSPELL || nAction == ACTION_INVALID || + nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT || + nAction || ACTION_USEOBJECT || nAction == ACTION_WAIT + ) ) + return FALSE; + + // Affected by something that prevents one from manifesting + effect eTest = GetFirstEffect(oManifester); + int nEType; + while(GetIsEffectValid(eTest)) + { + nEType = GetEffectType(eTest); + if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE || + nEType == EFFECT_TYPE_DAZED || + nEType == EFFECT_TYPE_PARALYZE || + nEType == EFFECT_TYPE_PETRIFY || + nEType == EFFECT_TYPE_SLEEP || + nEType == EFFECT_TYPE_STUNNED + ) + return FALSE; + + // Get next effect + eTest = GetNextEffect(oManifester); + } + + return TRUE; +} + +/** Internal function. + * Runs while the given creature is manifesting. If they move, take other actions + * that would cause them to interrupt manifesting the power or are affected by an + * effect that would cause such interruption, deletes the manifestation token. + * Stops if such condition occurs or something else destroys the token. + * + * @param oManifester A creature manifesting a power + * @param lManifester The location where the manifester was when starting the manifestation + * @param oMfToken The manifestation token that controls the ongoing manifestation + */ +void _ManifestationHB(object oManifester, location lManifester, object oMfToken) +{ + if(DEBUG) DoDebug("_ManifestationHB() running:\n" + + "oManifester = " + DebugObject2Str(oManifester) + "\n" + + "lManifester = " + DebugLocation2Str(lManifester) + "\n" + + "oMfToken = " + DebugObject2Str(oMfToken) + "\n" + + "Distance between manifestation start location and current location: " + FloatToString(GetDistanceBetweenLocations(lManifester, GetLocation(oManifester))) + "\n" + ); + if(GetIsObjectValid(oMfToken)) + { + // Continuance check + if(GetDistanceBetweenLocations(lManifester, GetLocation(oManifester)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting + !_ManifestationStateCheck(oManifester) // Action and effect check + ) + { + if(DEBUG) DoDebug("_ManifestationHB(): Manifester moved or lost concentration, destroying token"); + _DestroyManifestationToken(oManifester, oMfToken); + + // Inform manifester + FloatingTextStrRefOnCreature(16828435, oManifester, FALSE); // "You have lost concentration on the power you were attempting to manifest!" + } + // Schedule next HB + else + DelayCommand(PRC_MANIFESTATION_HB_DELAY, _ManifestationHB(oManifester, lManifester, oMfToken)); + } +} + +/** Internal function. + * Checks if the manifester is in range to use the power they are trying to use. + * If not, queues commands to make the manifester to run into range. + * + * @param oManifester A creature manifesting a power + * @param nPower SpellID of the power being manifested + * @param lTarget The target location or the location of the target object + */ +void _ManifestationRangeCheck(object oManifester, int nPower, location lTarget) +{ + float fDistance = GetDistanceBetweenLocations(GetLocation(oManifester), lTarget); + float fRangeLimit; + string sRange = Get2DACache("spells", "Range", nPower); + + // Personal range powers are always in range + if(sRange == "P") + return; + // Ranges according to the CCG spells.2da page + else if(sRange == "T") + fRangeLimit = 2.25f; + else if(sRange == "S") + fRangeLimit = 8.0f; + else if(sRange == "M") + fRangeLimit = 20.0f; + else if(sRange == "L") + fRangeLimit = 40.0f; + + // See if we are out of range + if(fDistance > fRangeLimit) + { + // Create waypoint for the movement + object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget); + + // Move into range, with a bit of fudge-factor + //ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f); + + // Cleanup + ActionDoCommand(DestroyObject(oWP)); + + // Cleanup, paranoia + AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f))); + } +} + +/** Internal function. + * Assigns the fakecast command that is used to display the conjuration VFX when using a power. + * Separated from UsePower() due to a bug with ActionFakeCastSpellAtObject(), which requires + * use of ClearAllActions() to work around. + * The problem is that if the target is an item on the ground, if the actor is out of spell + * range when doing the fakecast, they will run on top of the item instead of to the edge of + * the spell range. This only happens if there was a "real action" in the actor's action queue + * immediately prior to the fakecast. + */ +void _AssignUsePowerFakeCastCommands(object oManifester, object oTarget, location lTarget, int nSpellID) +{ + // Nuke actions to prevent the fakecast action from bugging + ClearAllActions(); + + if(GetIsObjectValid(oTarget)) + ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT); + else + ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT); +} + + +/** Internal function. + * Places the cheatcasting of the real power into the manifester's action queue. + */ +void _UsePowerAux(object oManifester, object oMfToken, int nSpellId, + object oTarget, location lTarget, + int nPower, int nClass, int bIsPsiLike, int nLevelOverride, + int bQuickened + ) +{ + if(DEBUG) DoDebug("_UsePowerAux() running:\n" + + "oManifester = " + DebugObject2Str(oManifester) + "\n" + + "oMfToken = " + DebugObject2Str(oMfToken) + "\n" + + "nSpellId = " + IntToString(nSpellId) + "\n" + + "oTarget = " + DebugObject2Str(oTarget) + "\n" + + "lTarget = " + DebugLocation2Str(lTarget) + "\n" + + "nPower = " + IntToString(nPower) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "bIsPsiLike = " + DebugBool2String(bIsPsiLike) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + + "bQuickened = " + DebugBool2String(bQuickened) + "\n" + ); + + // Make sure nothing has interrupted this manifestation + if(GetIsObjectValid(oMfToken)) + { + if(DEBUG) DoDebug("_UsePowerAux(): Token was valid, queueing actual manifestation"); + // Set the class to manifest as + SetLocalInt(oManifester, PRC_MANIFESTING_CLASS, nClass + 1); + + // Set the power's level + SetLocalInt(oManifester, PRC_POWER_LEVEL, StringToInt(lookup_spell_innate(nSpellId))); + + // Set whether the power is to run as a psi-like ability + SetLocalInt(oManifester, PRC_IS_PSILIKE, bIsPsiLike); + + // Set whether the power was quickened + SetLocalInt(oManifester, PRC_POWER_IS_QUICKENED, bQuickened); + + // Queue the real manifestation + //ActionCastSpell(nPower, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, TRUE, TRUE, oTarget); + + if(nLevelOverride != 0) + AssignCommand(oManifester, ActionDoCommand(SetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride))); + if(GetIsObjectValid(oTarget)) + AssignCommand(oManifester, ActionCastSpellAtObject(nPower, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + else + AssignCommand(oManifester, ActionCastSpellAtLocation(nPower, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + if(nLevelOverride != 0) + AssignCommand(oManifester, ActionDoCommand(DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE))); + + // Destroy the manifestation token for this manifestation + _DestroyManifestationToken(oManifester, oMfToken); + } +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags) +{ + /* Get some data */ + int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // Manifester-related stuff + int nManifesterLevel = GetManifesterLevel(oManifester); + int nPowerLevel = GetPowerLevel(oManifester); + int nClass = GetManifestingClass(oManifester); + int nWildSurge = GetWildSurge(oManifester); + int nManifesterPP = GetCurrentPowerPoints(oManifester); + int bIsPsiLike = GetLocalInt(oManifester, PRC_IS_PSILIKE); + // Target-specific stuff + int nVolatileMindCost = _VolatileMind(oTarget, oManifester); + int nPsionicHoleCost = _PsionicHole(oTarget); + + /* Initialise the manifestation structure */ + struct manifestation manif; + manif.oManifester = oManifester; + manif.bCanManifest = TRUE; // Assume successfull manifestation by default + manif.nPPCost = nPowerLevel * 2 - 1; // Initialise the cost to the base cost of the power + manif.nPsiFocUsesRemain = GetPsionicFocusesAvailable(oManifester);// Determine how many times psionic focus could be used + manif.nManifesterLevel = nManifesterLevel; + manif.nSpellID = PRCGetSpellId(); + + // Run an ability score check to see if the manifester can manifest the power at all + if (bIsPsiLike) + { + manif.bCanManifest = TRUE; + } + else if(GetAbilityScoreOfClass(oManifester, nClass) - 10 < nPowerLevel && !bIgnoreConstraints && !bIsPsiLike && !GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && manif.nSpellID != POWER_ELAN_RESILIANCE) + { + FloatingTextStrRefOnCreature(16826411, oManifester, FALSE); // "You do not have a high enough ability score to manifest this power" + manif.bCanManifest = FALSE; + } + + // Account for metapsionics + if(!bIsPsiLike) // Skipped for psi-like abilities + manif = EvaluateMetapsionics(manif, nMetaPsiFlags); + + // This has an initial power cost of 0, all cost comes from augmentation + if (manif.nSpellID == POWER_ELAN_RESILIANCE) manif.nPPCost = 0; + + // Account for augmentation. This factors in Wild Surge cost reduction + manif = EvaluateAugmentation(manif, pap); + + //* APPLY COST INCREASES THAT DO NOT CAUSE ONE TO LOSE PP ON FAILURE HERE *// + if (manif.nSpellID != POWER_ELAN_RESILIANCE) + { + // Catapsi check + if(GetLocalInt(oManifester, "PRC_IsInCatapsi") && // Manifester is in Catapsi field + !PRCMySavingThrow(SAVING_THROW_WILL, oManifester, GetLocalInt(oManifester, "PRC_Catapsi_DC"), // And fails the will save VS it + SAVING_THROW_TYPE_MIND_SPELLS, GetLocalObject(oManifester, "PRC_Catapsi_Manifester") + ) + ) + { + manif.nPPCost += 4; + } + } + + //* APPLY COST INCREASES THAT DO NOT CAUSE ONE TO LOSE PP ON FAILURE ABOVE *// + + // Skip paying anything if something has prevented successfull manifestation already by this point + if(manif.bCanManifest) + { + /* The manifester level value includes the manifester level increase from + * Wild Surge, but since the calculated cost already contains the augmentation + * cost reduction provided by Wild Surge, it should not apply here. + */ + if((nManifesterLevel - nWildSurge) >= manif.nPPCost || bIsPsiLike || bIgnoreConstraints) + { + // Reduced cost of manifesting a power, but does not allow you to exceed the manifester level cap + if(!bIsPsiLike) // Skipped for psi-like abilities + manif = _GetPPCostReduced(manif); + + //If the manifester does not have enough points before hostile modifiers, cancel power + if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints) + { + FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power" + manif.bCanManifest = FALSE; + } + // The manifester has enough power points that they would be able to use the power, barring extra costs + else + { + //* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL HERE *// + // Psionic Hole does not count against manifester level cap, but causes the power to fail if the manifester can't pay + manif.nPPCost += nPsionicHoleCost; + // Volatile Mind behaves the same + manif.nPPCost += nVolatileMindCost; + //* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL ABOVE *// + + if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints) + { + FloatingTextStrRefOnCreature(16826413, oManifester, FALSE); // "Your target's abilities cause you to use more Power Points than you have. The power fails" + manif.bCanManifest = FALSE; + } + + // Psi-like abilities ignore PP costs and metapsi + if(!bIsPsiLike) + { + // Set the power points to their new value and inform the manifester + LosePowerPoints(oManifester, manif.nPPCost, TRUE); + + // Psionic focus loss from using metapsionics. Has a side effect of telling the manifester which metapsionics were actually active + PayMetapsionicsFocuses(manif); + } + + //* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL MANIFESTATION HERE *// + // Psicraft for all those who can see + IdentifyPower(oManifester, manif.nSpellID); + // Damage from overchanneling happens only if one actually spends PP + _DoOverchannelDamage(oManifester, bIsPsiLike); + // Apply Hostile Mind damage, as necessary + _HostileMind(oManifester, oTarget); + // Apply Wild Surge side-effects + _SurgingEuphoriaOrPsychicEnervation(oManifester, nWildSurge); + // Apply Mind Trap PP loss + _DoMindTrapPPLoss(oManifester, oTarget); + // Spellfire friendly absorption - This may set bCananifest to FALSE + manif = _DoSpellfireFriendlyAbsorption(manif, oTarget); + //* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL MANIFESTATION ABOVE *// + } + } + // Cost was over the manifester cap + else + {// "Your manifester level is not high enough to spend X Power Points" + FloatingTextStringOnCreature(GetStringByStrRef(16826410) + " " + IntToString(manif.nPPCost) + " " + GetStringByStrRef(16826409), oManifester, FALSE); + manif.bCanManifest = FALSE; + } + }//end if - Something hadn't prevented successfull manifestation already before paying the power costs + + if(DEBUG) DoDebug("EvaluateManifestation(): Final result:\n" + DebugManifestation2Str(manif)); + + // Initiate manifestation-related variable cleanup + DelayCommand(0.5f, _CleanManifestationVariables(oManifester)); + + return manif; +} + +void UsePower(int nPower, int nClass, int bIsPsiLike = FALSE, int nLevelOverride = 0) +{ + object oManifester = OBJECT_SELF; + object oSkin = GetPCSkin(oManifester); + object oTarget = PRCGetSpellTargetObject(); + object oMfToken; + location lTarget = PRCGetSpellTargetLocation(); + int nSpellID = PRCGetSpellId(); + int nManifDur = StringToInt(Get2DACache("spells", "ConjTime", nPower)) + StringToInt(Get2DACache("spells", "CastTime", nPower)); + int bQuicken = FALSE; + + + if (GetLocalInt(oManifester, "MindStabDur")) nManifDur = 0; + + // Normally swift action powers check + if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nPower, nClass)) == "SWIFT_ACTION") // The power is swift action to use + { + if (TakeSwiftAction(oManifester)) // And the Manifester can take a swift action now + nManifDur = 0; + else + return; + } + // Quicken Power check + else if(nManifDur <= 6000 && // If the power could be quickened by having manifesting time of 1 round or less + GetLocalInt(oManifester, METAPSIONIC_QUICKEN_VAR) && // And the manifester has Quicken Power active + GetIsPsionicallyFocused(oManifester) && // And might be able to pay the psionic focus for it + TakeSwiftAction(oManifester) // And the manifester can take a swift action + ) + { + // Set the manifestation time to 0 to skip VFX + nManifDur = 0; + // And set the Quicken Power used marker to TRUE + bQuicken = TRUE; + } + + if(DEBUG) DoDebug("UsePower(): Manifester is " + DebugObject2Str(oManifester) + "\n" + + "nPower = " + IntToString(nPower) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "bIsPsiLike = " + DebugBool2String(bIsPsiLike) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + + "Manifestation duration = " + IntToString(nManifDur) + "ms \n" + + "bQuicken = " + DebugBool2String(bQuicken) + "\n" + //+ "Token exists = " + DebugBool2String(GetIsObjectValid(oMfToken)) + ); + + // Create the manifestation token. Deletes any old tokens and cancels corresponding manifestations as a side effect + oMfToken = _CreateManifestationToken(oManifester); + + /// @todo Hook to the manifester's OnDamaged event for the concentration checks to avoid losing the power + + // Nuke action queue to prevent cheating with creative power stacking. + // Probably not necessary anymore - Ornedan + if(DEBUG) SendMessageToPC(oManifester, "Clearing all actions in preparation for second stage of the power."); + ClearAllActions(); + + // If out of range, move to range + _ManifestationRangeCheck(oManifester, nPower, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget); + + // Start the manifestation monitor HB + DelayCommand(nManifDur / 1000.0f, ActionDoCommand(_ManifestationHB(oManifester, GetLocation(oManifester), oMfToken))); + + // Assuming the spell isn't used as a swift action, fakecast for visuals + if(nManifDur > 0) + { + // Hack. Workaround of a bug with the fakecast actions. See function comment for details + ActionDoCommand(_AssignUsePowerFakeCastCommands(oManifester, oTarget, lTarget, nSpellID)); + } + + // Action queue the function that will cheatcast the actual power + DelayCommand(nManifDur / 1000.0f, AssignCommand(oManifester, ActionDoCommand(_UsePowerAux(oManifester, oMfToken, nSpellID, oTarget, lTarget, nPower, nClass, bIsPsiLike, nLevelOverride, bQuicken)))); +} + +string DebugManifestation2Str(struct manifestation manif) +{ + string sRet; + + sRet += "oManifester = " + DebugObject2Str(manif.oManifester) + "\n"; + sRet += "bCanManifest = " + DebugBool2String(manif.bCanManifest) + "\n"; + sRet += "nPPCost = " + IntToString(manif.nPPCost) + "\n"; + sRet += "nPsiFocUsesRemain = " + IntToString(manif.nPsiFocUsesRemain) + "\n"; + sRet += "nManifesterLevel = " + IntToString(manif.nManifesterLevel) + "\n"; + + sRet += "nTimesAugOptUsed_1 = " + IntToString(manif.nTimesAugOptUsed_1) + "\n"; + sRet += "nTimesAugOptUsed_2 = " + IntToString(manif.nTimesAugOptUsed_2) + "\n"; + sRet += "nTimesAugOptUsed_3 = " + IntToString(manif.nTimesAugOptUsed_3) + "\n"; + sRet += "nTimesAugOptUsed_4 = " + IntToString(manif.nTimesAugOptUsed_4) + "\n"; + sRet += "nTimesAugOptUsed_5 = " + IntToString(manif.nTimesAugOptUsed_5) + "\n"; + sRet += "nTimesGenericAugUsed = " + IntToString(manif.nTimesGenericAugUsed) + "\n"; + + sRet += "bChain = " + DebugBool2String(manif.bChain) + "\n"; + sRet += "bEmpower = " + DebugBool2String(manif.bEmpower) + "\n"; + sRet += "bExtend = " + DebugBool2String(manif.bExtend) + "\n"; + sRet += "bMaximize = " + DebugBool2String(manif.bMaximize) + "\n"; + sRet += "bSplit = " + DebugBool2String(manif.bSplit) + "\n"; + sRet += "bTwin = " + DebugBool2String(manif.bTwin) + "\n"; + sRet += "bWiden = " + DebugBool2String(manif.bWiden) + "\n"; + sRet += "bQuicken = " + DebugBool2String(manif.bQuicken);// + "\n"; + + return sRet; +} + +void SetLocalManifestation(object oObject, string sName, struct manifestation manif) +{ + //SetLocal (oObject, sName + "_", ); + SetLocalObject(oObject, sName + "_oManifester", manif.oManifester); + + SetLocalInt(oObject, sName + "_bCanManifest", manif.bCanManifest); + SetLocalInt(oObject, sName + "_nPPCost", manif.nPPCost); + SetLocalInt(oObject, sName + "_nPsiFocUsesRemain", manif.nPsiFocUsesRemain); + SetLocalInt(oObject, sName + "_nManifesterLevel", manif.nManifesterLevel); + SetLocalInt(oObject, sName + "_nSpellID", manif.nSpellID); + + SetLocalInt(oObject, sName + "_nTimesAugOptUsed_1", manif.nTimesAugOptUsed_1); + SetLocalInt(oObject, sName + "_nTimesAugOptUsed_2", manif.nTimesAugOptUsed_2); + SetLocalInt(oObject, sName + "_nTimesAugOptUsed_3", manif.nTimesAugOptUsed_3); + SetLocalInt(oObject, sName + "_nTimesAugOptUsed_4", manif.nTimesAugOptUsed_4); + SetLocalInt(oObject, sName + "_nTimesAugOptUsed_5", manif.nTimesAugOptUsed_5); + SetLocalInt(oObject, sName + "_nTimesGenericAugUsed", manif.nTimesGenericAugUsed); + + SetLocalInt(oObject, sName + "_bChain", manif.bChain); + SetLocalInt(oObject, sName + "_bEmpower", manif.bEmpower); + SetLocalInt(oObject, sName + "_bExtend", manif.bExtend); + SetLocalInt(oObject, sName + "_bMaximize", manif.bMaximize); + SetLocalInt(oObject, sName + "_bSplit", manif.bSplit); + SetLocalInt(oObject, sName + "_bTwin", manif.bTwin); + SetLocalInt(oObject, sName + "_bWiden", manif.bWiden); + SetLocalInt(oObject, sName + "_bQuicken", manif.bQuicken); +} + +struct manifestation GetLocalManifestation(object oObject, string sName) +{ + struct manifestation manif; + manif.oManifester = GetLocalObject(oObject, sName + "_oManifester"); + + manif.bCanManifest = GetLocalInt(oObject, sName + "_bCanManifest"); + manif.nPPCost = GetLocalInt(oObject, sName + "_nPPCost"); + manif.nPsiFocUsesRemain = GetLocalInt(oObject, sName + "_nPsiFocUsesRemain"); + manif.nManifesterLevel = GetLocalInt(oObject, sName + "_nManifesterLevel"); + manif.nSpellID = GetLocalInt(oObject, sName + "_nSpellID"); + + manif.nTimesAugOptUsed_1 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_1"); + manif.nTimesAugOptUsed_2 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_2"); + manif.nTimesAugOptUsed_3 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_3"); + manif.nTimesAugOptUsed_4 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_4"); + manif.nTimesAugOptUsed_5 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_5"); + manif.nTimesGenericAugUsed = GetLocalInt(oObject, sName + "_nTimesGenericAugUsed"); + + manif.bChain = GetLocalInt(oObject, sName + "_bChain"); + manif.bEmpower = GetLocalInt(oObject, sName + "_bEmpower"); + manif.bExtend = GetLocalInt(oObject, sName + "_bExtend"); + manif.bMaximize = GetLocalInt(oObject, sName + "_bMaximize"); + manif.bSplit = GetLocalInt(oObject, sName + "_bSplit"); + manif.bTwin = GetLocalInt(oObject, sName + "_bTwin"); + manif.bWiden = GetLocalInt(oObject, sName + "_bWiden"); + manif.bQuicken = GetLocalInt(oObject, sName + "_bQuicken"); + + return manif; +} + +void DebugIgnoreConstraints(object oManifester) +{ + SetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS, TRUE); + DelayCommand(0.0f, DeleteLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS)); +} + +//Below is a modification of Evaluate Manifestation for the Diamond Dragon abilities +struct manifestation EvaluateDiaDragChannel(object oManifester, object oTarget, struct power_augment_profile pap, int nPowerLevel) +{ + /* Get some data */ + int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // Manifester-related stuff + int nClass = GetPrimaryPsionicClass(oManifester); + int nWildSurge = GetWildSurge(oManifester); + //When Manifester level is retrieved, remove Wildsurge and Overchannel bonuses as they don't apply + int nManifesterLevel = GetManifesterLevel(oManifester, nClass); + int nManifesterPP = GetCurrentPowerPoints(oManifester); + + /* Initialise the manifestation structure */ + struct manifestation manif; + manif.oManifester = oManifester; + manif.bCanManifest = TRUE; // Assume successfull manifestation by default + manif.nPPCost = nPowerLevel * 2 - 1; // Initialise the cost to the base cost of the power + manif.nPsiFocUsesRemain = 0; // Not needed + manif.nManifesterLevel = nManifesterLevel; + manif.nSpellID = PRCGetSpellId(); + + // Run an ability score check to see if the manifester can manifest the power at all + if(GetAbilityScoreOfClass(oManifester, nClass) - 10 < nPowerLevel && (!bIgnoreConstraints)) + { + FloatingTextStrRefOnCreature(16826411, oManifester, FALSE); // "You do not have a high enough ability score to manifest this power" + manif.bCanManifest = FALSE; + } + + // Account for augmentation. This factors in Wild Surge cost reduction + manif = EvaluateAugmentation(manif, pap); + + // Skip paying anything if something has prevented successfull manifestation already by this point + if(manif.bCanManifest) + { + //Remove the Wild Surge cost reduction + manif.nPPCost += nWildSurge; + /* The manifester level value includes the manifester level increase from + * Wild Surge, but since the calculated cost already contains the augmentation + * cost reduction provided by Wild Surge, it should not apply here. + */ + if((nManifesterLevel) >= manif.nPPCost || bIgnoreConstraints) + { + + //If the manifester does not have enough points before hostile modifiers, cancel power + if(manif.nPPCost > nManifesterPP && !bIgnoreConstraints) + { + FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power" + manif.bCanManifest = FALSE; + } + // The manifester has enough power points that they would be able to use the power, barring extra costs + else + { + //* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL ABOVE *// + + if(manif.nPPCost > nManifesterPP && !bIgnoreConstraints) + { + FloatingTextStrRefOnCreature(16826413, oManifester, FALSE); // "Your target's abilities cause you to use more Power Points than you have. The power fails" + manif.bCanManifest = FALSE; + } + + // Set the power points to their new value and inform the manifester + LosePowerPoints(oManifester, manif.nPPCost, TRUE); + + } + } + // Cost was over the manifester cap + else + {// "Your manifester level is not high enough to spend X Power Points" + FloatingTextStringOnCreature(GetStringByStrRef(16826410) + " " + IntToString(manif.nPPCost) + " " + GetStringByStrRef(16826409), oManifester, FALSE); + manif.bCanManifest = FALSE; + } + }//end if - Something hadn't prevented successfull manifestation already before paying the power costs + + if(DEBUG) DoDebug("EvaluateManifestation(): Final result:\n" + DebugManifestation2Str(manif)); + + // Initiate manifestation-related variable cleanup + DelayCommand(0.5f, _CleanManifestationVariables(oManifester)); + + return manif; +} + +// Test main +//void main(){} diff --git a/src/include/psi_inc_pwresist.nss b/src/include/psi_inc_pwresist.nss new file mode 100644 index 0000000..e1c5a96 --- /dev/null +++ b/src/include/psi_inc_pwresist.nss @@ -0,0 +1,148 @@ +/* + ---------------- + prc_inc_psionics + ---------------- + + 20/10/04 by Stratovarius + + Calculates Power Resistance for Psionics and performs the checks. +*/ + +/* +#include "prc_inc_racial" +#include "prc_feat_const" +#include "prc_class_const" +*/ +#include "prc_alterations" + +// Constants that dictate ResistPower results +const int POWER_RESIST_FAIL = 1; +const int POWER_RESIST_PASS = 0; + + +// +// This function is a wrapper should someone wish to rewrite the Bioware +// version. This is where it should be done. +// +int +PRCResistPower(object oCaster, object oTarget) +{ + return ResistSpell(oCaster, oTarget); +} + +// +// This function is a wrapper should someone wish to rewrite the Bioware +// version. This is where it should be done. +// +int +PRCGetPowerResistance(object oTarget, object oCaster) +{ + int iPowerRes = GetSpellResistance(oTarget); + + //racial pack SR + int iRacialPowerRes = 0; + if(GetHasFeat(FEAT_SPELL27, oTarget)) + iRacialPowerRes += 27+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL25, oTarget)) + iRacialPowerRes += 25+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL18, oTarget)) + iRacialPowerRes += 18+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL16, oTarget)) + iRacialPowerRes += 16+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL15, oTarget)) + iRacialPowerRes += 15+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL14, oTarget)) + iRacialPowerRes += 14+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL13, oTarget)) + iRacialPowerRes += 13+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL11, oTarget)) + iRacialPowerRes += 11+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL10, oTarget)) + iRacialPowerRes += 10+GetHitDice(oTarget); + else if(GetHasFeat(FEAT_SPELL5, oTarget)) + iRacialPowerRes += 5+GetHitDice(oTarget); + if(iRacialPowerRes > iPowerRes) + iPowerRes = iRacialPowerRes; + + // Exalted Companion, can also be used for Celestial Template + if (GetLocalInt(oTarget, "CelestialTemplate")) + { + int nHD = GetHitDice(oTarget); + int nSR = nHD * 2; + if (nSR > 25) nSR = 25; + if (nSR > iPowerRes) iPowerRes = nSR; + } + + // Thought Shield, 13 + augment vs Mind-Affecting + if(GetLocalInt(oTarget, "PRC_Power_ThoughtShield_PR") && + Get2DACache("spells", "ImmunityType", PRCGetSpellId()) == "Mind_Affecting" + ) + { + // Only use the PR given by the power if it's higher than the previous + iPowerRes = PRCMax(iPowerRes, GetLocalInt(oTarget, "PRC_Power_ThoughtShield_PR")); + } + // Tower of Iron Will, 19 + augment vs Mind-Affecting + if(GetLocalInt(oTarget, "PRC_Power_TowerOfIronWill_PR") && + Get2DACache("spells", "ImmunityType", PRCGetSpellId()) == "Mind_Affecting" + ) + { + // Only use the PR given by the power if it's higher than the previous + iPowerRes = PRCMax(iPowerRes, GetLocalInt(oTarget, "PRC_Power_TowerOfIronWill_PR")); + } + + // Foe Hunter SR stacks with normal SR + // when a Power is cast by their hated enemy + if(GetHasFeat(FEAT_HATED_ENEMY_SR, oTarget) && GetLocalInt(oTarget, "HatedFoe") == MyPRCGetRacialType(oCaster) ) + { + iPowerRes += 15 + GetLevelByClass(CLASS_TYPE_FOE_HUNTER, oTarget); + } + + return iPowerRes; +} + +// +// If a Power is resisted, display the effect +// +void +PRCShowPowerResist(object oCaster, object oTarget, int nResist, float fDelay = 0.0) +{ + // If either caster/target is a PC send them a message + if (GetIsPC(oCaster)) + { + string message = nResist == POWER_RESIST_FAIL ? + "Target is affected by the power." : "Target resisted the power."; + SendMessageToPC(oCaster, message); + } + if (GetIsPC(oTarget)) + { + string message = nResist == POWER_RESIST_FAIL ? + "You are affected by the power." : "You resisted the power."; + SendMessageToPC(oTarget, message); + } +} + +// +// This function overrides the BioWare MyResistSpell. +// TODO: Change name to PRCMyResistPower. +// +int +PRCMyResistPower(object oCaster, object oTarget, int nEffCasterLvl=0, float fDelay = 0.0) +{ + int nResist = POWER_RESIST_FAIL; + int nCasterCheck = nEffCasterLvl + d20(1); + int nTargetPR = PRCGetPowerResistance(oTarget, oCaster); + + // A tie favors the caster. + if (nCasterCheck < nTargetPR) nResist = POWER_RESIST_PASS; + + //Spellfire - done this way because constants may change + if(CheckSpellfire(oCaster, oTarget)) nResist = POWER_RESIST_PASS; + + // Only show resistance if the target has any + if (nTargetPR > 0) PRCShowPowerResist(oCaster, oTarget, nResist, fDelay); + + return nResist; +} + +// Test main +//void main(){} diff --git a/src/include/psi_inc_soulkn.nss b/src/include/psi_inc_soulkn.nss new file mode 100644 index 0000000..8a45a1d --- /dev/null +++ b/src/include/psi_inc_soulkn.nss @@ -0,0 +1,255 @@ +//:://///////////////////////////////////////////// +//:: Soulknife includes +//:: psi_inc_soulkn +//:://///////////////////////////////////////////// +/** @file Soulknife includes + Constants and common functions used by + Soulknife scripts. + + @author Ornedan + @date Created - 06.04.2005 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "prc_alterations" +#include "prc_class_const" +#include "inc_utility" + + +////////////////////////////////////////////////// +/* Constant declarations */ +////////////////////////////////////////////////// + +const string MBLADE_SHAPE = "PRC_PSI_SK_MindbladeShape"; +const string FREEDRAW_USED = "PRC_PSI_SK_FreeDraw_Used"; +const string THROW_MBLD_USED = "PRC_PSI_SK_ThrowMindblade_Used"; +const string PSYCHIC_STRIKE_MAINH = "PRC_PSI_SK_PsychisStrike_MainHand"; +const string PSYCHIC_STRIKE_OFFH = "PRC_PSI_SK_PsychisStrike_OffHand"; +const string KTTS = "PRC_PSI_SK_KnifeToTheSoul"; +const string BLADEWIND = "PRC_PSI_SK_Bladewind_Active"; +const string MBLADE_HAND = "PRC_PSI_SK_MindbladeManifestationHand"; + +const int KTTS_TYPE_MASK = 3; // 2 LSB +const int KTTS_TYPE_OFF = 0; +const int KTTS_TYPE_INT = 1; +const int KTTS_TYPE_WIS = 2; +const int KTTS_TYPE_CHA = 3; + +const int MBLADE_SHAPE_SHORTSWORD = 0; +const int MBLADE_SHAPE_DUAL_SHORTSWORDS = 1; +const int MBLADE_SHAPE_LONGSWORD = 2; +const int MBLADE_SHAPE_BASTARDSWORD = 3; +const int MBLADE_SHAPE_RANGED = 4; // Actual shape is throwing axe + + +const string MBLADE_FLAGS = "PRC_PSI_SK_MindbladeFlags"; +const int MBLADE_FLAG_COUNT = 23; + +const int MBLADE_FLAG_LUCKY = 0x1; +const int MBLADE_FLAG_DEFENDING = 0x2; +const int MBLADE_FLAG_KEEN = 0x4; +const int MBLADE_FLAG_VICIOUS = 0x8; +const int MBLADE_FLAG_PSYCHOKINETIC = 0x10; +const int MBLADE_FLAG_MIGHTYCLEAVING = 0x20; +const int MBLADE_FLAG_COLLISION = 0x40; +const int MBLADE_FLAG_MINDCRUSHER = 0x80; +const int MBLADE_FLAG_PSYCHOKINETICBURST = 0x100; +const int MBLADE_FLAG_SUPPRESSION = 0x200; +const int MBLADE_FLAG_WOUNDING = 0x400; +const int MBLADE_FLAG_DISRUPTING = 0x800; +const int MBLADE_FLAG_SOULBREAKER = 0x1000; +const int MBLADE_FLAG_SHIELD_1 = 0x2000; +const int MBLADE_FLAG_SHIELD_2 = 0x4000; +const int MBLADE_FLAG_SHIELD_3 = 0x8000; +const int MBLADE_FLAG_SHIELD_4 = 0x10000; +const int MBLADE_FLAG_SHIELD_5 = 0x20000; +const int MBLADE_FLAG_SHIELD_6 = 0x40000; +const int MBLADE_FLAG_SHIELD_7 = 0x80000; +const int MBLADE_FLAG_SHIELD_8 = 0x100000; +const int MBLADE_FLAG_SHIELD_9 = 0x200000; +const int MBLADE_FLAG_SHIELD_10 = 0x400000; + + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +// Sums the enhancement costs of enhancements contained in the given flag set +// ========================================================================== +// nFlags a set of mindblade flags +// +// Returns the sum of the enhancements costs of the mindblade abilities +// set in nFlags. +int GetTotalEnhancementCost(int nFlags); + +// Gets the enhancement cost of the given mindblade ability +// ======================================================== +// nFlag one of the MBLADE_FLAG_* contants +int GetFlagCost(int nFlag); + +// Gets the maximum mindblade enhancement usable by the given creature +// =================================================================== +// oSK a creature to calculate the value of Soulknife class ability +// "Mind blade enhancement" for +int GetMaxEnhancementCost(object oSK); + +/** + * Checks the given object's tag to determine whether it is a mindblade + * or not. + * + * @param oWeapon Weapon to test + * @return TRUE if oWeapon is a mindblade, FALSE otherwise + */ +int GetIsMindblade(object oWeapon); + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +int GetTotalEnhancementCost(int nFlags) +{ + int nCost, i; + for(; i < MBLADE_FLAG_COUNT; i++) + nCost += GetFlagCost(nFlags & (1 << i)); + return nCost; +} + +int GetFlagCost(int nFlag) +{ + switch(nFlag) + { + case 0: return 0; + case MBLADE_FLAG_LUCKY: return 1; + case MBLADE_FLAG_DEFENDING: return 1; + case MBLADE_FLAG_KEEN: return 1; + case MBLADE_FLAG_VICIOUS: return 1; + case MBLADE_FLAG_PSYCHOKINETIC: return 1; + case MBLADE_FLAG_MIGHTYCLEAVING: return 2; + case MBLADE_FLAG_COLLISION: return 2; + case MBLADE_FLAG_MINDCRUSHER: return 2; + case MBLADE_FLAG_PSYCHOKINETICBURST: return 2; + case MBLADE_FLAG_SUPPRESSION: return 2; + case MBLADE_FLAG_WOUNDING: return 2; + case MBLADE_FLAG_DISRUPTING: return 3; + case MBLADE_FLAG_SOULBREAKER: return 4; + case MBLADE_FLAG_SHIELD_1: return 1; + case MBLADE_FLAG_SHIELD_2: return 2; + case MBLADE_FLAG_SHIELD_3: return 3; + case MBLADE_FLAG_SHIELD_4: return 4; + case MBLADE_FLAG_SHIELD_5: return 5; + case MBLADE_FLAG_SHIELD_6: return 6; + case MBLADE_FLAG_SHIELD_7: return 7; + case MBLADE_FLAG_SHIELD_8: return 8; + case MBLADE_FLAG_SHIELD_9: return 9; + case MBLADE_FLAG_SHIELD_10: return 10; + + default: + WriteTimestampedLogEntry("Unknown flag passed to GetFlagCost: " + IntToString(nFlag)); + } + + return 0; +} + +int GetMaxEnhancementCost(object oSK) +{ + int nEffMBldLevel = GetLevelByClass(CLASS_TYPE_SOULKNIFE, oSK); + + if(GetHasFeat(FEAT_SOULBLADE_WARRIOR, oSK)) nEffMBldLevel += 2; + return (nEffMBldLevel - 2) / 4; +} + +int GetIsMindblade(object oWeapon) +{ + return GetStringLeft(GetTag(oWeapon), 14) == "prc_sk_mblade_"; +} + + + + + + +/* +/\/ Template used to generate the startingconditionals for Mindblade Enhancement convo \/\ +//:://///////////////////////////////////////////// +//:: Soulknife: Conversation - Show ~~~Name~~~ +//:: psi_sk_conv_~~~Suffix~~~ +//:://///////////////////////////////////////////// +/* + Checks whether to show ~~~Name~~~ and whether + it is to be added or removed. +/ +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 06.04.2005 +//::////////////////////////////////////////////// + +#include "psi_inc_sk_const" + + +int StartingConditional() +{ + int nReturn; // Implicit init to FALSE + // Check if the flag is already present + if(GetLocalInt(GetPCSpeaker(), MBLADE_FLAGS + "_T") & ~~~Flag~~~) + { + SetCustomToken(~~~TokenNum~~~, GetStringByStrRef(7654)); // Remove + nReturn = TRUE; + } + // It isn't, so see if there is enough bonus left to add it + else if(GetTotalEnhancementCost(GetLocalInt(GetPCSpeaker(), MBLADE_FLAGS + "_T")) + GetFlagCost(~~~Flag~~~) <= GetMaxEnhancementCost(GetPCSpeaker())) + { + SetCustomToken(~~~TokenNum~~~, GetStringByStrRef(62476)); // Add + nReturn = TRUE; + } + + return nReturn; +} + + +/\/ Template used to generate the toggles for Mindblade Enhancement convo \/\ +//:://///////////////////////////////////////////// +//:: Soulknife: Conversation - Toggle ~~~Name~~~ +//:: psi_sk_conv_~~~Suffix~~~ +//:://///////////////////////////////////////////// +/* + Adds or removes ~~~Name~~~ from the mindblade + flags. +/ +//::////////////////////////////////////////////// +//:: Created By: Ornedan +//:: Created On: 06.04.2005 +//::////////////////////////////////////////////// + +#include "psi_inc_sk_const" + + +void main() +{ + SetLocalInt(GetPCSpeaker(), MBLADE_FLAGS + "_T", + GetLocalInt(GetPCSpeaker(), MBLADE_FLAGS + "_T") ^ ~~~Flag~~~ + ); +} + + + +/\/ 2da files used for both \/\ +2DA V2.0 + + Suffix Name TokenNum Flag +0 lu_s Lucky 102 MBLADE_FLAG_LUCKY +1 de_s Defending 103 MBLADE_FLAG_DEFENDING +2 ke_s Keen 104 MBLADE_FLAG_KEEN +3 vi_s Vicous 105 MBLADE_FLAG_VICIOUS +4 ps_s Psychokinetic 106 MBLADE_FLAG_PSYCHOKINETIC +5 mc_s "Mighty Cleaving" 107 MBLADE_FLAG_MIGHTYCLEAVING +6 co_s Collision 108 MBLADE_FLAG_COLLISION +7 mi_s Mindcrusher 109 MBLADE_FLAG_MINDCRUSHER +8 pb_s "Psychokinetic Burst" 110 MBLADE_FLAG_PSYCHOKINETICBURST +9 su_s Suppression 111 MBLADE_FLAG_SUPPRESSION +10 wo_s Wounding 112 MBLADE_FLAG_WOUNDING +11 di_s Disrupting 113 MBLADE_FLAG_DISRUPTING +12 so_s Soulbreaker 114 MBLADE_FLAG_SOULBREAKER +*/ diff --git a/src/include/psi_power_const.nss b/src/include/psi_power_const.nss new file mode 100644 index 0000000..4d599c9 --- /dev/null +++ b/src/include/psi_power_const.nss @@ -0,0 +1,296 @@ +//real power spell constants + +// Level 1 Powers +const int POWER_BOLT = 14001; +const int POWER_CALLTOMIND = 14002; +const int POWER_CHARMPERSON = 14003; +const int POWER_CRYSTALSHARD = 14004; +const int POWER_DAZE = 14005; +const int POWER_DECELERATION = 14006; +const int POWER_DEFPRECOG = 14007; +const int POWER_DEMORALIZE = 14008; +const int POWER_DISABLE = 14009; +const int POWER_DISSIPATINGTOUCH = 14010; +const int POWER_DISTRACT = 14011; +const int POWER_EMPTYMIND = 14012; +const int POWER_CONCEALTHOUGHT = 14013; +const int POWER_ENERGYRAY_COLD = 14014; +const int POWER_ENERGYRAY_ELEC = 14015; +const int POWER_ENERGYRAY_FIRE = 14016; +const int POWER_ENERGYRAY_SONIC = 14017; +const int POWER_ENTANGLE = 14018; +const int POWER_FORCESCREEN = 14019; +const int POWER_GREASE = 14020; +const int POWER_GRIP_IRON = 14265; +const int POWER_HAMMER = 14021; +const int POWER_INERTIALARMOUR = 14022; +const int POWER_MINDTHRUST = 14023; +const int POWER_MYLIGHT = 14024; +const int POWER_OFFPRECOG = 14025; +const int POWER_OFFPRESC = 14026; +const int POWER_STOMP = 14027; +const int POWER_SYNESTHETE = 14028; +const int POWER_THICKSKIN = 14029; +const int POWER_VIGOR = 14030; +const int POWER_EMPATHY = 14031; +const int POWER_FARHAND = 14032; +const int POWER_MATTERAGITATION = 14033; +const int POWER_SKATE = 14034; +const int POWER_TELEMPATHICPRO = 14035; +const int POWER_ASTRALCONSTRUCT_SLOT1 = 14036; +const int POWER_ASTRALCONSTRUCT_SLOT2 = 14037; +const int POWER_ASTRALCONSTRUCT_SLOT3 = 14038; +const int POWER_ASTRALCONSTRUCT_SLOT4 = 14039; +const int POWER_BURST = 14041; +const int POWER_DESTINYDISSONANCE = 14042; +const int POWER_PRECOGNITION_MAIN = 14043; +const int POWER_PRECOGNITION_ATTACK = 14044; +const int POWER_PRECOGNITION_DAMAGE = 14045; +const int POWER_PRECOGNITION_SAVES = 14046; +const int POWER_PRECOGNITION_SKILLS = 14047; +const int POWER_BITE_WOLF = 14048; +const int POWER_CLAWS_BEAST = 14049; +const int POWER_CREATESOUND = 14050; +const int POWER_CALL_WEAPONRY = 14257; +const int POWER_COMPRESSION = 14258; +const int POWER_EXPANSION = 14259; +const int POWER_CONTROL_OBJECT = 14260; +const int POWER_METAPHYSICAL_CLAW = 14261; +const int POWER_METAPHYSICAL_WEAPON = 14262; +const int POWER_PREVENOM = 14263; +const int POWER_PREVENOM_WEAPON = 14264; + +// Level 2 Powers +const int POWER_BESTOWPOWER = 14051; +const int POWER_BIOFEEDBACK = 14052; +const int POWER_BRAINLOCK = 14053; +const int POWER_CONCBLAST = 14054; +const int POWER_CONCEALAMORPHA = 14055; +const int POWER_CRYSTALSWARM = 14056; +const int POWER_DISSOLVINGTOUCH = 14057; +const int POWER_EGOWHIP = 14058; +const int POWER_ELFSIGHT = 14059; +const int POWER_ENERGYADAPTACID = 14060; +const int POWER_ENERGYADAPTCOLD = 14061; +const int POWER_ENERGYADAPTELEC = 14062; +const int POWER_ENERGYADAPTFIRE = 14063; +const int POWER_ENERGYADAPTSONIC = 14064; +const int POWER_BODY_EQUILIBRIUM = 14065; +const int POWER_PAINFUL_STRIKE = 14066; +const int POWER_ENERGYPUSH_COLD = 14067; +const int POWER_ENERGYPUSH_ELEC = 14068; +const int POWER_ENERGYPUSH_FIRE = 14069; +const int POWER_ENERGYPUSH_SONIC = 14070; +const int POWER_LOCK = 14071; +const int POWER_ENERGYSTUN_COLD = 14072; +const int POWER_ENERGYSTUN_ELEC = 14073; +const int POWER_ENERGYSTUN_FIRE = 14074; +const int POWER_ENERGYSTUN_SONIC = 14075; +const int POWER_IDENTIFY = 14076; +const int POWER_IDINSINUATION = 14077; +const int POWER_INFLICTPAIN = 14078; +const int POWER_KNOCK = 14079; +const int POWER_MINDDISRUPT = 14080; +const int POWER_RECALLAGONY = 14081; +const int POWER_THOUGHTSHIELD = 14082; +const int POWER_DISSOLVEWEAP = 14083; +const int POWER_SHAREPAIN = 14084; +const int POWER_CONTROLAIR = 14085; +const int POWER_CONTROLSOUND = 14086; +const int POWER_REPAIRDAMAGE = 14087; +const int POWER_AVERSION = 14088; +const int POWER_DIMENSIONSWAP = 14089; +const int POWER_EMPATHICTRANSFER = 14090; +const int POWER_ENERGYMISSILE_COLD = 14091; +const int POWER_ENERGYMISSILE_ELEC = 14092; +const int POWER_ENERGYMISSILE_FIRE = 14093; +const int POWER_ENERGYMISSILE_SONIC = 14094; +const int POWER_ANIMAL_AFFINITY = 14095; +const int POWER_STRENGTH_OF_MY_ENEMY = 14096; +const int POWER_CHAMELEON = 14097; +const int POWER_CLAIRVOYANT_SENSE = 14098; +const int POWER_CLOUD_MIND = 14099; + +// Level 3 Powers +const int POWER_BODYADJUST = 14100; +const int POWER_GREATAMORPHA = 14101; +const int POWER_DARKVISION = 14102; +const int POWER_PSIBLAST = 14103; +const int POWER_DANGERSENSE = 14104; +const int POWER_ENERGYBOLT_COLD = 14105; +const int POWER_ENERGYBOLT_ELEC = 14106; +const int POWER_ENERGYBOLT_FIRE = 14107; +const int POWER_ENERGYBOLT_SONIC = 14108; +const int POWER_ENERGYBURST_COLD = 14109; +const int POWER_ENERGYBURST_ELEC = 14110; +const int POWER_ENERGYBURST_FIRE = 14111; +const int POWER_ENERGYBURST_SONIC = 14112; +const int POWER_ENERGYRETORT_COLD = 14113; +const int POWER_ENERGYRETORT_ELEC = 14114; +const int POWER_ENERGYRETORT_FIRE = 14115; +const int POWER_ENERGYRETORT_SONIC = 14116; +const int POWER_EXHALEBLACKDRAG = 14117; +const int POWER_ERADICATEINVIS = 14118; +const int POWER_SHAREPAINFORCED = 14119; +const int POWER_KEENEDGE = 14120; +const int POWER_MENTALBARRIER = 14121; +const int POWER_MINDTRAP = 14122; +const int POWER_TOUCHSIGHT = 14123; +const int POWER_BODYPURIFICATION = 14124; +const int POWER_DISPELPSIONICS = 14125; +const int POWER_ENERGYWALL_COLD = 14126; +const int POWER_ENERGYWALL_ELEC = 14127; +const int POWER_ENERGYWALL_FIRE = 14128; +const int POWER_ENERGYWALL_SONIC = 14129; +const int POWER_HUSTLE = 14130; +const int POWER_TIMEHOP = 14131; +const int POWER_UBIQVISION = 14132; +const int POWER_ECTOCOCOON = 14133; +const int POWER_CRISISBREATH = 14134; +const int POWER_EMPATHICTRANSFERHOSTILE = 14135; +const int POWER_FATELINK = 14136; +const int POWER_DIMENSION_SLIDE = 14137; +const int POWER_ECTOPLASMICFORM = 14138; +const int POWER_ENERGYCONE_COLD = 14139; +const int POWER_ENERGYCONE_ELEC = 14140; +const int POWER_ENERGYCONE_FIRE = 14141; +const int POWER_ENERGYCONE_SONIC = 14142; +const int POWER_CLAW_OF_THE_VAMPIRE = 14143; +const int POWER_DUODIMENSIONAL_CLAW = 14144; +const int POWER_VAMPIRIC_WEAPON = 14145; +const int POWER_ESCAPE_DETECTION = 14146; + +// Level 4 Powers +const int POWER_INERTBARRIER = 14147; +const int POWER_STEADFASTPERCEP = 14148; +const int POWER_DETECT_REMOTE_VIEWING = 14149; +const int POWER_EMPATHICFEEDBACK = 14150; +const int POWER_ENERGYADAPTION = 14151; +const int POWER_FREEDOM = 14152; +const int POWER_MINDWIPE = 14153; +const int POWER_POWERLEECH = 14154; +const int POWER_PSYCHICREFORMATION = 14155; +const int POWER_TELEKINETICMANEUVER = 14156; +const int POWER_DIMENSIONALANCHOR = 14157; +const int POWER_DISMISSAL = 14158; +const int POWER_DIMENSIONDOOR_SELFONLY = 14159; +const int POWER_DIMENSIONDOOR_PARTY = 14160; +const int POWER_DOMINATE = 14161; +const int POWER_DIMENSIONDOOR_SELFONLY_DIRDIST = 14163; +const int POWER_DIMENSIONDOOR_PARTY_DIRDIST = 14164; +const int POWER_ENERGYBALL_COLD = 14165; +const int POWER_ENERGYBALL_ELEC = 14166; +const int POWER_ENERGYBALL_FIRE = 14167; +const int POWER_ENERGYBALL_SONIC = 14168; +const int POWER_PSYCHICVAMPIRE = 14169; +const int POWER_CLAW_ENERGY_COLD = 14170; +const int POWER_CLAW_ENERGY_ELEC = 14171; +const int POWER_CLAW_ENERGY_FIRE = 14172; +const int POWER_IMMOVABILITY = 14173; +const int POWER_TRUEVENOM = 14174; +const int POWER_TRUEVENOM_WEAPON = 14175; +const int POWER_WEAPON_ENERGY_COLD = 14176; +const int POWER_WEAPON_ENERGY_ELEC = 14177; +const int POWER_WEAPON_ENERGY_FIRE = 14178; +const int POWER_INTELLECTFORTRESS = 14179; +const int POWER_REMOTE_VIEWING = 14180; + +// Level 5 Powers +const int POWER_BALEFULTEL = 14181; +const int POWER_ECTOSHAMBLER = 14182; +const int POWER_POWERRESISTANCE = 14183; +const int POWER_PSYCHICCRUSH = 14184; +const int POWER_TOWERIRONWILL = 14185; +const int POWER_TRUESEEING = 14186; +const int POWER_CATAPSI = 14187; +const int POWER_SHATTERMINDBLANK = 14188; +const int POWER_HAILCRYSTALS = 14189; +const int POWER_SECONDCHANCE = 14190; +const int POWER_TELEPORT_SELFONLY = 14191; +const int POWER_TELEPORT_PARTY = 14192; +const int POWER_ENERGYCURRENT_COLD = 14193; +const int POWER_ENERGYCURRENT_ELEC = 14194; +const int POWER_ENERGYCURRENT_FIRE = 14195; +const int POWER_ENERGYCURRENT_SONIC = 14196; +const int POWER_PSIONICREVIVIFY = 14197; +const int POWER_PSYCHOFEEDBACK = 14198; +const int POWER_CLAIRTANGENT_HAND = 14199; + +// Level 6 Powers +const int POWER_BREATHBLACKDRAGON = 14200; +const int POWER_DISINTEGRATE = 14201; +const int POWER_CRYSTALLIZE = 14202; +const int POWER_FUSEFLESH = 14203; +const int POWER_RETRIEVE = 14204; +const int POWER_TEMPORALACCELERATION = 14205; +const int POWER_BANISHMENT = 14206; +const int POWER_GREATERPRECOGNITION_MAIN = 14207; +const int POWER_GREATERPRECOGNITION_ATTACK = 14208; +const int POWER_GREATERPRECOGNITION_DAMAGE = 14209; +const int POWER_GREATERPRECOGNITION_SAVES = 14210; +const int POWER_GREATERPRECOGNITION_SKILLS = 14211; +const int POWER_PSIONICRESTORATION = 14212; +const int POWER_DISPELLING_BUFFER = 14213; +const int POWER_FORM_OF_DOOM = 14214; +const int POWER_NULL_PSIONICS_FIELD = 14215; +const int POWER_REMOTE_VIEW_TRAP = 14216; +const int POWER_CLOUD_MIND_MASS = 14217; + +// Level 7 Powers +const int POWER_CRISISLIFE = 14218; +const int POWER_DECEREBRATE = 14219; +const int POWER_ENERGYWAVE_COLD = 14220; +const int POWER_ENERGYWAVE_ELEC = 14221; +const int POWER_ENERGYWAVE_FIRE = 14222; +const int POWER_ENERGYWAVE_SONIC = 14223; +const int POWER_INSANITY = 14224; +const int POWER_MINDBLANKPERSONAL = 14225; +const int POWER_OAKBODY = 14226; +const int POWER_ULTRABLAST = 14227; +const int POWER_EVADEBURST = 14228; +const int POWER_MOMENTOFPRESCIENCEATTACK = 14229; +const int POWER_MOMENTOFPRESCIENCEARMOUR = 14230; +const int POWER_MOMENTOFPRESCIENCESAVES = 14231; +const int POWER_MOMENTOFPRESCIENCESKILLS = 14232; +const int POWER_ECTOCOCOONMASS = 14233; +const int POWER_ETHEREALJAUNT = 14234; +const int POWER_REDDOPSI = 14235; +const int POWER_SEQUESTER = 14236; + +// Level 8 Powers +const int POWER_RECALLDEATH = 14237; +const int POWER_IRONBODY = 14238; +const int POWER_PSIMINDBLANK = 14239; +const int POWER_TRUEMETABOLISM = 14240; +const int POWER_SHADOWBODY = 14241; +const int POWER_ASTRALSEED = 14242; +const int POWER_TIMEHOPMASS = 14243; +const int POWER_HYPERCOGNITION = 14244; +const int POWER_GREATER_TELEPORT_SELFONLY = 14245; +const int POWER_GREATER_TELEPORT_PARTY = 14246; + +// Level 9 Powers +const int POWER_ASSIMILATE = 14247; +const int POWER_ETHEREALNESS = 14248; +const int POWER_MICROCOSM = 14249; +const int POWER_TIMELESSBODY = 14250; +const int POWER_GENESIS = 14251; +const int POWER_PSYCHICCHIR_REPAIR = 14252; +const int POWER_TELEPORTATIONCIRCLE_VISIBLE = 14253; +const int POWER_TELEPORTATIONCIRCLE_HIDDEN = 14254; +const int POWER_PSYCHICCHIR_TRANSFER = 14255; +const int POWER_TORNADO_BLAST = 14256; + +//Diamond Dragon Powers +const int POWER_DIADRAG_CLAWS = 2459; +const int POWER_DIADRAG_WINGS = 2460; +const int POWER_DIADRAG_TAIL = 2462; +const int POWER_DIADRAG_DRAGONFEAR = 2463; +const int POWER_DIADRAG_BREATH_COLD = 2464; +const int POWER_DIADRAG_BREATH_ELEC = 2465; +const int POWER_DIADRAG_BREATH_FIRE = 2466; +const int POWER_DIADRAG_BREATH_SONIC = 2467; +const int POWER_DIADRAG_IMMUNITY = 2468; + +// Elan racial constant +const int POWER_ELAN_RESILIANCE = 1992; \ No newline at end of file diff --git a/src/include/psi_spellhook.nss b/src/include/psi_spellhook.nss new file mode 100644 index 0000000..12c651d --- /dev/null +++ b/src/include/psi_spellhook.nss @@ -0,0 +1,211 @@ +//:://///////////////////////////////////////////// +//:: Spell Hook Include File +//:: prc_psi_splhook +//::////////////////////////////////////////////// +/* + + This file acts as a hub for all code that + is hooked into the psionic spellscripts + +*/ +//::////////////////////////////////////////////// +//:: Created By: Stratovarius +//:: Created On: 20-10-2004 +//::////////////////////////////////////////////// + +//#include "prc_x2_craft" +#include "x2_inc_spellhook" +#include "prc_inc_spells" +#include "inc_utility" +#include "prc_inc_itmrstr" +#include "psi_inc_psifunc" + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int PsiPrePowerCastCode(); + +//This function handles the "free swipe when manifesting" ability of the Diamond Dragon +void Dragonswipe() +{ + object oPC = OBJECT_SELF; + // If claws are not activated, exit + if(!GetLocalInt(oPC, "DiamondClawsOn")) return; + object oTarget = PRCGetSpellTargetObject(); + + // Get the item used to cast the spell + object oItem = GetSpellCastItem(); + + // Clawswipes only work on powers manifested by the Diamond Dragon, not by items he uses. + if (oItem != OBJECT_INVALID) + { + FloatingTextStringOnCreature("You do not gain clawswipes from Items.", OBJECT_SELF, FALSE); + return; + } + + effect eInvalid; + + if(TakeSwiftAction(oPC)) + { + //grab the closest enemy to swipe at + oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, oPC, 1, + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + if (oTarget != oPC && GetDistanceToObject(oTarget) < FeetToMeters(15.0)) + { + object oClaw = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC); + PerformAttack(oTarget, oPC, eInvalid, 0.0, 0, 0, DAMAGE_TYPE_SLASHING, "*Clawswipe Hit*", "*Clawswipe Missed*", FALSE, oClaw); + } + } + +} + +//------------------------------------------------------------------------------ +// if FALSE is returned by this function, the spell will not be cast +// the order in which the functions are called here DOES MATTER, changing it +// WILL break the crafting subsystems +//------------------------------------------------------------------------------ +int PsiPrePowerCastCode() +{ + object oManifester = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + int nPowerID = PRCGetSpellId(); + int nPowerLevel = GetPowerLevel(oManifester); + int nManifestingClass = GetManifestingClass(oManifester); + int bPowerIsHostile = Get2DACache("spells", "HostileSetting", nPowerID) == "1"; + + int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oManifester); + + //--------------------------------------------------------------------------- + // Break any spell require maintaining concentration + //--------------------------------------------------------------------------- + X2BreakConcentrationSpells(); + + // Ectoplasmic Form conc check + if (GetLocalInt(oTarget, "PRC_Power_EctoForm")) + { + nContinue = GetIsSkillSuccessful(oManifester, SKILL_CONCENTRATION, (20 + nPowerLevel)); + } + + //--------------------------------------------------------------------------- + // Run Disrupting Strike Check + //--------------------------------------------------------------------------- + if (nContinue && GetLocalInt(oManifester, "DisruptingStrike_PsionicsFail")) + { + nContinue = FALSE; + } + + //--------------------------------------------------------------------------- + // Check for PRC spell effects + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PRCSpellEffects(oManifester, oTarget, nPowerID, nPowerLevel, nManifestingClass, bPowerIsHostile, -1); + + //--------------------------------------------------------------------------- + // Run Grappling Concentration Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = GrappleConc(oManifester, nPowerLevel); + + //--------------------------------------------------------------------------- + // This stuff is only interesting for player characters we assume that use + // magic device always works and NPCs don't use the crafting feats or + // sequencers anyway. Thus, any NON PC spellcaster always exits this script + // with TRUE (unless they are DM possessed or in the Wild Magic Area in + // Chapter 2 of Hordes of the Underdark. + //--------------------------------------------------------------------------- + if(!GetIsPC(oManifester) + && !GetPRCSwitch(PRC_NPC_HAS_PC_SPELLCASTING)) + { + if(!GetIsDMPossessed(oManifester) && !GetLocalInt(GetArea(oManifester), "X2_L_WILD_MAGIC")) + { + return TRUE; + } + } + + if (nContinue) + { + //--------------------------------------------------------------------------- + // Run use magic device skill check + //--------------------------------------------------------------------------- + nContinue = X2UseMagicDeviceCheck(oManifester); + } + + if (nContinue) + { + //----------------------------------------------------------------------- + // run any user defined spellscript here + //----------------------------------------------------------------------- + nContinue = X2RunUserDefinedSpellScript(); + } + + //--------------------------------------------------------------------------- + // Check for the new restricted itemproperties + //--------------------------------------------------------------------------- + if(nContinue + && GetIsObjectValid(GetSpellCastItem()) + && !CheckPRCLimitations(GetSpellCastItem(), oManifester)) + { + SendMessageToPC(oManifester, "You cannot use "+GetName(GetSpellCastItem())); + nContinue = FALSE; + } + + //perform the clawswipe + Dragonswipe(); + + //--------------------------------------------------------------------------- + // The following code is only of interest if an item was targeted + //--------------------------------------------------------------------------- + if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + + //----------------------------------------------------------------------- + // Check if spell was used to trigger item creation feat + //----------------------------------------------------------------------- + if (nContinue) { + nContinue = !ExecuteScriptAndReturnInt("x2_pc_craft", oManifester); + } + + //----------------------------------------------------------------------- + // * Execute item OnSpellCast At routing script if activated + //----------------------------------------------------------------------- + SetUserDefinedItemEventNumber(X2_ITEM_EVENT_SPELLCAST_AT); + //Tag-based PRC scripts first + int nRet = ExecuteScriptAndReturnInt("is_"+GetTag(oTarget), oManifester); + if(nRet == X2_EXECUTE_SCRIPT_END) + return FALSE; + + if(GetModuleSwitchValue(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS) == TRUE) + { + nRet = ExecuteScriptAndReturnInt(GetUserDefinedItemEventScriptName(oTarget), oManifester); + if(nRet == X2_EXECUTE_SCRIPT_END) + return FALSE; + } + + //----------------------------------------------------------------------- + // Prevent any spell that has no special coding to handle targetting of items + // from being cast on items. We do this because we can not predict how + // all the hundreds spells in NWN will react when cast on items + //----------------------------------------------------------------------- + if (nContinue) { + nContinue = X2CastOnItemWasAllowed(oTarget); + } + } + + //Cleaning spell variables used for holding the charge + if(!GetLocalInt(oManifester, "PRC_SPELL_EVENT")) + { + DeleteLocalInt(oManifester, "PRC_SPELL_CHARGE_COUNT"); + DeleteLocalInt(oManifester, "PRC_SPELL_CHARGE_SPELLID"); + DeleteLocalObject(oManifester, "PRC_SPELL_CONC_TARGET"); + DeleteLocalInt(oManifester, "PRC_SPELL_METAMAGIC"); + DeleteLocalManifestation(oManifester, "PRC_POWER_HOLD_MANIFESTATION"); + DeleteLocalMystery(oManifester, "MYST_HOLD_MYST"); + } + else if(GetLocalInt(oManifester, "PRC_SPELL_CHARGE_SPELLID") != PRCGetSpellId()) + { //Sanity check, in case something goes wrong with the action queue + DeleteLocalInt(oManifester, "PRC_SPELL_EVENT"); + } + + return nContinue; +} + diff --git a/src/include/sbr_include.nss b/src/include/sbr_include.nss new file mode 100644 index 0000000..be8e2dd --- /dev/null +++ b/src/include/sbr_include.nss @@ -0,0 +1,349 @@ +//:://///////////////////////////////////////////// +//:: Name Demetrious' Supply Based Rest +//:: FileName SBR_include +//::////////////////////////////////////////////// +// http://nwvault.ign.com/Files/scripts/data/1055903555000.shtml + +/* +Always recompile your module after modifying 'sbr_include' because it is an include file. +#1: Select "Build" from the toolset menu and then choose "Build Module". +#2: Click the "Advanced Controls" box to bring up the advanced options. +#3: Make sure that the only boxes that are selected are "Compile" and "Scripts". +#4: Click the "Build" button. +#5: Remember to always make sure you are using the options you want to use when running "Build Module"! +*/ + +#include "inc_logmessage" +#include "nw_i0_plot" + +// **************************************************************************** +// ** CONFIGURATION +// **************************************************************************** + +// The variable below sets up how the DM Rest widget is configured. +// Change this to true to only toggle rest module wide rather +// than using the 3 different level options. +// If this is TRUE clicking the ground, yourself or a player will +// toggle the Module level rest restiction. +// If FALSE, then you have 3 options. +// Target yourself = Module level toggle. +// Target ground (ie the area) = Area level toggle. +// Target player = Party level toggle. +// +// In either mode, targetting an NPC or other placeable will report +// all pertinent rest system information to you. +const int SBR_MAKE_IT_SIMPLE = FALSE; + +// This is the maximum distance the player can be from a "restful object" to +// automatically use it when they hit rest. +const float SBR_DISTANCE = 5.0; + +// This is the event number that will be signalled to your module OnUserDefined Event +// when a player rests. This user defined event is how you should extend the system +// so that specific things happen on rest. IE: create some wandering monsters, +// play a cutscene, teleport them to a "dream" area, etc. +const int SBR_EVENT_REST = 2000; + +// **************************************************************************** +// ** CONSTANTS - TAGS +// **************************************************************************** + +// These tags correspond to items that come with this package. + +const string SBR_KIT_WOODLAND = "woodlandkit"; +const string SBR_KIT_REGULAR = "supplykit"; +const string SBR_DM_WIDGET = "DMRestWidget"; + +// **************************************************************************** +// ** CONSTANTS - LOCAL VARIABLE NAMES +// **************************************************************************** + +const string SBR_RESTING = "SBR_SomeoneResting"; +const string SBR_SUPPLIES = "SBR_Supplies"; +const string SBR_USED_KIT = "SBR_UsedKit"; +const string SBR_REST_NOT_ALLOWED = "SBR_RestIsNotAllowed"; + +/* +Possible log levels for LogMessage 1.06 +// Do not send a message. +const int LOG_DISABLED = 0x0; +// Send only to the oPC who activated it (floating text) +const int LOG_PC = 0x1; +// Send only to the oPC who activated it (server message window) +const int LOG_PC_SERVER = 0x2; +// Send to all players on the server (server message window) +const int LOG_PC_ALL = 0x4; +// Send to the oPC and all of their party members (floating text) +const int LOG_PARTY = 0x8; +// Send to the oPC and all of their party members (server message window) +const int LOG_PARTY_SERVER = 0x10; +// Send to the oPC and their nearby (30m) party members (floating text) +const int LOG_PARTY_30 = 0x20; +// Send to the DM channel (DM channel) +const int LOG_DM_ALL = 0x40; +// Send to all DMs within distance 10m of oPC (floating text) +const int LOG_DM_10 = 0x80; +// Send to all DMs within distance 20m of oPC (floating text) +const int LOG_DM_20 = 0x100; +// Send to all DMs within distance 40m of oPC (floating text) +const int LOG_DM_40 = 0x200; +// Send to all DMs within distance 80m of oPC (floating text) +const int LOG_DM_80 = 0x400; +// Make oPC whisper the message (chat message window) +const int LOG_WHISPER = 0x800; +// Make oPC talk the message (chat message window) +const int LOG_TALK = 0x1000; +// Make oPC shout the message (chat message window) +const int LOG_SHOUT = 0x2000; +// Send to the server log file +const int LOG_TO_SERVER_LOG = 0x4000; +// Send to the server log file with time stamp +const int LOG_TIME_SERVER_LOG = 0x8000; +// Send to all DMs within distance 10m of oPC (server message window) +const int LOG_DM_10_SERVER = 0x10000; +// Send to all DMs within distance 20m of oPC (server message window) +const int LOG_DM_20_SERVER = 0x20000; +// Send to all DMs within distance 40m of oPC (server message window) +const int LOG_DM_40_SERVER = 0x40000; +// Send to all DMs within distance 80m of oPC (server message window) +const int LOG_DM_80_SERVER = 0x80000; +// Send to the oPC and all of their party members who percieve oPC (floating text) +const int LOG_PARTY_PERC = 0x100000; +// Send to the oPC and all of their party members who percieve oPC (server message window) +const int LOG_PARTY_PERC_SERVER = 0x200000; +// Send to the oPC and their nearby (10m) party members (floating text) +const int LOG_PARTY_10 = 0x400000; +// Send to the oPC and their nearby (20m) party members (floating text) +const int LOG_PARTY_20 = 0x800000; +// Send to the oPC and their nearby (40m) party members (floating text) +const int LOG_PARTY_40 = 0x1000000; +// Send to the oPC and their nearby (80m) party members (floating text) +const int LOG_PARTY_80 = 0x2000000; +// Send to all DMs as a server message +const int LOG_DM_ALL_SERVER = 0x4000000; +// Send to all party EXCEPT for the player who triggered as a server message +const int LOG_PARTY_ONLY = 0x8000000; +// Send to all party EXCEPT for the player *and people who can't see the player) who triggered as a server message +const int LOG_PARTY_PERC_ONLY = 0x10000000; +*/ + +//:://///////////////////////////////////////////// +//:: Report Rest Statistics to the DM +//:: uhhh.... no copyright - Demetrious +//::////////////////////////////////////////////// +void ReportStats(object oPC) +{ +object oPlayer = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC, oPC); +int iLogLevel = LOG_PC; // Log level used with LogMessage + +//report some general information to the DM not based on a specific player + if (GetLocalInt(GetModule(), SBR_REST_NOT_ALLOWED)==1) + LogMessage(iLogLevel, oPC, "MODULE REST DISABLED"); + else + LogMessage(iLogLevel, oPC, "MODULE REST ENABLED"); + + if (!SBR_MAKE_IT_SIMPLE) + { + if (GetLocalInt(GetArea(oPC), SBR_REST_NOT_ALLOWED)==1) + LogMessage(iLogLevel, oPC, "AREA REST DISABLED: "+GetName(GetArea(oPC))+"."); + else + LogMessage(iLogLevel, oPC, "AREA REST ENABLED: "+GetName(GetArea(oPC))+"."); + } + // see if there is a rest trigger in the area and alert the DM if there is one. + object oSafeTrigger = GetNearestObjectByTag("X0_SAFEREST", oPC); + if (GetArea(oSafeTrigger)==GetArea(oPC)) + LogMessage(iLogLevel, oPC, "Must Secure Region to Rest. AREA: "+ GetName(GetArea(oPC))+"."); + else + LogMessage(iLogLevel, oPC, "No rest triggers. AREA: "+ GetName(GetArea(oPC))+"."); + + // now look for the closest player and see if we can give more details to the DM. + int nSupply = 0; int nWoodland = 0; + + + if (!GetIsObjectValid(oPlayer)) + { + LogMessage(iLogLevel, oPC, "There are no characters near where you clicked."); + return; + } + else + { + object oCurrentPlayer = GetFirstFactionMember(oPlayer, TRUE); + + while (GetIsObjectValid(oCurrentPlayer)) + { + object oCurrent = GetFirstItemInInventory(oCurrentPlayer); + while (GetIsObjectValid(oCurrent)) + { + if (GetTag(oCurrent)==SBR_KIT_REGULAR) + { + nSupply = nSupply+1; + } + if (GetTag(oCurrent)==SBR_KIT_WOODLAND) + { + nWoodland = nWoodland+1; + } + + oCurrent = GetNextItemInInventory(oCurrentPlayer); + } + + oCurrentPlayer = GetNextFactionMember(oPlayer, TRUE); + } + + //Here we send all the information to the DM regarding the rest system and the closest player. + + LogMessage(iLogLevel, oPC, "Stats for (closest player): "+GetName(oPlayer)); + LogMessage(iLogLevel, oPC, IntToString(nSupply)+ " : Supply kits remaining in the party."); + LogMessage(iLogLevel, oPC, IntToString(nWoodland)+ " : Woodland kits remaining in the party."); + + if (!SBR_MAKE_IT_SIMPLE) + { + if (GetPLocalInt(oPlayer, SBR_REST_NOT_ALLOWED)==1) + LogMessage(iLogLevel, oPC, "PARTY REST DISABLED: "+GetName(oPlayer)+"."); + else + LogMessage(iLogLevel, oPC, "PARTY REST ENABLED: "+GetName(oPlayer)+"."); + } + +} + +} + + +//:://///////////////////////////////////////////// +//:: CreateKit function +//:: uhhh.... no copyright - Demetrious +//::////////////////////////////////////////////// +// This function creates the kit you just tried +// to use but could not for any number of reasons. +void CreateKit(object oPC, string sItemTag, int bOnGround = FALSE) +{ + string sResRef = sItemTag == SBR_KIT_WOODLAND ? "supplykit001" : "supplykit002"; + + //now give back the item the dumb player should not have tried to use :) + if (bOnGround == TRUE) + { + // Create the kit on the ground. + CreateObject(OBJECT_TYPE_ITEM, sResRef, GetLocation(oPC)); + } else { + // Create the kit on the player. + CreateItemOnObject(sResRef, oPC, 1); + } +} + + + +//:://///////////////////////////////////////////// +//:: CanIRest +//:: uhhh.... no copyright - Demetrious +//::////////////////////////////////////////////// +/* +Rest can be disabled on three different levels + +Module wide, area wide, and party wide. + +This flexibility is included due to the number of different manners +in which people run servers. + +The script will check for restrictions at each level +and return TRUE if no restrictions are found. +*/ +int CanIRest(object oPC) + { + int iLogLevel = LOG_DM_20; // Log level used with LogMessage + + if (GetLocalInt(GetModule(), SBR_REST_NOT_ALLOWED)==1) + { + LogMessage(iLogLevel, oPC, GetName(oPC)+" can't rest because of Module Level Restriction"); + return FALSE; + } + if (SBR_MAKE_IT_SIMPLE) + return TRUE; + + if (GetLocalInt(GetArea(oPC), SBR_REST_NOT_ALLOWED)==1) + { + LogMessage(iLogLevel, oPC, GetName(oPC)+" can't rest because of Area Level Restriction"); + return FALSE; + } + if (GetPLocalInt(oPC, SBR_REST_NOT_ALLOWED)==1) + { + LogMessage(iLogLevel, oPC, GetName(oPC)+" can't rest because of Party Level Restriction"); + return FALSE; + } + + return TRUE; + } + +//:://///////////////////////////////////////////// +//:: NotOnSafeRest +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + returns TRUE if the player is not in a safe + zone within an area. + + RULE: There must be at least one door + All doors must be closed + + - takes player object + - finds nearest safe zone + - is player in safe zone? + - find all doors in safe zone + - are all doors closed? + - if YES to all the above + is safe to rest, + RETURN FALSE + - otherwise give appropriate feedback and return TRUE + +EDITS: I added a quick check to look for the nearest trigger + in the SAME AREA ONLY. This is the only code that I + changed. - Demetrious +*/ +int NotOnSafeRest(object oPC) +{ // SpawnScriptDebugger(); + object oSafeTrigger = GetNearestObjectByTag("X0_SAFEREST", oPC); + int bAtLeastOneDoor = FALSE; + int bAllDoorsClosed = TRUE; + int bPCInTrigger = FALSE; + if (GetArea(oSafeTrigger)!=GetArea(oPC)) + return FALSE; + + if (GetIsObjectValid(oSafeTrigger)) + { + if (GetObjectType(oSafeTrigger) == OBJECT_TYPE_TRIGGER) + { + + // * cycle through trigger looking for oPC + // * and looking for closed doors + object oInTrig = GetFirstInPersistentObject(oSafeTrigger, OBJECT_TYPE_ALL); + while (GetIsObjectValid(oInTrig) == TRUE) + { + // * rester is in trigger! + if (oPC == oInTrig) + { + bPCInTrigger = TRUE; + } + else + { + // * one door found + if (GetObjectType(oInTrig) == OBJECT_TYPE_DOOR) + { + bAtLeastOneDoor = TRUE; + // * the door was open, exit + if (GetIsOpen(oInTrig) == TRUE) + { + return TRUE; //* I am no in a safe rest place because a door is open + } + } + } + oInTrig = GetNextInPersistentObject(oSafeTrigger, OBJECT_TYPE_ALL); + } + } + } + if (bPCInTrigger == FALSE || bAtLeastOneDoor == FALSE) + { + return TRUE; + } + // * You are in a safe trigger, if in a trigger, and all doors closed on that trigger. + return FALSE; +} + + diff --git a/src/include/shd_inc_metashd.nss b/src/include/shd_inc_metashd.nss new file mode 100644 index 0000000..47cf347 --- /dev/null +++ b/src/include/shd_inc_metashd.nss @@ -0,0 +1,236 @@ +//:://///////////////////////////////////////////// +//:: Shadowcasting include: Metashadow +//:: shd_inc_metashd +//:://///////////////////////////////////////////// +/** @file + Defines functions for handling metashadows + + @author Stratovarius + @date Created - 2019.2.7 + @thanks to Ornedan for his work on Psionics upon which this is based. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "shd_myst_const" + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/// No metashadows +const int METASHADOW_NONE = 0x0; +/// Quicken mystery +const int METASHADOW_QUICKEN = 0x2; +/// Empower mystery +const int METASHADOW_EMPOWER = 0x4; +/// Extend mystery +const int METASHADOW_EXTEND = 0x8; +/// Maximize mystery +const int METASHADOW_MAXIMIZE = 0x16; +/// Maximize mystery +const int METASHADOW_STILL = 0x32; + +/// Internal constant. Value is equal to the lowest metashadow constant. Used when looping over metashadow flag variables +const int METASHADOW_MIN = 0x2; +/// Internal constant. Value is equal to the highest metashadow constant. Used when looping over metashadow flag variables +const int METASHADOW_MAX = 0x32; + +/// Empower Mystery variable name +const string METASHADOW_EMPOWER_VAR = "PRC_ShadMeta_Empower"; +/// Extend Mystery variable name +const string METASHADOW_EXTEND_VAR = "PRC_ShadMeta_Extend"; +/// Quicken Mystery variable name +const string METASHADOW_QUICKEN_VAR = "PRC_ShadMeta_Quicken"; +/// Maximize Mystery variable name +const string METASHADOW_MAXIMIZE_VAR = "PRC_ShadMeta_Maximize"; +/// Still Mystery variable name +const string METASHADOW_STILL_VAR = "PRC_ShadMeta_Still"; + +/// The name of a marker variable that tells that the Mystery being shadowcast had Quicken Mystery used on it +const string PRC_MYSTERY_IS_QUICKENED = "PRC_MysteryIsQuickened"; + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure that contains common data used during mystery. + */ +struct mystery{ + /* Generic stuff */ + /// The creature Shadowcasting the Mystery + object oShadow; + /// Whether the mystery is successful or not + int bCanMyst; + /// The creature's shadowcaster level in regards to this mystery + int nShadowcasterLevel; + /// The mystery's spell ID + int nMystId; + /// Used to mark mysteries that have gone supernatural + int bIgnoreSR; + + /* Metashadows */ + /// Whether Empower mystery was used with this mystery + int bEmpower; + /// Whether Extend mystery was used with this mystery + int bExtend; + /// Whether Quicken mystery was used with this mystery + int bQuicken; + /// Whether Maximize mystery was used with this mystery + int bMaximize; + /// Whether Still mystery was used with this mystery + int bStill; + + /* Speak Unto the Masses */ + // Check if the target is a friend of not + int bFriend; + // Saving Throw DC + int nSaveDC; + // Saving Throw + int nSaveThrow; + // Saving Throw Type + int nSaveType; + // Spell Pen + int nPen; + // Duration Effects + effect eLink; + // Impact Effects + effect eLink2; + // Any Item Property + itemproperty ipIProp1; + // Any Item Property + itemproperty ipIProp2; + // Any Item Property + itemproperty ipIProp3; + // Duration + float fDur; +}; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the metashadows used in this mystery of a mystery + * and the cost added by their use. + * + * @param myst The mystery data related to this particular mystery + * @param nMetaMystFlags An integer containing a set of bitflags that determine + * which metashadow mysterys may be used with the Mystery being shadowcast + * + * @return The mystery data, modified to account for the metashadows + */ +struct mystery EvaluateMetashadows(struct mystery myst, int nMetaMystFlags); + +/** + * Calculates a mystery's damage based on the given dice and metashadows. + * + * @param nDieSize Size of the dice to use + * @param nNumberOfDice Amount of dice to roll + * @param nBonus A bonus amount of damage to add into the total once + * @param nBonusPerDie A bonus amount of damage to add into the total for each die rolled + * @param bDoesHPDamage Whether the Mystery deals hit point damage, or some other form of point damage + * @param bIsRayOrRangedTouch Whether the mystery's use involves a ranged touch attack roll or not + * @return The amount of damage the Mystery should deal + */ +int MetashadowsDamage(struct mystery myst, int nDieSize, int nNumberOfDice, int nBonus = 0, + int nBonusPerDie = 0, int bDoesHPDamage = FALSE, int bIsRayOrRangedTouch = FALSE); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_utility" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct mystery EvaluateMetashadows(struct mystery myst, int nMetaMystFlags) +{ + // Quicken Mystery - special handling + if(GetLocalInt(myst.oShadow, PRC_MYSTERY_IS_QUICKENED)) + { + // Mark the mystery as quickened here + myst.bQuicken = TRUE; + + // Delete the marker var + DeleteLocalInt(myst.oShadow, PRC_MYSTERY_IS_QUICKENED); + } + + if((nMetaMystFlags & METASHADOW_EMPOWER) && (GetLocalInt(myst.oShadow, METASHADOW_EMPOWER_VAR) || GetLocalInt(myst.oShadow, "FloodShadow"))) + { + // Mark the mystery as empowered here + myst.bEmpower = TRUE; + // Then clear the variable + DeleteLocalInt(myst.oShadow, METASHADOW_EMPOWER_VAR); + } + if((nMetaMystFlags & METASHADOW_EXTEND) && GetLocalInt(myst.oShadow, METASHADOW_EXTEND_VAR)) + { + // Mark the mystery as extended here + myst.bExtend = TRUE; + // Then clear the variable + DeleteLocalInt(myst.oShadow, METASHADOW_EXTEND_VAR); + } + if((nMetaMystFlags & METASHADOW_MAXIMIZE) && GetLocalInt(myst.oShadow, METASHADOW_MAXIMIZE_VAR)) + { + // Mark the mystery as maximized here + myst.bMaximize = TRUE; + // Then clear the variable + DeleteLocalInt(myst.oShadow, METASHADOW_MAXIMIZE_VAR); + } + + return myst; +} + +int MetashadowsDamage(struct mystery myst, int nDieSize, int nNumberOfDice, int nBonus = 0, + int nBonusPerDie = 0, int bDoesHPDamage = FALSE, int bIsRayOrRangedTouch = FALSE) +{ + int nBaseDamage = 0, + nBonusDamage = nBonus + (nNumberOfDice * nBonusPerDie); + + // Calculate the base damage + int i; + for (i = 0; i < nNumberOfDice; i++) + nBaseDamage += Random(nDieSize) + 1; + + + // Apply general modifying effects + if(bDoesHPDamage) + { + if(bIsRayOrRangedTouch) + { + // Anything that affects Ray Mysterys goes here + } + } + + // Apply metashadows + // Both empower & maximize + if(myst.bEmpower && myst.bMaximize) + { + nBaseDamage = nBaseDamage / 2 + nDieSize * nNumberOfDice; + if(DEBUG) DoDebug("MetashadowsDamage(): Empower + Max"); + } + // Just empower + else if(myst.bEmpower) + { + nBaseDamage += nBaseDamage / 2; + if(DEBUG) DoDebug("MetashadowsDamage(): Empower only"); + } + // Just maximize + else if(myst.bMaximize) + { + nBaseDamage = nDieSize * nNumberOfDice; + if(DEBUG) DoDebug("MetashadowsDamage(): Max only"); + } + + return nBaseDamage + nBonusDamage; +} + +// Test main +//void main(){} diff --git a/src/include/shd_inc_myst.nss b/src/include/shd_inc_myst.nss new file mode 100644 index 0000000..40d7f93 --- /dev/null +++ b/src/include/shd_inc_myst.nss @@ -0,0 +1,722 @@ +//:://///////////////////////////////////////////// +//:: Shadowcasting include: Shadowcasting +//:: shd_inc_myst +//:://///////////////////////////////////////////// +/** @file + Defines structures and functions for handling + shadowcasting a mystery + + @author Stratovarius + @date Created - 2019.2.7 + @thanks to Ornedan for his work on Psionics upon which this is based. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string PRC_SHADOWCASTING_CLASS = "PRC_CurrentMystery_ShadowcastingClass"; +const string PRC_MYSTERY_LEVEL = "PRC_CurrentMystery_Level"; +const string MYST_DEBUG_IGNORE_CONSTRAINTS = "MYST_DEBUG_IGNORE_CONSTRAINTS"; + +/** + * The variable in which the mystery token is stored. If no token exists, + * the variable is set to point at the shadowcaster itself. That way OBJECT_INVALID + * means the variable is unitialised. + */ +//const string PRC_MYSTERY_TOKEN_VAR = "PRC_MysteryToken"; +//const string PRC_MYSTERY_TOKEN_NAME = "PRC_MYSTTOKEN"; +//const float PRC_MYSTERY_HB_DELAY = 0.5f; + + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +// struct mystery moved to shd_inc_metashd + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines if the mystery that is currently being attempted to be TrueSpoken + * can in fact be truespoken. Determines metashadows used. + * + * @param oShadow A creature attempting to shadowcast a mystery at this moment. + * @param oTarget The target of the mystery, if any. For pure Area of Effect + * mysteries, this should be OBJECT_INVALID. Otherwise the main + * target of the mystery as returned by PRCGetSpellTargetObject(). + * @param nMetaShadFlags The metashadows that may be used to modify this mystery. Any number + * of METASHADOW_* constants ORd together using the | operator. + * For example (METASHADOW_EMPOWER | METASHADOW_EXTEND) + * + * @return A mystery structure that contains the data about whether + * the mystery was successfully shadowcast, what metashadows + * were used and some other commonly used data, like the + * creator's shadowcaster level for this mystery. + */ +struct mystery EvaluateMystery(object oShadow, object oTarget, int nMetaShadFlags); + +/** + * Causes OBJECT_SELF to use the given mystery. + * + * @param nMyst The index of the mystery to use in spells.2da or a MYST_* + * @param nClass The index of the class to use the mystery as in classes.2da or a CLASS_TYPE_* + * @param nLevelOverride An optional override to normal shadowcaster level. + * Default: 0, which means the parameter is ignored. + */ +void UseMystery(int nMyst, int nClass, int nLevelOverride = 0); + +/** + * A debugging function. Takes a mystery structure and + * makes a string describing the contents. + * + * @param myst A set of mystery data + * @return A string describing the contents of myst + */ +string DebugMystery2Str(struct mystery myst); + +/** + * Sets the evaluation functions to ignore constraints on shadowcasting. + * Call this just prior to EvaluateMystery() in a mystery script. + * That evaluation will then ignore lacking mystery ability score, + * and other restrictions + * + * @param oShadow A creature attempting to shadowcast a mystery at this moment. + */ +void ShadowcastDebugIgnoreConstraints(object oShadow); + +/** + * Returns the uses per day already used + * + * @param oShadow Caster of the Mystery + * @param nMystId SpellId of the Mystery + * @return Number of uses per day already used + */ +int GetMysteryUses(object oShadow, int nMystId); + +/** + * Returns the bonus uses per day already used + * + * @param oShadow Caster of the Mystery + * @param nMystLevel Level of the Mystery + * @return Number of uses per day already used + */ +int GetBonusUses(object oShadow, int nMystLevel); + +/** + * Adds a use per day + * + * @param oShadow Caster of the Mystery + * @param nMystId SpellId of the Mystery + */ +void SetMysteryUses(object oShadow, int nMystId); + +/** + * Adds a bonus use per day + * + * @param oShadow Caster of the Mystery + * @param nMystLevel Level of the Mystery + */ +void SetBonusUses(object oShadow, int nMystLevel); + +/** + * Deletes all of the Local Ints stored by uses per day. + * Called OnRest and OnEnter + * + * @param oShadow Caster of the Mystery + */ +void ClearMystLocalVars(object oShadow); + +/** + * Returns total uses per day for the shadowcaster for a given mystery + * + * @param oShadow Caster of the Mystery + * @param nMystId SpellId of the Mystery + * @param nClass Class to check against + */ +int MysteriesPerDay(object oShadow, int nMystId, int nClass); + +/** + * Calculates bonus mysteries from a high intelligence + * + * @param oShadow Caster of the Mystery + * @param nMystLevel Mystery level to check + */ +int BonusMysteriesPerDay(object oShadow, int nMystLevel); + +/** + * Returns the name of the mystery + * + * @param nMystId SpellId of the mystery + */ +string GetMysteryName(int nMystId); + +/** + * Checks whether the mystery is supernatural or not + * + * @param nMystId The Mystery to Check + * @return TRUE if Mystery is (Su), else FALSE + */ +int GetIsMysterySupernatural(object oShadow, int nMystId, int nClass); + +/** + * Checks whether the mystery is a SLA or not + * + * @param nMystId The Mystery to Check + * @return TRUE if Mystery is (Su), else FALSE + */ +int GetIsMysterySLA(object oShadow, int nMystId, int nClass); + +/** + * Checks whether the mystery is a Fundamental or not + * + * @param nMystId The Mystery to Check + * @return TRUE if Mystery is a Fundamental, else FALSE + */ +int GetIsFundamental(int nMystId); + +/** + * Checks whether caster has Favored Mystery in the cast mystery + * + * @param oShadow The Shadowcaster + * @param nMyst The Mystery to Check + * @return TRUE if he has the feat, else FALSE + */ +int GetHasFavoredMystery(object oShadow, int nMyst); + +/** + * Checks whether caster has Shadow Cast feat + * + * @param oShadow The Shadowcaster + * @return TRUE if he has the feat, else FALSE + */ +int GetShadowCast(object oShadow); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "shd_inc_metashd" +#include "shd_inc_shdfunc" +#include "prc_inc_combat" +#include "inc_newspellbook" +#include "inc_lookups" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Handles Spellfire absorption when a mystery is used on a friendly spellfire + * user. + */ +struct mystery _DoShadowcastSpellfireFriendlyAbsorption(struct mystery myst, object oTarget) +{ + if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") && + GetIsFriend(oTarget, myst.oShadow) + ) + { + if(CheckSpellfire(myst.oShadow, oTarget, TRUE)) + { + PRCShowSpellResist(myst.oShadow, oTarget, SPELL_RESIST_MANTLE); + myst.bCanMyst = FALSE; + } + } + + return myst; +} + +/** Internal function. + * Deletes mystery-related local variables. + * + * @param oShadow The creature currently shadowcasting a mystery + */ +void _CleanMysteryVariables(object oShadow) +{ + DeleteLocalInt(oShadow, PRC_SHADOWCASTING_CLASS); + DeleteLocalInt(oShadow, PRC_MYSTERY_LEVEL); +} + +/** Internal function. + * Sets mystery-related local variables. + * + * @param oShadow The creature currently shadowcasting a mystery + * @param nClass Mystery casting class constant + * @param nLevel Mystery level + * @param bQuicken If the mystery was quickened 1, else 0 + */ +void _SetMysteryVariables(object oShadow, int nClass, int nLevel, int bQuicken) +{ + if (DEBUG) FloatingTextStringOnCreature(GetName(oShadow)+" is a "+IntToString(nClass)+" at "+IntToString(nLevel)+" level", oShadow); + SetLocalInt(oShadow, PRC_SHADOWCASTING_CLASS, nClass + 1); + SetLocalInt(oShadow, PRC_MYSTERY_LEVEL, nLevel); + SetLocalInt(oShadow, PRC_MYSTERY_IS_QUICKENED, bQuicken); +} + +// Makes sure radial spells are stored on the correct row number +string _GetMysterySpellId(int nMystId) +{ + string nReturn = Get2DACache("spells", "Master", nMystId); + if (1 > StringToInt(nReturn)) nReturn = IntToString(nMystId); // SpellId invalid for the Master column + + return nReturn; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct mystery EvaluateMystery(object oShadow, object oTarget, int nMetaShadFlags) +{ + /* Get some data */ + int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oShadow, MYST_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // shadowcaster-related stuff + int nShadowcasterLevel = GetShadowcasterLevel(oShadow); + int nMystLevel = GetMysteryLevel(oShadow); + int nClass = GetShadowcastingClass(oShadow); + + if (DEBUG) FloatingTextStringOnCreature(GetName(oShadow)+" is a "+IntToString(nClass)+" casting a "+IntToString(nMystLevel)+" level mystery at "+IntToString(nShadowcasterLevel)+" shadowcaster level", oShadow); + + /* Initialise the mystery structure */ + struct mystery myst; + myst.oShadow = oShadow; + myst.bCanMyst = TRUE; // Assume successful mystery by default + myst.nShadowcasterLevel = nShadowcasterLevel; + myst.nMystId = PRCGetSpellId(); + myst.bIgnoreSR = GetIsMysterySupernatural(oShadow, myst.nMystId, nClass); + + // Account for metashadows. This includes adding the appropriate DC boosts. + myst = EvaluateMetashadows(myst, nMetaShadFlags); + + // Skip paying anything if something has prevented a successful cast already by this point + // Fundamentals track their uses per day differently. + if(myst.bCanMyst && !GetIsFundamental(myst.nMystId)) + { + if (GetMysteryUses(oShadow, myst.nMystId) >= MysteriesPerDay(oShadow, myst.nMystId, nClass)) // Used up all regular uses + { + SetBonusUses(myst.oShadow, nMystLevel); + FloatingTextStringOnCreature("You have "+IntToString(BonusMysteriesPerDay(oShadow, nMystLevel)-GetBonusUses(oShadow, nMystLevel))+" bonus uses of level "+IntToString(nMystLevel)+" remaining", oShadow, FALSE); + } + else + { + SetMysteryUses(myst.oShadow, myst.nMystId); + FloatingTextStringOnCreature("You have "+IntToString(MysteriesPerDay(myst.oShadow, myst.nMystId, nClass)-GetMysteryUses(myst.oShadow, myst.nMystId))+" uses of " + GetMysteryName(myst.nMystId) + " remaining", oShadow, FALSE); + FloatingTextStringOnCreature("You have "+IntToString(BonusMysteriesPerDay(oShadow, nMystLevel)-GetBonusUses(oShadow, nMystLevel))+" bonus uses of level "+IntToString(nMystLevel)+" remaining", oShadow, FALSE); + } + }//end if + + if(DEBUG) DoDebug("EvaluateMystery(): Final result:\n" + DebugMystery2Str(myst)); + + // Initiate mystery-related variable CleanUp + //DelayCommand(0.5f, _CleanMysteryVariables(oShadow)); + + return myst; +} + +void UseMystery(int nMyst, int nClass, int nLevelOverride = 0) +{ + object oShadow = OBJECT_SELF; + int bQuickened = FALSE; + object oSkin = GetPCSkin(oShadow); + int nMystDur = StringToInt(Get2DACache("spells", "ConjTime", nMyst)) + StringToInt(Get2DACache("spells", "CastTime", nMyst)); + int nMystLevel = GetMysteryLevel(oShadow, nMyst); + + if(GetAbilityScore(oShadow, ABILITY_INTELLIGENCE, TRUE) < nMystLevel + 10) + { + FloatingTextStringOnCreature("Your Intelligence score is too low to shadowcast this mystery", oShadow, FALSE); + return; + } + + // Check uses per day. This is done here so that players don't waste an action on it + if ((GetMysteryUses(oShadow, nMyst) >= MysteriesPerDay(oShadow, nMyst, nClass)) && (GetBonusUses(oShadow, nMystLevel) >= BonusMysteriesPerDay(oShadow, nMystLevel))) + { + FloatingTextStringOnCreature("You have used " + GetMysteryName(nMyst) + " the maximum amount of times today.", oShadow, FALSE); + return; + } + + // Quicken mystery check + if(nMystDur <= 6000 && // If the mystery could be quickened by having shadowcasting time of 1 round or less + GetLocalInt(oShadow, METASHADOW_QUICKEN_VAR) && // And the shadowcaster has Quicken active + TakeSwiftAction(oShadow)) // And the shadowcaster can take a swift action + { + //Adding Auto-Quicken III for one round - deleted after casting is finished. + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nMystDur/1000.0f)); + bQuickened = TRUE; + // Then clear the variable + DeleteLocalInt(oShadow, METASHADOW_QUICKEN_VAR); + } + + if (nMyst == MYST_SHADOW_SKIN || nMyst == MYST_SHADOW_EVOCATION_CONV || nMyst == MYST_GREATER_SHADOW_EVO_CONV || + nMyst == MYST_GREATER_SHADOW_EVO || nMyst == MYST_SHADOW_EVOCATION || (nMyst == MYST_ECHO_SPELL && GetLocalInt(oShadow, "EchoedSpell"))) // These are immediate actions + { + //Adding Auto-Quicken III for one round - deleted after casting is finished. + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nMystDur/1000.0f)); + bQuickened = TRUE; + } + + // SLAs and Supernaturals both ignore the Somatic component + if(GetIsMysterySupernatural(oShadow, nMyst, nClass) || GetIsMysterySLA(oShadow, nMyst, nClass)) + { + //Adding Auto-Still for one round - deleted after casting is finished. 7-9th level mysteries don't become SLA, hence lack of Still III + itemproperty ipAutoStill = ItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_AUTO_STILL_II); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoStill, oSkin, nMystDur/1000.0f)); + ipAutoStill = ItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_AUTO_STILL_I); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoStill, oSkin, nMystDur/1000.0f)); + } + // Supernaturals don't generate AoO, nor do casters with the Shadowcast feat in the right situation + if(GetIsMysterySupernatural(oShadow, nMyst, nClass) || GetShadowCast(oShadow)) + { + //Adding Improved Combat Casting for one round - deleted after casting is finished. + itemproperty ipICC = ItemPropertyBonusFeat(IP_CONST_IMP_CC); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipICC, oSkin, nMystDur/1000.0f)); + } + // Still mystery check. Both SLA and Su are automatically stilled. + if(!GetIsMysterySupernatural(oShadow, nMyst, nClass) && !GetIsMysterySLA(oShadow, nMyst, nClass) && GetLocalInt(oShadow, METASHADOW_STILL_VAR)) + { + //Adding Auto-Still for one round - deleted after casting is finished. + itemproperty ipAutoStill = ItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_AUTO_STILL_II); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoStill, oSkin, nMystDur/1000.0f)); + ipAutoStill = ItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_AUTO_STILL_I); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoStill, oSkin, nMystDur/1000.0f)); + ipAutoStill = ItemPropertyBonusFeat(IP_CONST_FEAT_EPIC_AUTO_STILL_III); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoStill, oSkin, nMystDur/1000.0f)); + // Then clear the variable + DeleteLocalInt(oShadow, METASHADOW_STILL_VAR); + } + + // Setup mystery-related variables + ActionDoCommand(_SetMysteryVariables(oShadow, nClass, StringToInt(lookup_spell_innate(nMyst)), bQuickened)); + + // cast the actual mystery + ActionCastSpell(nMyst, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, FALSE); + + // Initiate mystery-related variable CleanUp + ActionDoCommand(_CleanMysteryVariables(oShadow)); +} + +string DebugMystery2Str(struct mystery myst) +{ + string sRet; + + sRet += "oShadow = " + DebugObject2Str(myst.oShadow) + "\n"; + sRet += "bCanMyst = " + DebugBool2String(myst.bCanMyst) + "\n"; + sRet += "nShadowcasterLevel = " + IntToString(myst.nShadowcasterLevel) + "\n"; + + sRet += "bEmpower = " + DebugBool2String(myst.bEmpower) + "\n"; + sRet += "bExtend = " + DebugBool2String(myst.bExtend) + "\n"; + sRet += "bQuicken = " + DebugBool2String(myst.bQuicken);// + "\n"; + + return sRet; +} + +void ShadowcastDebugIgnoreConstraints(object oShadow) +{ + SetLocalInt(oShadow, MYST_DEBUG_IGNORE_CONSTRAINTS, TRUE); + DelayCommand(0.0f, DeleteLocalInt(oShadow, MYST_DEBUG_IGNORE_CONSTRAINTS)); +} + +int GetMysteryUses(object oShadow, int nMystId) +{ + // This makes sure everything is stored using the proper number + return GetLocalInt(oShadow, MYSTERY_USES + _GetMysterySpellId(nMystId)); +} + +int GetBonusUses(object oShadow, int nMystLevel) +{ + // This makes sure everything is stored using the proper number + return GetLocalInt(oShadow, MYSTERY_BONUS_USES + IntToString(nMystLevel)); +} + +void SetMysteryUses(object oShadow, int nMystId) +{ + // Apprentice mysteries only for Warp Spell + if(4 > GetMysteryLevel(oShadow, nMystId) && GetLocalInt(oShadow, "WarpSpellSuccess")) + { + DeleteLocalInt(oShadow, "WarpSpellSuccess"); + FloatingTextStringOnCreature("Warped Spell used", oShadow, FALSE); + return; + } + if (nMystId == MYST_ECHO_SPELL && GetLocalInt(oShadow, "EchoedSpell")) + return; //This is a free use and doesn't count. + + if (GetLocalInt(oShadow, "MysteryFreeUse")) + return; + + if (GetLocalInt(oShadow, "InnateCounterSuccess") == GetMysteryLevel(oShadow, nMystId)) + { + DeleteLocalInt(oShadow, "InnateCounterSuccess"); + FloatingTextStringOnCreature("Innate Counterspell used", oShadow, FALSE); + return; + } + + // This makes sure everything is stored using the proper number + string sSpellId = _GetMysterySpellId(nMystId); + // Uses are stored for each Mystery by SpellId + int nNum = GetLocalInt(oShadow, MYSTERY_USES + sSpellId); + // Store the number of times per day its been cast succesfully + SetLocalInt(oShadow, MYSTERY_USES + sSpellId, (nNum + 1)); +} + +void SetBonusUses(object oShadow, int nMystLevel) +{ + // Uses are stored by level + int nNum = GetLocalInt(oShadow, MYSTERY_BONUS_USES + IntToString(nMystLevel)); + // Store the number of times per day its been cast succesfully + SetLocalInt(oShadow, MYSTERY_BONUS_USES + IntToString(nMystLevel), (nNum + 1)); +} + +void ClearMystLocalVars(object oShadow) +{ + // Uses are stored for each Mystery by SpellId + // So we loop em all and blow em away + // Because there are only 60, this should not TMI + // i is the SpellId + int i; + for(i = 18352; i < 18429; i++) + { + DeleteLocalInt(oShadow, MYSTERY_USES + IntToString(i)); + } + // Web Enhancement Mysteries + for(i = 18579; i < 18590; i++) + { + DeleteLocalInt(oShadow, MYSTERY_USES + IntToString(i)); + } + for(i = 0; i < 10; i++) + { + DeleteLocalInt(oShadow, MYSTERY_BONUS_USES + IntToString(i)); + } +} + +int MysteriesPerDay(object oShadow, int nMystId, int nClass) +{ + if (nClass == CLASS_TYPE_SHADOWSMITH) return 1; // They never get more than this. + + int nUses = 1; //always get at least 1 + int nLevel = GetMysteryLevel(oShadow, nMystId); + // Done this way so it doesn't count for feats or other misc boosts + int nShadow = GetLevelByClass(nClass, oShadow) + GetShadowMagicPRCLevels(oShadow); + + //if(DEBUG) DoDebug("MysteriesPerDay(): GetMysteryLevel "+IntToString(nLevel)); + //if(DEBUG) DoDebug("MysteriesPerDay(): GetShadowcasterLevel "+IntToString(nShadow)); + + if (nMystId == MYST_ECHO_SPELL && GetLocalInt(oShadow, "EchoedSpell")) + return 99; //This is a free use and doesn't count. + + if (GetLocalInt(oShadow, "MysteryFreeUse")) + return 99; + + if (nShadow >= 13 && 4 > nLevel) + nUses = 3; + else if (nShadow >= 13 && 7 > nLevel && nLevel > 3) + nUses = 2; + else if (nShadow >= 7 && 4 > nLevel) + nUses = 2; + + if (nShadow >= 13 && 4 > nLevel && GetHasFavoredMystery(oShadow, nMystId)) // Favored mystery can grant +1 use per day to Apprentice + nUses += 1; + + return nUses; +} + +int BonusMysteriesPerDay(object oShadow, int nMystLevel) +{ + if(GetAbilityScore(oShadow, ABILITY_CHARISMA, TRUE) < nMystLevel + 10) + return 0; + int nSlots; + + // Both Mystery classes use Int for this + int nAbilityMod = GetAbilityModifier(ABILITY_CHARISMA, oShadow); + if (nAbilityMod >= nMystLevel) // Need an ability modifier at least equal to the spell level to gain bonus slots + nSlots += ((nAbilityMod - nMystLevel) / 4) + 1; + if(DEBUG) DoDebug("BonusMysteriesPerDay(): nSlots "+IntToString(nSlots)); + return nSlots; +} + +string GetMysteryName(int nMystId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nMystId))); +} + +int GetIsMysterySupernatural(object oShadow, int nMystId, int nClass) +{ + if (nClass == CLASS_TYPE_SHADOWSMITH) return FALSE; + int nLevel = GetMysteryLevel(oShadow, nMystId); + int nShadow = GetShadowcasterLevel(oShadow); + + if (nShadow >= 13 && 4 > nLevel) + return TRUE; + if (nShadow >= 13 && 7 > nLevel && nLevel > 3 && GetHasFavoredMystery(oShadow, nMystId)) // Bump up Initiate + return TRUE; + + // If nothing returns TRUE, fail + return FALSE; +} + +int GetIsMysterySLA(object oShadow, int nMystId, int nClass) +{ + if (nClass == CLASS_TYPE_SHADOWSMITH) return FALSE; + int nLevel = GetMysteryLevel(oShadow, nMystId); + int nShadow = GetShadowcasterLevel(oShadow); + + if (nShadow >= 13 && 7 > nLevel && nLevel > 3) + return TRUE; + else if (nShadow >= 7 && 4 > nLevel) + return TRUE; + else if (nShadow < 7 && 4 > nLevel && GetHasFavoredMystery(oShadow, nMystId)) // Bump up Apprentice + return TRUE; + if (nShadow < 13 && nShadow >= 7 && 7 > nLevel && nLevel > 3 && GetHasFavoredMystery(oShadow, nMystId)) // Bump up Initiate + return TRUE; + if (nShadow >= 13 && nLevel >= 7 && GetHasFavoredMystery(oShadow, nMystId)) // Bump up Master + return TRUE; + + // If nothing returns TRUE, fail + return FALSE; +} + +int GetIsFundamental(int nMystId) +{ + if(nMystId == FUND_ARROW_DUSK || + nMystId == FUND_BLACK_CANDLE_LIGHT || + nMystId == FUND_BLACK_CANDLE_DARK || + nMystId == FUND_CAUL_SHADOW || + nMystId == FUND_MYSTIC_REFLECTIONS || + nMystId == FUND_SHADOW_HOOD || + nMystId == FUND_SIGHT_OBSCURED || + nMystId == FUND_UMBRAL_HAND || + nMystId == FUND_WIDENED_EYES) + return TRUE; + + return FALSE; +} + +int GetHasFavoredMystery(object oShadow, int nMyst) +{ + if (DEBUG) DoDebug("GetHasFavoredMystery(): Mystery "+IntToString(nMyst)); + int nFavored, nReturn; + switch(nMyst) + { + case MYST_BEND_PERSPECTIVE : nFavored = FEAT_FAV_MYST_BENDPERSPECTIVE ; break; + case MYST_CARPET_SHADOW : nFavored = FEAT_FAV_MYST_CARPETSHADOW ; break; + case MYST_DUSK_AND_DAWN_DUSK : nFavored = FEAT_FAV_MYST_DUSKANDDAWN ; break; + case MYST_DUSK_AND_DAWN_DAWN : nFavored = FEAT_FAV_MYST_DUSKANDDAWN ; break; + case MYST_LIFE_FADES : nFavored = FEAT_FAV_MYST_LIFEFADES ; break; + case MYST_MESMERIZING_SHADE : nFavored = FEAT_FAV_MYST_MESMERIZINGSHADE ; break; + case MYST_STEEL_SHADOWS : nFavored = FEAT_FAV_MYST_STEELSHADOWS ; break; + case MYST_VOICE_SHADOW_APPROACH: nFavored = FEAT_FAV_MYST_VOICEOFSHADOW ; break; + case MYST_VOICE_SHADOW_DROP : nFavored = FEAT_FAV_MYST_VOICEOFSHADOW ; break; + case MYST_VOICE_SHADOW_FALL : nFavored = FEAT_FAV_MYST_VOICEOFSHADOW ; break; + case MYST_VOICE_SHADOW_FLEE : nFavored = FEAT_FAV_MYST_VOICEOFSHADOW ; break; + case MYST_VOICE_SHADOW_HALT : nFavored = FEAT_FAV_MYST_VOICEOFSHADOW ; break; + case MYST_BLACK_FIRE : nFavored = FEAT_FAV_MYST_BLACKFIRE ; break; + case MYST_CONGRESS_SHADOWS : nFavored = FEAT_FAV_MYST_CONGRESSSHADOWS ; break; + case MYST_FLESH_FAILS_STR : nFavored = FEAT_FAV_MYST_FLESHFAILS ; break; + case MYST_FLESH_FAILS_DEX : nFavored = FEAT_FAV_MYST_FLESHFAILS ; break; + case MYST_FLESH_FAILS_CON : nFavored = FEAT_FAV_MYST_FLESHFAILS ; break; + case MYST_PIERCING_SIGHT : nFavored = FEAT_FAV_MYST_PIERCINGSIGHT ; break; + case MYST_SHADOW_SKIN : nFavored = FEAT_FAV_MYST_SHADOWSKIN ; break; + case MYST_SIGHT_ECLIPSED : nFavored = FEAT_FAV_MYST_SIGHTECLIPSED ; break; + case MYST_THOUGHTS_SHADOW_INT : nFavored = FEAT_FAV_MYST_THOUGHTSSHADOW ; break; + case MYST_THOUGHTS_SHADOW_WIS : nFavored = FEAT_FAV_MYST_THOUGHTSSHADOW ; break; + case MYST_THOUGHTS_SHADOW_CHA : nFavored = FEAT_FAV_MYST_THOUGHTSSHADOW ; break; + case MYST_AFRAID_DARK : nFavored = FEAT_FAV_MYST_AFRAIDOFTHEDARK ; break; + case MYST_CLINGING_DARKNESS : nFavored = FEAT_FAV_MYST_CLINGINGDARKNESS ; break; + case MYST_DANCING_SHADOWS : nFavored = FEAT_FAV_MYST_DANCINGSHADOWS ; break; + case MYST_FLICKER : nFavored = FEAT_FAV_MYST_FLICKER ; break; + case MYST_KILLING_SHADOWS : nFavored = FEAT_FAV_MYST_KILLINGSHADOWS ; break; + case MYST_SHARP_SHADOWS : nFavored = FEAT_FAV_MYST_SHARPSHADOWS ; break; + case MYST_UMBRAL_TOUCH : nFavored = FEAT_FAV_MYST_UMBRALTOUCH ; break; + case MYST_AURA_OF_SHADE : nFavored = FEAT_FAV_MYST_AURAOFSHADE ; break; + case MYST_BOLSTER : nFavored = FEAT_FAV_MYST_BOLSTER ; break; + case MYST_SHADOW_EVOCATION : nFavored = FEAT_FAV_MYST_SHADOWEVOCATION ; break; + case MYST_SHADOW_VISION : nFavored = FEAT_FAV_MYST_SHADOWVISION ; break; + case MYST_SHADOWS_FADE : nFavored = FEAT_FAV_MYST_SHADOWSFADE ; break; + case MYST_STEP_SHADOW_SELF : nFavored = FEAT_FAV_MYST_STEPINTOSHADOW ; break; + case MYST_STEP_SHADOW_PARTY : nFavored = FEAT_FAV_MYST_STEPINTOSHADOW ; break; + case MYST_WARP_SPELL : nFavored = FEAT_FAV_MYST_WARPSPELL ; break; + case MYST_CURTAIN_SHADOWS : nFavored = FEAT_FAV_MYST_CURTAINSHADOWS ; break; + case MYST_DARK_AIR : nFavored = FEAT_FAV_MYST_DARKAIR ; break; + case MYST_ECHO_SPELL : nFavored = FEAT_FAV_MYST_FEIGNLIFE ; break; + case MYST_FEIGN_LIFE : nFavored = FEAT_FAV_MYST_DARKSOUL ; break; + case MYST_LANGUOR_SLOW : nFavored = FEAT_FAV_MYST_LANGUOR ; break; + case MYST_LANGUOR_HOLD : nFavored = FEAT_FAV_MYST_LANGUOR ; break; + case MYST_PASS_SHADOW_SELF : nFavored = FEAT_FAV_MYST_PASSINTOSHADOW ; break; + case MYST_PASS_SHADOW_PARTY : nFavored = FEAT_FAV_MYST_PASSINTOSHADOW ; break; + case MYST_UNRAVEL_DWEOMER : nFavored = FEAT_FAV_MYST_UNRAVELDWEOMER ; break; + case MYST_FLOOD_SHADOW : nFavored = FEAT_FAV_MYST_FLOODSHADOWS ; break; + case MYST_GREATER_SHADOW_EVO : nFavored = FEAT_FAV_MYST_GREATERSHADOWEVOCATION ; break; + case MYST_SHADOW_INVESTITURE : nFavored = FEAT_FAV_MYST_SHADOWINVESTITURE ; break; + case MYST_SHADOW_STORM : nFavored = FEAT_FAV_MYST_SHADOWSTORM ; break; + case MYST_SHADOWS_FADE_GREATER : nFavored = FEAT_FAV_MYST_SHADOWSFADE_GREATER ; break; + case MYST_UNVEIL : nFavored = FEAT_FAV_MYST_UNVEIL ; break; + case MYST_VOYAGE_SHADOW_SELF : nFavored = FEAT_FAV_MYST_VOYAGESHADOW ; break; + case MYST_VOYAGE_SHADOW_PARTY : nFavored = FEAT_FAV_MYST_VOYAGESHADOW ; break; + case MYST_DARK_SOUL : nFavored = FEAT_FAV_MYST_DARKSOUL ; break; + case MYST_EPHEMERAL_IMAGE : nFavored = FEAT_FAV_MYST_EPHEMERALIMAGE ; break; + case MYST_LIFE_FADES_GREATER : nFavored = FEAT_FAV_MYST_LIFEFADESGREATER ; break; + case MYST_PRISON_NIGHT : nFavored = FEAT_FAV_MYST_PRISONNIGHT ; break; + case MYST_UMBRAL_SERVANT : nFavored = FEAT_FAV_MYST_UMBRALSERVANT ; break; + case MYST_TRUTH_REVEALED : nFavored = FEAT_FAV_MYST_TRUTHREVEALED ; break; + case MYST_FAR_SIGHT : nFavored = FEAT_FAV_MYST_FARSIGHT ; break; + case MYST_GR_FLESH_FAILS_STR : nFavored = FEAT_FAV_MYST_GRFLESHFAILS ; break; + case MYST_GR_FLESH_FAILS_DEX : nFavored = FEAT_FAV_MYST_GRFLESHFAILS ; break; + case MYST_GR_FLESH_FAILS_CON : nFavored = FEAT_FAV_MYST_GRFLESHFAILS ; break; + case MYST_SHADOW_PLAGUE : nFavored = FEAT_FAV_MYST_SHADOWPLAGUE ; break; + case MYST_SOUL_PUPPET : nFavored = FEAT_FAV_MYST_SOULPUPPET ; break; + case MYST_TOMB_NIGHT : nFavored = FEAT_FAV_MYST_TOMBNIGHT ; break; + case MYST_UMBRAL_BODY : nFavored = FEAT_FAV_MYST_UMBRALBODY ; break; + case MYST_ARMY_SHADOW : nFavored = FEAT_FAV_MYST_ARMYSHADOW ; break; + case MYST_CONSUME_ESSENCE : nFavored = FEAT_FAV_MYST_CONSUMEESSENCE ; break; + case MYST_EPHEMERAL_STORM : nFavored = FEAT_FAV_MYST_EPHEMERALSTORM ; break; + case MYST_REFLECTIONS : nFavored = FEAT_FAV_MYST_REFLECTIONS ; break; + case MYST_SHADOW_SURGE : nFavored = FEAT_FAV_MYST_SHADOWSURGE ; break; + case MYST_SHADOW_TIME : nFavored = FEAT_FAV_MYST_SHADOWTIME ; break; + case MYST_QUICKER_THAN_THE_EYE : nFavored = FEAT_FAV_MYST_QUICKERTHANTHEEYE ; break; + case MYST_TRAIL_OF_HAZE : nFavored = FEAT_FAV_MYST_TRAILHAZE ; break; + case MYST_UMBRAL_FIST : nFavored = FEAT_FAV_MYST_UMBRALFIST ; break; + case MYST_FEARFUL_GLOOM : nFavored = FEAT_FAV_MYST_FEARFULGLOOM ; break; + case MYST_SICKENING_SHADOW : nFavored = FEAT_FAV_MYST_SICKENINGSHADOW ; break; + case MYST_DEADLY_SHADE_DR : nFavored = FEAT_FAV_MYST_DEADLYSHADE ; break; + case MYST_DEADLY_SHADE_NEG : nFavored = FEAT_FAV_MYST_DEADLYSHADE ; break; + case MYST_GRASPING_SHADOWS : nFavored = FEAT_FAV_MYST_GRASPINGSHADOWS ; break; + case MYST_MENAGERIE_OF_DARKNESS: nFavored = FEAT_FAV_MYST_MENAGERIEDARKNESS ; break; + case MYST_BLACK_LABYRINTH : nFavored = FEAT_FAV_MYST_BLACKLABYRINTH ; break; + } + if(GetHasFeat(nFavored, oShadow)) + nReturn = 1; + + // If none of those trigger. + return nReturn; +} + +int GetShadowCast(object oShadow) +{ + if (DEBUG) DoDebug("GetShadowCast() enter"); + if (!GetHasFeat(FEAT_SHADOW_CAST, oShadow)) return FALSE; // Need the feat + + location lTarget = GetLocation(oShadow); + int nCount, nReturn; + + // Use the function to get the closest creature as a target + object oCount = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oCount)) + { + if(GetIsEnemy(oCount, oShadow) && GetIsInMeleeRange(oShadow, oCount)) // Must be an enemy in melee range + { + nCount++; + if (DEBUG) DoDebug("GetShadowCast() nCount "+IntToString(nCount)); + } + //Select the next target within the spell shape. + oCount = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + + if (nCount > 1) return FALSE; + + return TRUE; +} \ No newline at end of file diff --git a/src/include/shd_inc_mystknwn.nss b/src/include/shd_inc_mystknwn.nss new file mode 100644 index 0000000..bc6ecc5 --- /dev/null +++ b/src/include/shd_inc_mystknwn.nss @@ -0,0 +1,801 @@ +//:://///////////////////////////////////////////// +//:: Shadowcasting include: Mysteries Known +//:: shd_inc_mystknwn +//:://///////////////////////////////////////////// +/** @file + Defines functions for adding & removing + Mysteries known. + + Data stored: + + - For each Path list + -- Total number of Mysteries known + -- A modifier value to maximum Mysteries known on this list to account for feats and classes that add Mysteries + -- An array related to Mysteries the knowledge of which is not dependent on character level + --- Each array entry specifies the spells.2da row of the known Mysteries's class-specific entry + -- For each character level on which Mysteries have been gained from this list + --- An array of Mysteries gained on this level + ---- Each array entry specifies the spells.2da row of the known Mysteries's class-specific entry + + @author Stratovarius + @date Created - 2019.02.06 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int MYSTERY_LIST_SHADOWCASTER = 136; +const int MYSTERY_LIST_SHADOWSMITH = 98; + +/// Special Mystery list. Mysteries gained via other sources. +const int MYSTERY_LIST_MISC = CLASS_TYPE_INVALID;//-1; + +const string _MYSTERY_LIST_NAME_BASE = "PRC_MysteryList_"; +const string _MYSTERY_LIST_PATH = "PRC_PathTotal_"; +const string _MYSTERY_LIST_TOTAL_KNOWN = "_TotalKnown"; +const string _MYSTERY_LIST_MODIFIER = "_KnownModifier"; +const string _MYSTERY_LIST_MISC_ARRAY = "_MysteriesKnownMiscArray"; +const string _MYSTERY_LIST_LEVEL_ARRAY = "_MysteriesKnownLevelArray_"; +const string _MYSTERY_LIST_GENERAL_ARRAY = "_MysteriesKnownGeneralArray"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gives the creature the control feats for the given Mystery and marks the Mystery + * in a Mysteries known array. + * If the Mystery's data is already stored in one of the Mysteries known arrays for + * the list or adding the Mystery's data to the array fails, the function aborts. + * + * @param oShadow The creature to gain the Mystery + * @param nList The list the Mystery comes from. One of MYSTERY_LIST_* + * @param n2daRow The 2da row in the lists's 2da file that specifies the Mystery. + * @param bLevelDependent If this is TRUE, the Mystery is tied to a certain level and can + * be lost via level loss. If FALSE, the Mystery is not dependent + * of a level and cannot be lost via level loss. + * @param nLevelToTieTo If bLevelDependent is TRUE, this specifies the level the Mystery + * is gained on. Otherwise, it's ignored. + * The default value (-1) means that the current level of oShadow + * will be used. + * + * @return TRUE if the Mystery was successfully stored and control feats added. + * FALSE otherwise. + */ +int AddMysteryKnown(object oShadow, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1); + +/** + * Removes all Mysteries gained from each list on the given level. + * + * @param oShadow The creature whose Mysteries to remove + * @param nLevel The level to clear + */ +void RemoveMysteriesKnownOnLevel(object oShadow, int nLevel); + +/** + * Gets the value of the Mysteries known modifier, which is a value that is added + * to the 2da-specified maximum Mysteries known to determine the actual maximum. + * + * @param oShadow The creature whose modifier to get + * @param nList The list the maximum Mysteries known from which the modifier + * modifies. One of MYSTERY_LIST_* + */ +int GetKnownMysteriesModifier(object oShadow, int nList); + +/** + * Sets the value of the Mysteries known modifier, which is a value that is added + * to the 2da-specified maximum Mysteries known to determine the actual maximum. + * + * @param oShadow The creature whose modifier to set + * @param nList The list the maximum Mysteries known from which the modifier + * modifies. One of MYSTERY_LIST_* + */ +void SetKnownMysteriesModifier(object oShadow, int nList, int nNewValue); + +/** + * Gets the number of Mysteries a character possesses + * + * @param oShadow The creature whose Mysteries to check + * @param nList The list to check. One of MYSTERY_LIST_* + * + * @return The number of Mysteries known oShadow has from nList + */ +int GetMysteryCount(object oShadow, int nList); + +/** + * Gets the number of Mysteries a character character possesses from a + * specific path + * + * @param oShadow The creature whose Mysteries to check + * @param nPath The path to check. One of PATH_* + * + * @return The number of Mysteries known oShadow has from nPath + */ +int GetMysteryCountByPath(object oShadow, int nPath); + +/** + * Gets the maximum number of Mysteries a character may possess from a given list + * at this time. Calculated based on class levels, feats and a misceallenous + * modifier. + * + * @param oShadow Character to determine maximum Mysteries for + * @param nList MYSTERY_LIST_* of the list to determine maximum Mysteries for + * + * @return Maximum number of Mysteries that oShadow may know from the given list. + */ +int GetMaxMysteryCount(object oShadow, int nList); + +/** + * Determines whether a character has a given Mystery, gained via some Mystery list. + * + * @param nMystery Mystery_* of the Mystery to test + * @param oShadow Character to test for the possession of the Mystery + * @return TRUE if the character has the Mystery, FALSE otherwise + */ +int GetHasMystery(int nMystery, object oShadow = OBJECT_SELF); + +/** + * Gets the highest Mystery level a character may learn at this time. + * + * @param oShadow Character to determine maximum Mystery level for + * @param nClass Class to check against + * @param nType 1 = apprentice, 2 = initiate, 3 = master + * + * @return Maxmimum mystery level + */ +int GetMaxMysteryLevelLearnable(object oShadow, int nClass, int nType); + +/** + * Gets the total number of completed paths for the character + * + * @param oShadow Character to check + */ +int GetCompletedPaths(object oShadow); + +/** + * Adds the feat as a bonus feat gained from having completed a path. Aborts if the feat + * was one the character already had + * + * @param oShadow Character to check + * @param nFeat Feat to add + */ +void AddPathBonusFeat(object oShadow, int nFeat); + +/** + * Returns how many Path Bonus feats the character has + * + * @param oShadow Character to check + */ +int GetPathBonusFeats(object oShadow); + +/** + * Reapplies any missing Path Bonus feats the character has chosen and doesn't have + * + * @param oShadow Character to check + */ +void ReapplyPathBonusFeat(object oShadow); + +/** + * Returns the IP_CONST version of the feat. + * Only works on Bonus Path feats + * + * @param nFeat Feat to itemproperty + */ +int PathFeatToIPFeat(int nFeat); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _MysteryRecurseRemoveArray(object oShadow, string sArrayName, string sMystFile, int nArraySize, int nCurIndex) +{ + if(DEBUG) DoDebug("_MysteryRecurseRemoveArray():\n" + + "oShadow = " + DebugObject2Str(oShadow) + "\n" + + "sArrayName = '" + sArrayName + "'\n" + + "sMystFile = '" + sMystFile + "'\n" + + "nArraySize = " + IntToString(nArraySize) + "\n" + + "nCurIndex = " + IntToString(nCurIndex) + "\n" + ); + + // Determine whether we've already parsed the whole array or not + if(nCurIndex >= nArraySize) + { + if(DEBUG) DoDebug("_MysteryRecurseRemoveArray(): Running itemproperty removal loop."); + // Loop over itemproperties on the skin and remove each match + object oSkin = GetPCSkin(oShadow); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + GetLocalInt(oShadow, "PRC_MystFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))) + ) + { + if(DEBUG) DoDebug("_MysteryRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + DeleteLocalInt(oShadow, "PRC_MystFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))); + } + + ipTest = GetNextItemProperty(oSkin); + } + } + // Still parsing the array + else + { + int nMystID = GetPowerfileIndexFromSpellID(persistant_array_get_int(oShadow, sArrayName, nCurIndex)); + // Set the marker + string sName = "PRC_MystFeatRemovalMarker_" + Get2DACache(sMystFile, "IPFeatID", nMystID); + if(DEBUG) DoDebug("_MysteryRecurseRemoveArray(): Recursing through array, marker set:\n" + sName); + SetLocalInt(oShadow, sName, TRUE); + + string sPathArray = _MYSTERY_LIST_PATH + "_" + Get2DACache(sMystFile, "Path", nMystID); + SetPersistantLocalInt(oShadow, sPathArray, + GetPersistantLocalInt(oShadow, sPathArray) - 1); + + // Recurse to next array index + _MysteryRecurseRemoveArray(oShadow, sArrayName, sMystFile, nArraySize, nCurIndex + 1); + } +} + +void _RemoveMysteryArray(object oShadow, int nList, int nLevel) +{ + if(DEBUG) DoDebug("_RemoveMysteryArray():\n" + + "oShadow = " + DebugObject2Str(oShadow) + "\n" + + "nList = " + IntToString(nList) + "\n" + ); + + string sBase = _MYSTERY_LIST_NAME_BASE + IntToString(nList); + string sArray = sBase + _MYSTERY_LIST_LEVEL_ARRAY + IntToString(nLevel); + int nSize = persistant_array_get_size(oShadow, sArray); + + // Reduce the total by the array size + SetPersistantLocalInt(oShadow, sBase + _MYSTERY_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oShadow, sBase + _MYSTERY_LIST_TOTAL_KNOWN) - nSize + ); + + // Remove each Mystery in the array + _MysteryRecurseRemoveArray(oShadow, sArray, GetAMSDefinitionFileName(nList), nSize, 0); + + // Remove the array itself + persistant_array_delete(oShadow, sArray); +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int AddMysteryKnown(object oShadow, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1) +{ + string sBase = _MYSTERY_LIST_NAME_BASE + IntToString(nList); + string sArray = sBase; + string sPowerFile = GetAMSDefinitionFileName(/*PowerListToClassType(*/nList/*)*/); + string sTestArray; + int i, j, nSize, bReturn; + + // Get the spells.2da row corresponding to the cls_psipw_*.2da row + int nSpells2daRow = StringToInt(Get2DACache(sPowerFile, "SpellID", n2daRow)); + + // Determine the array name. + if(bLevelDependent) + { + // If no level is specified, default to the creature's current level + if(nLevelToTieTo == -1) + nLevelToTieTo = GetHitDice(oShadow); + + sArray += _MYSTERY_LIST_LEVEL_ARRAY + IntToString(nLevelToTieTo); + } + else + { + sArray += _MYSTERY_LIST_GENERAL_ARRAY; + } + + // Make sure the power isn't already in an array. If it is, abort and return FALSE + // Loop over each level array and check that it isn't there. + for(i = 1; i <= GetHitDice(oShadow); i++) + { + sTestArray = sBase + _MYSTERY_LIST_LEVEL_ARRAY + IntToString(i); + if(persistant_array_exists(oShadow, sTestArray)) + { + nSize = persistant_array_get_size(oShadow, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oShadow, sArray, j) == nSpells2daRow) + return FALSE; + } + } + // Check the non-level-dependent array + sTestArray = sBase + _MYSTERY_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oShadow, sTestArray)) + { + nSize = persistant_array_get_size(oShadow, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oShadow, sArray, j) == nSpells2daRow) + return FALSE; + } + + // All checks are made, now start adding the new power + // Create the array if it doesn't exist yet + if(!persistant_array_exists(oShadow, sArray)) + persistant_array_create(oShadow, sArray); + + // Store the power in the array + if(persistant_array_set_int(oShadow, sArray, persistant_array_get_size(oShadow, sArray), nSpells2daRow) != SDL_SUCCESS) + { + if(DEBUG) DoDebug("shd_inc_mystknwn: AddPowerKnown(): ERROR: Unable to add power to known array\n" + + "oShadow = " + DebugObject2Str(oShadow) + "\n" + + "nList = " + IntToString(nList) + "\n" + + "n2daRow = " + IntToString(n2daRow) + "\n" + + "bLevelDependent = " + DebugBool2String(bLevelDependent) + "\n" + + "nLevelToTieTo = " + IntToString(nLevelToTieTo) + "\n" + + "nSpells2daRow = " + IntToString(nSpells2daRow) + "\n" + ); + return FALSE; + } + + // Increment Mysteries known total + SetPersistantLocalInt(oShadow, sBase + _MYSTERY_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oShadow, sBase + _MYSTERY_LIST_TOTAL_KNOWN) + 1 + ); + + // Increment Mysteries known by path + string sPathArray = _MYSTERY_LIST_PATH + "_" + Get2DACache(sPowerFile, "Path", n2daRow); + SetPersistantLocalInt(oShadow, sPathArray, + GetPersistantLocalInt(oShadow, sPathArray) + 1); + + // Give the power's control feats + object oSkin = GetPCSkin(oShadow); + string sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID", n2daRow); + itemproperty ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + // Second power feat, if any + /*sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID2", n2daRow); + if(sPowerFeatIP != "") + { + ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + }*/ + + return TRUE; +} + +void RemoveMysteriesKnownOnLevel(object oShadow, int nLevel) +{ + if(DEBUG) DoDebug("shd_inc_mystknwn: RemoveMysteryKnownOnLevel():\n" + + "oShadow = " + DebugObject2Str(oShadow) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + + string sPostFix = _MYSTERY_LIST_LEVEL_ARRAY + IntToString(nLevel); + // For each Mystery list, determine if an array exists for this level. + if(persistant_array_exists(oShadow, _MYSTERY_LIST_NAME_BASE + IntToString(MYSTERY_LIST_SHADOWCASTER) + sPostFix)) + // If one does exist, clear it + _RemoveMysteryArray(oShadow, MYSTERY_LIST_SHADOWCASTER, nLevel); + + if(persistant_array_exists(oShadow, _MYSTERY_LIST_NAME_BASE + IntToString(MYSTERY_LIST_SHADOWSMITH) + sPostFix)) + _RemoveMysteryArray(oShadow, MYSTERY_LIST_SHADOWSMITH, nLevel); + + if(persistant_array_exists(oShadow, _MYSTERY_LIST_NAME_BASE + IntToString(MYSTERY_LIST_MISC) + sPostFix)) + _RemoveMysteryArray(oShadow, MYSTERY_LIST_MISC, nLevel); + +} + +int GetKnownMysteriesModifier(object oShadow, int nList) +{ + return GetPersistantLocalInt(oShadow, _MYSTERY_LIST_NAME_BASE + IntToString(nList) + _MYSTERY_LIST_MODIFIER); +} + +void SetKnownMysteriesModifier(object oShadow, int nList, int nNewValue) +{ + SetPersistantLocalInt(oShadow, _MYSTERY_LIST_NAME_BASE + IntToString(nList) + _MYSTERY_LIST_MODIFIER, nNewValue); +} + +int GetMysteryCount(object oShadow, int nList) +{ + return GetPersistantLocalInt(oShadow, _MYSTERY_LIST_NAME_BASE + IntToString(nList) + _MYSTERY_LIST_TOTAL_KNOWN); +} + +int GetMysteryCountByPath(object oShadow, int nPath) +{ + return GetPersistantLocalInt(oShadow, _MYSTERY_LIST_PATH + "_" + IntToString(nPath)); +} + +int GetMaxMysteryCount(object oShadow, int nList) +{ + int nMaxMysteries = 0; + + if(nList == MYSTERY_LIST_MISC) + { + if(DEBUG) DoDebug("GetMaxMysteryCount(): ERROR: Using unfinished power list!"); + } + else + { + int nLevel = GetLevelByClass(nList, oShadow); + if (nList == GetPrimaryShadowMagicClass(oShadow)) + nLevel += GetShadowMagicPRCLevels(oShadow); + string sFile = GetAMSKnownFileName(nList); + nMaxMysteries = StringToInt(Get2DACache(sFile, "Mysteries", nLevel - 1)); + + // Calculate feats + + // Add in the custom modifier + nMaxMysteries += GetKnownMysteriesModifier(oShadow, nList); + if(DEBUG) DoDebug("GetMaxMysteryCount(): " + IntToString(nList) + " Mysteries: " + IntToString(nMaxMysteries)); + } + + return nMaxMysteries; +} + +int GetHasMystery(int nMystery, object oShadow = OBJECT_SELF) +{ + if((GetLevelByClass(CLASS_TYPE_SHADOWCASTER, oShadow) + && GetHasFeat(GetClassFeatFromPower(nMystery, CLASS_TYPE_SHADOWCASTER), oShadow) + ) || + (GetLevelByClass(CLASS_TYPE_SHADOWSMITH, oShadow) + && GetHasFeat(GetClassFeatFromPower(nMystery, CLASS_TYPE_SHADOWSMITH), oShadow) + ) + // add new classes here + ) + return TRUE; + return FALSE; +} + +string DebugListKnownMysteries(object oShadow) +{ + string sReturn = "Mysteries known by " + DebugObject2Str(oShadow) + ":\n"; + int i, j, k, numPowerLists = 6; + int nPowerList, nSize; + string sTemp, sArray, sArrayBase, sPowerFile; + // Loop over all power lists + for(i = 1; i <= numPowerLists; i++) + { + // Some padding + sReturn += " "; + // Get the power list for this loop + switch(i) + { + case 1: nPowerList = MYSTERY_LIST_SHADOWCASTER; sReturn += "SHADOWCASTER"; break; + case 2: nPowerList = MYSTERY_LIST_SHADOWSMITH; sReturn += "SHADOWSMITH"; break; + + // This should always be last + case 6: nPowerList = MYSTERY_LIST_MISC; sReturn += "Misceallenous"; break; + } + sReturn += " Mysteries known:\n"; + + // Determine if the character has any Mysteries from this list + sPowerFile = GetAMSDefinitionFileName(nPowerList); + sArrayBase = _MYSTERY_LIST_NAME_BASE + IntToString(nPowerList); + + // Loop over levels + for(j = 1; j <= GetHitDice(oShadow); j++) + { + sArray = sArrayBase + _MYSTERY_LIST_LEVEL_ARRAY + IntToString(j); + if(persistant_array_exists(oShadow, sArray)) + { + sReturn += " Gained on level " + IntToString(j) + ":\n"; + nSize = persistant_array_get_size(oShadow, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oShadow, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + // Non-leveldependent Mysteries + sArray = sArrayBase + _MYSTERY_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oShadow, sArray)) + { + sReturn += " Non-leveldependent:\n"; + nSize = persistant_array_get_size(oShadow, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oShadow, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + return sReturn; +} + +int GetMaxMysteryLevelLearnable(object oShadow, int nClass, int nType) +{ + if(DEBUG) DoDebug("GetMaxMysteryLevelLearnable nType: " + IntToString(nType)); + + // Rules Quote: + // Within a category—Apprentice, Initiate, Master—you must have at least two mysteries of any given level + // before you can take any mysteries of the next higher level. For instance, you must have two 1st-level + // mysteries before you can take any 2nds, and at least two 2nds before you can take any 3rds. + int nMaxLrn, i, nMystLevel, nCount1, nCount2; + string sFeatID; + int nLevel = GetLevelByClass(nClass, oShadow); + if (nClass == GetPrimaryShadowMagicClass(oShadow)) + nLevel += GetShadowMagicPRCLevels(oShadow); + if(DEBUG) DoDebug("GetMaxMysteryLevelLearnable nClass: " + IntToString(nClass)); + string sPowerFile = GetAMSDefinitionFileName(nClass); + if(DEBUG) DoDebug("GetMaxMysteryLevelLearnable sPowerFile: " + sPowerFile); + + if (nType == 3 && nLevel >= 13 && nClass == CLASS_TYPE_SHADOWCASTER) // Master mysteries, must be minimum 13th level + { + for(i = 0; i < GetPRCSwitch(FILE_END_CLASS_POWER) ; i++) + { + sFeatID = Get2DACache(sPowerFile, "FeatID", i); + if(sFeatID != "" && GetHasFeat(StringToInt(sFeatID), oShadow)) // Make sure it's not a blank mystery and that the PC has it + { + nMystLevel = StringToInt(Get2DACache(sPowerFile, "Level", i)); + if (nMystLevel == 7) nCount1++; + else if (nMystLevel == 8) nCount2++; // Don't need to check for 9th because 10th don't exist + } + } + //DoDebug("GetMaxMysteryLevelLearnable nCount1: " + IntToString(nCount1)); + //DoDebug("GetMaxMysteryLevelLearnable nCount2: " + IntToString(nCount2)); + if (nCount2 > 1) // Need to know at least two + nMaxLrn = 9; + else if (nCount1 > 1) // Need to know at least two + nMaxLrn = 8; + else // At 13th level you get access to 7ths regardless + nMaxLrn = 7; + } + else if (nType == 2 && nLevel >= 7 && nClass == CLASS_TYPE_SHADOWCASTER) // Initiate mysteries + { + for(i = 0; i < GetPRCSwitch(FILE_END_CLASS_POWER) ; i++) + { + sFeatID = Get2DACache(sPowerFile, "FeatID", i); + if(sFeatID != "" && GetHasFeat(StringToInt(sFeatID), oShadow)) // Make sure it's not a blank mystery and that the PC has it + { + nMystLevel = StringToInt(Get2DACache(sPowerFile, "Level", i)); + if (nMystLevel == 4) nCount1++; + else if (nMystLevel == 5) nCount2++; // Don't need to check for 6th because 7th is triggered by level + } + } + //DoDebug("GetMaxMysteryLevelLearnable nCount1: " + IntToString(nCount1)); + //DoDebug("GetMaxMysteryLevelLearnable nCount2: " + IntToString(nCount2)); + if (nCount2 > 1) // Need to know at least two + nMaxLrn = 6; + else if (nCount1 > 1) // Need to know at least two + nMaxLrn = 5; + else // At 7th level you get access to 4ths regardless + nMaxLrn = 4; + } + else // Apprentice mysteries + { + for(i = 0; i < GetPRCSwitch(FILE_END_CLASS_POWER) ; i++) + { + sFeatID = Get2DACache(sPowerFile, "FeatID", i); + if(sFeatID != "" && GetHasFeat(StringToInt(sFeatID), oShadow)) // Make sure it's not a blank mystery and that the PC has it + { + nMystLevel = StringToInt(Get2DACache(sPowerFile, "Level", i)); + if (nMystLevel == 1) nCount1++; + else if (nMystLevel == 2) nCount2++; // Don't need to check for 3rd because 4th is triggered by level + } + } + //DoDebug("GetMaxMysteryLevelLearnable nCount1: " + IntToString(nCount1)); + //DoDebug("GetMaxMysteryLevelLearnable nCount2: " + IntToString(nCount2)); + if (nCount2 > 1) // Need to know at least two + nMaxLrn = 3; + else if (nCount1 > 1) // Need to know at least two + nMaxLrn = 2; + else // At 1st level you get access to 1st regardless + nMaxLrn = 1; + } + + //DoDebug("GetMaxMysteryLevelLearnable nMaxLrn: " + IntToString(nMaxLrn)); + + return nMaxLrn; +} + +int GetCompletedPaths(object oShadow) +{ + int i, nReturn, nPath; + for(i = 0; i < 24; i++) + { + //if(DEBUG) DoDebug("GetCompletedPaths(): i " + IntToString(i)); + nPath = GetMysteryCountByPath(oShadow, i); + //if(DEBUG) DoDebug("GetCompletedPaths(): nPath " + IntToString(nPath)); + if(nPath == 3) // Completed Path + nReturn++; //Number of completed paths + } + + //if(DEBUG) DoDebug("GetCompletedPaths(): nReturn " + IntToString(nReturn)); + // Return the total + return nReturn; +} + +void AddPathBonusFeat(object oShadow, int nFeat) +{ + if (GetHasFeat(nFeat, oShadow)) return; // No point adding something they've got + + int nCount, i; + for(i = 1; i < 7 ; i++) + { + if (GetPersistantLocalInt(oShadow, "PathBonus"+IntToString(i)) > 0) // Make sure to only fill empty ones + nCount++; + } + + SetPersistantLocalInt(oShadow, "PathBonus"+IntToString(nCount + 1), nFeat); + + // Give the power's control feats + object oSkin = GetPCSkin(oShadow); + int nIPFeat = PathFeatToIPFeat(nFeat); + itemproperty ipFeat = PRCItemPropertyBonusFeat(nIPFeat); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); +} + +int GetPathBonusFeats(object oShadow) +{ + int nCount, i; + for(i = 1; i < 7 ; i++) + { + if (GetPersistantLocalInt(oShadow, "PathBonus"+IntToString(i)) > 0) // Count filled ones + nCount++; + } + + return nCount; +} + +void ReapplyPathBonusFeat(object oShadow) +{ + int nFeat, i; + object oSkin = GetPCSkin(oShadow); + for(i = 1; i < 7 ; i++) + { + nFeat = GetPersistantLocalInt(oShadow, "PathBonus"+IntToString(i)); + if (nFeat > 0 && !GetHasFeat(nFeat, oShadow))// Only apply if it's a valid feat they don't have + { + int nIPFeat = PathFeatToIPFeat(nFeat); + itemproperty ipFeat = PRCItemPropertyBonusFeat(nIPFeat); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + } +} + +int PathFeatToIPFeat(int nFeat) +{ + int nIPFeat = -1; + switch(nFeat) + { + case FEAT_PATH_FOCUS_CLOAK_SHADOWS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_CLOAK_SHADOWS ; break; + case FEAT_PATH_FOCUS_DARK_TERRAIN : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_DARK_TERRAIN ; break; + case FEAT_PATH_FOCUS_EBON_WHISPERS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_EBON_WHISPERS ; break; + case FEAT_PATH_FOCUS_EYES_DARKNESS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_EYES_DARKNESS ; break; + case FEAT_PATH_FOCUS_SHUTTERS_CLOUDS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_SHUTTERS_CLOUDS ; break; + case FEAT_PATH_FOCUS_TOUCH_TWILIGHT : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_TOUCH_TWILIGHT ; break; + case FEAT_PATH_FOCUS_UMBRAL_MIND : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_UMBRAL_MIND ; break; + case FEAT_PATH_FOCUS_BLACK_MAGIC : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_BLACK_MAGIC ; break; + case FEAT_PATH_FOCUS_BODY_SOUL : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_BODY_SOUL ; break; + case FEAT_PATH_FOCUS_DARK_REFLECTIONS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_DARK_REFLECTIONS ; break; + case FEAT_PATH_FOCUS_EBON_ROADS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_EBON_ROADS ; break; + case FEAT_PATH_FOCUS_ELEMENTAL_SHADOWS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_ELEMENTAL_SHADOWS ; break; + case FEAT_PATH_FOCUS_UNBINDING_SHADE : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_UNBINDING_SHADE ; break; + case FEAT_PATH_FOCUS_VEIL_SHADOWS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_VEIL_SHADOWS ; break; + case FEAT_PATH_FOCUS_BREATH_TWILIGHT : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_BREATH_TWILIGHT ; break; + case FEAT_PATH_FOCUS_DARK_METAMORPHOSIS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_DARK_METAMORPHOSIS ; break; + case FEAT_PATH_FOCUS_EBON_WALLS : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_EBON_WALLS ; break; + case FEAT_PATH_FOCUS_EYES_NIGHT_SKY : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_EYES_NIGHT_SKY ; break; + case FEAT_PATH_FOCUS_HEART_SOUL : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_HEART_SOUL ; break; + case FEAT_PATH_FOCUS_SHADOW_CALLING : nIPFeat = IP_CONST_FEAT_PATH_FOCUS_SHADOW_CALLING ; break; + case FEAT_GREATER_PATH_FOCUS_CLOAK_SHADOWS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_CLOAK_SHADOWS ; break; + case FEAT_GREATER_PATH_FOCUS_DARK_TERRAIN : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_DARK_TERRAIN ; break; + case FEAT_GREATER_PATH_FOCUS_EBON_WHISPERS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_EBON_WHISPERS ; break; + case FEAT_GREATER_PATH_FOCUS_EYES_DARKNESS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_EYES_DARKNESS ; break; + case FEAT_GREATER_PATH_FOCUS_SHUTTERS_CLOUDS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_SHUTTERS_CLOUDS ; break; + case FEAT_GREATER_PATH_FOCUS_TOUCH_TWILIGHT : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_TOUCH_TWILIGHT ; break; + case FEAT_GREATER_PATH_FOCUS_UMBRAL_MIND : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_UMBRAL_MIND ; break; + case FEAT_GREATER_PATH_FOCUS_BLACK_MAGIC : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_BLACK_MAGIC ; break; + case FEAT_GREATER_PATH_FOCUS_BODY_SOUL : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_BODY_SOUL ; break; + case FEAT_GREATER_PATH_FOCUS_DARK_REFLECTIONS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_DARK_REFLECTIONS ; break; + case FEAT_GREATER_PATH_FOCUS_EBON_ROADS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_EBON_ROADS ; break; + case FEAT_GREATER_PATH_FOCUS_ELEMENTAL_SHADOWS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_ELEMENTAL_SHADOWS ; break; + case FEAT_GREATER_PATH_FOCUS_UNBINDING_SHADE : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_UNBINDING_SHADE ; break; + case FEAT_GREATER_PATH_FOCUS_VEIL_SHADOWS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_VEIL_SHADOWS ; break; + case FEAT_GREATER_PATH_FOCUS_BREATH_TWILIGHT : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_BREATH_TWILIGHT ; break; + case FEAT_GREATER_PATH_FOCUS_DARK_METAMORPHOSIS: nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_DARK_METAMORPHOSIS; break; + case FEAT_GREATER_PATH_FOCUS_EBON_WALLS : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_EBON_WALLS ; break; + case FEAT_GREATER_PATH_FOCUS_EYES_NIGHT_SKY : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_EYES_NIGHT_SKY ; break; + case FEAT_GREATER_PATH_FOCUS_HEART_SOUL : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_HEART_SOUL ; break; + case FEAT_GREATER_PATH_FOCUS_SHADOW_CALLING : nIPFeat = IP_CONST_FEAT_GREATER_PATH_FOCUS_SHADOW_CALLING ; break; + case FEAT_NOCTURNAL_CASTER_CLOAK_SHADOWS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_CLOAK_SHADOWS ; break; + case FEAT_NOCTURNAL_CASTER_DARK_TERRAIN : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_DARK_TERRAIN ; break; + case FEAT_NOCTURNAL_CASTER_EBON_WHISPERS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_EBON_WHISPERS ; break; + case FEAT_NOCTURNAL_CASTER_EYES_DARKNESS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_EYES_DARKNESS ; break; + case FEAT_NOCTURNAL_CASTER_SHUTTERS_CLOUDS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_SHUTTERS_CLOUDS ; break; + case FEAT_NOCTURNAL_CASTER_TOUCH_TWILIGHT : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_TOUCH_TWILIGHT ; break; + case FEAT_NOCTURNAL_CASTER_UMBRAL_MIND : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_UMBRAL_MIND ; break; + case FEAT_NOCTURNAL_CASTER_BLACK_MAGIC : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_BLACK_MAGIC ; break; + case FEAT_NOCTURNAL_CASTER_BODY_SOUL : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_BODY_SOUL ; break; + case FEAT_NOCTURNAL_CASTER_DARK_REFLECTIONS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_DARK_REFLECTIONS ; break; + case FEAT_NOCTURNAL_CASTER_EBON_ROADS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_EBON_ROADS ; break; + case FEAT_NOCTURNAL_CASTER_ELEMENTAL_SHADOWS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_ELEMENTAL_SHADOWS ; break; + case FEAT_NOCTURNAL_CASTER_UNBINDING_SHADE : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_UNBINDING_SHADE ; break; + case FEAT_NOCTURNAL_CASTER_VEIL_SHADOWS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_VEIL_SHADOWS ; break; + case FEAT_NOCTURNAL_CASTER_BREATH_TWILIGHT : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_BREATH_TWILIGHT ; break; + case FEAT_NOCTURNAL_CASTER_DARK_METAMORPHOSIS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_DARK_METAMORPHOSIS ; break; + case FEAT_NOCTURNAL_CASTER_EBON_WALLS : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_EBON_WALLS ; break; + case FEAT_NOCTURNAL_CASTER_EYES_NIGHT_SKY : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_EYES_NIGHT_SKY ; break; + case FEAT_NOCTURNAL_CASTER_HEART_SOUL : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_HEART_SOUL ; break; + case FEAT_NOCTURNAL_CASTER_SHADOW_CALLING : nIPFeat = IP_CONST_FEAT_NOCTURNAL_CASTER_SHADOW_CALLING ; break; + case FEAT_FAV_MYST_BENDPERSPECTIVE : nIPFeat = IP_CONST_FEAT_FAV_MYST_BENDPERSPECTIVE ; break; + case FEAT_FAV_MYST_CARPETSHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_CARPETSHADOW ; break; + case FEAT_FAV_MYST_DUSKANDDAWN : nIPFeat = IP_CONST_FEAT_FAV_MYST_DUSKANDDAWN ; break; + case FEAT_FAV_MYST_LIFEFADES : nIPFeat = IP_CONST_FEAT_FAV_MYST_LIFEFADES ; break; + case FEAT_FAV_MYST_MESMERIZINGSHADE : nIPFeat = IP_CONST_FEAT_FAV_MYST_MESMERIZINGSHADE ; break; + case FEAT_FAV_MYST_STEELSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_STEELSHADOWS ; break; + case FEAT_FAV_MYST_VOICEOFSHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_VOICEOFSHADOW ; break; + case FEAT_FAV_MYST_BLACKFIRE : nIPFeat = IP_CONST_FEAT_FAV_MYST_BLACKFIRE ; break; + case FEAT_FAV_MYST_CONGRESSSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_CONGRESSSHADOWS ; break; + case FEAT_FAV_MYST_FLESHFAILS : nIPFeat = IP_CONST_FEAT_FAV_MYST_FLESHFAILS ; break; + case FEAT_FAV_MYST_PIERCINGSIGHT : nIPFeat = IP_CONST_FEAT_FAV_MYST_PIERCINGSIGHT ; break; + case FEAT_FAV_MYST_SHADOWSKIN : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWSKIN ; break; + case FEAT_FAV_MYST_SIGHTECLIPSED : nIPFeat = IP_CONST_FEAT_FAV_MYST_SIGHTECLIPSED ; break; + case FEAT_FAV_MYST_THOUGHTSSHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_THOUGHTSSHADOW ; break; + case FEAT_FAV_MYST_AFRAIDOFTHEDARK : nIPFeat = IP_CONST_FEAT_FAV_MYST_AFRAIDOFTHEDARK ; break; + case FEAT_FAV_MYST_CLINGINGDARKNESS : nIPFeat = IP_CONST_FEAT_FAV_MYST_CLINGINGDARKNESS ; break; + case FEAT_FAV_MYST_DANCINGSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_DANCINGSHADOWS ; break; + case FEAT_FAV_MYST_FLICKER : nIPFeat = IP_CONST_FEAT_FAV_MYST_FLICKER ; break; + case FEAT_FAV_MYST_KILLINGSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_KILLINGSHADOWS ; break; + case FEAT_FAV_MYST_SHARPSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHARPSHADOWS ; break; + case FEAT_FAV_MYST_UMBRALTOUCH : nIPFeat = IP_CONST_FEAT_FAV_MYST_UMBRALTOUCH ; break; + case FEAT_FAV_MYST_AURAOFSHADE : nIPFeat = IP_CONST_FEAT_FAV_MYST_AURAOFSHADE ; break; + case FEAT_FAV_MYST_BOLSTER : nIPFeat = IP_CONST_FEAT_FAV_MYST_BOLSTER ; break; + case FEAT_FAV_MYST_SHADOWEVOCATION : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWEVOCATION ; break; + case FEAT_FAV_MYST_SHADOWVISION : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWVISION ; break; + case FEAT_FAV_MYST_SHADOWSFADE : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWSFADE ; break; + case FEAT_FAV_MYST_STEPINTOSHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_STEPINTOSHADOW ; break; + case FEAT_FAV_MYST_WARPSPELL : nIPFeat = IP_CONST_FEAT_FAV_MYST_WARPSPELL ; break; + case FEAT_FAV_MYST_CURTAINSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_CURTAINSHADOWS ; break; + case FEAT_FAV_MYST_DARKAIR : nIPFeat = IP_CONST_FEAT_FAV_MYST_DARKAIR ; break; + case FEAT_FAV_MYST_ECHOSPELL : nIPFeat = IP_CONST_FEAT_FAV_MYST_ECHOSPELL ; break; + case FEAT_FAV_MYST_FEIGNLIFE : nIPFeat = IP_CONST_FEAT_FAV_MYST_FEIGNLIFE ; break; + case FEAT_FAV_MYST_LANGUOR : nIPFeat = IP_CONST_FEAT_FAV_MYST_LANGUOR ; break; + case FEAT_FAV_MYST_PASSINTOSHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_PASSINTOSHADOW ; break; + case FEAT_FAV_MYST_UNRAVELDWEOMER : nIPFeat = IP_CONST_FEAT_FAV_MYST_UNRAVELDWEOMER ; break; + case FEAT_FAV_MYST_FLOODSHADOWS : nIPFeat = IP_CONST_FEAT_FAV_MYST_FLOODSHADOWS ; break; + case FEAT_FAV_MYST_GREATERSHADOWEVOCATION : nIPFeat = IP_CONST_FEAT_FAV_MYST_GREATERSHADOWEVOCATION ; break; + case FEAT_FAV_MYST_SHADOWINVESTITURE : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWINVESTITURE ; break; + case FEAT_FAV_MYST_SHADOWSTORM : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWSTORM ; break; + case FEAT_FAV_MYST_SHADOWSFADE_GREATER : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWSFADE_GREATER ; break; + case FEAT_FAV_MYST_UNVEIL : nIPFeat = IP_CONST_FEAT_FAV_MYST_UNVEIL ; break; + case FEAT_FAV_MYST_VOYAGESHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_VOYAGESHADOW ; break; + case FEAT_FAV_MYST_DARKSOUL : nIPFeat = IP_CONST_FEAT_FAV_MYST_DARKSOUL ; break; + case FEAT_FAV_MYST_EPHEMERALIMAGE : nIPFeat = IP_CONST_FEAT_FAV_MYST_EPHEMERALIMAGE ; break; + case FEAT_FAV_MYST_LIFEFADESGREATER : nIPFeat = IP_CONST_FEAT_FAV_MYST_LIFEFADESGREATER ; break; + case FEAT_FAV_MYST_PRISONNIGHT : nIPFeat = IP_CONST_FEAT_FAV_MYST_PRISONNIGHT ; break; + case FEAT_FAV_MYST_UMBRALSERVANT : nIPFeat = IP_CONST_FEAT_FAV_MYST_UMBRALSERVANT ; break; + case FEAT_FAV_MYST_TRUTHREVEALED : nIPFeat = IP_CONST_FEAT_FAV_MYST_TRUTHREVEALED ; break; + case FEAT_FAV_MYST_FARSIGHT : nIPFeat = IP_CONST_FEAT_FAV_MYST_FARSIGHT ; break; + case FEAT_FAV_MYST_GRFLESHFAILS : nIPFeat = IP_CONST_FEAT_FAV_MYST_GRFLESHFAILS ; break; + case FEAT_FAV_MYST_SHADOWPLAGUE : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWPLAGUE ; break; + case FEAT_FAV_MYST_SOULPUPPET : nIPFeat = IP_CONST_FEAT_FAV_MYST_SOULPUPPET ; break; + case FEAT_FAV_MYST_TOMBNIGHT : nIPFeat = IP_CONST_FEAT_FAV_MYST_TOMBNIGHT ; break; + case FEAT_FAV_MYST_UMBRALBODY : nIPFeat = IP_CONST_FEAT_FAV_MYST_UMBRALBODY ; break; + case FEAT_FAV_MYST_ARMYSHADOW : nIPFeat = IP_CONST_FEAT_FAV_MYST_ARMYSHADOW ; break; + case FEAT_FAV_MYST_CONSUMEESSENCE : nIPFeat = IP_CONST_FEAT_FAV_MYST_CONSUMEESSENCE ; break; + case FEAT_FAV_MYST_EPHEMERALSTORM : nIPFeat = IP_CONST_FEAT_FAV_MYST_EPHEMERALSTORM ; break; + case FEAT_FAV_MYST_REFLECTIONS : nIPFeat = IP_CONST_FEAT_FAV_MYST_REFLECTIONS ; break; + case FEAT_FAV_MYST_SHADOWSURGE : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWSURGE ; break; + case FEAT_FAV_MYST_SHADOWTIME : nIPFeat = IP_CONST_FEAT_FAV_MYST_SHADOWTIME ; break; + case FEAT_SHADOW_CAST : nIPFeat = IP_CONST_FEAT_SHADOW_CAST ; break; + case FEAT_EMPOWER_MYSTERY : nIPFeat = IP_CONST_FEAT_EMPOWER_MYSTERY ; break; + case FEAT_EXTEND_MYSTERY : nIPFeat = IP_CONST_FEAT_EXTEND_MYSTERY ; break; + case FEAT_MAXIMIZE_MYSTERY : nIPFeat = IP_CONST_FEAT_MAXIMIZE_MYSTERY ; break; + case FEAT_QUICKEN_MYSTERY : nIPFeat = IP_CONST_FEAT_QUICKEN_MYSTERY ; break; + case FEAT_STILL_MYSTERY : nIPFeat = IP_CONST_FEAT_STILL_MYSTERY ; break; + } + + return nIPFeat; +} \ No newline at end of file diff --git a/src/include/shd_inc_shdfunc.nss b/src/include/shd_inc_shdfunc.nss new file mode 100644 index 0000000..63eb943 --- /dev/null +++ b/src/include/shd_inc_shdfunc.nss @@ -0,0 +1,756 @@ +//:://///////////////////////////////////////////// +//:: Shadowcasting main include: Miscellaneous +//:: shd_inc_shdfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to Shadowcasting. + + Also acts as inclusion nexus for the general + shadowcasting includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2019.02.08 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/10/19 + +//:: Test Void +// void main (){} + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's mystery list the currently being shadowcast + * mystery is shadowcast from. + * + * @param oShadow A creature shadowcasting a mystery at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetShadowcastingClass(object oShadow = OBJECT_SELF); + +/** + * Determines the given creature's Shadowcaster level. If a class is specified, + * then returns the Shadowcaster level for that class. Otherwise, returns + * the Shadowcaster level for the currently active mystery. + * + * @param oShadow The creature whose Shadowcaster level to determine + * @param nSpecificClass The class to determine the creature's Shadowcaster + * level in. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * Shadowcaster level in regards to an ongoing mystery + * is determined instead. + * @return The Shadowcaster level + */ +int GetShadowcasterLevel(object oShadow = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID); + +/** + * Determines whether a given creature uses ShadowMagic. + * Requires either levels in a ShadowMagic-related class or + * natural ShadowMagic ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use ShadowMagics, FALSE otherwise. + */ +int GetIsShadowMagicUser(object oCreature); + +/** + * Determines the given creature's highest unmodified Shadowcaster level among its + * shadowcasting classes. + * + * @param oCreature Creature whose highest Shadowcaster level to determine + * @return The highest unmodified Shadowcaster level the creature can have + */ +int GetHighestShadowcasterLevel(object oCreature); + +/** + * Determines whether a given class is a ShadowMagic-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a ShadowMagic-related class, FALSE otherwise + */ +int GetIsShadowMagicClass(int nClass); + +/** + * Gets the level of the mystery being currently shadowcast or the level + * of the mystery ID passed to it. + * + * @param oShadow The creature currently shadowcasting a mystery + * @return The level of the mystery being shadowcast + */ +int GetMysteryLevel(object oShadow, int nMystId = 0); + +/** + * Returns the name of the Path + * + * @param nPath PATH_* to name + */ +string GetPathName(int nPath); + +/** + * Returns the Path the mystery is in + * @param nMystId Mystery to check + * + * @return PATH_* + */ +int GetPathByMystery(int nMystId); + +/** + * Returns true or false if the character has Path + * focus in the chosen path + * @param oShadow Person to check + * @param nPath Path to check + * + * @return TRUE or FALSE + */ +int GetHasPathFocus(object oShadow, int nPath); + +/** + * Calculates how many shadowcaster levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added shadowcaster levels for + * @return The number of shadowcaster levels gained + */ +int GetShadowMagicPRCLevels(object oShadow); + +/** + * Determines which of the character's classes is their highest or first + * shadowcasting class, if any. This is the one which gains shadowcaster + * level raise benefits from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first shadowcasting class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryShadowMagicClass(object oCreature = OBJECT_SELF); + +/** + * Determines the position of a creature's first shadowcasting class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first shadowcasting class {1, 2, 3} or 0 if + * the creature possesses no levels in shadowcasting classes. + */ +int GetFirstShadowMagicClassPosition(object oCreature = OBJECT_SELF); + +/** + * Returns ability score needed to Shadowcast + * Type 1 is score to cast, Type 2 is score for DC + * + * @param nClass The class to check + * @return ABILITY_* + */ +int GetShadowAbilityOfClass(int nClass, int nType); + +/** + * Calculates the DC of the Mystery being currently shadowcast. + * + * WARNING: Return value is not defined when a mystery isn't being shadowcast. + * + */ +int GetShadowcasterDC(object oShadow = OBJECT_SELF); + +/** + * Calculates the SpellPen of the Mystery being currently shadowcast. + * Whether a Mystery is supernatural or not is checked in EvaluateMystery + * + * Currently just a placeholder returning GetShadowcasterLevel + */ +int ShadowSRPen(object oShadow, int nShadowcasterLevel); + +/** + * Stores a mystery structure as a set of local variables. If + * a structure was already stored with the same name on the same object, + * it is overwritten. + * + * @param oObject The object on which to store the structure + * @param sName The name under which to store the structure + * @param myst The mystery structure to store + */ +void SetLocalMystery(object oObject, string sName, struct mystery myst); + +/** + * Retrieves a previously stored mystery structure. If no structure is stored + * by the given name, the structure returned is empty. + * + * @param oObject The object from which to retrieve the structure + * @param sName The name under which the structure is stored + * @return The structure built from local variables stored on oObject under sName + */ +struct mystery GetLocalMystery(object oObject, string sName); + +/** + * Returns the boost to caster level from feats + * + * @param oShadow The caster + * @param nMyst The mystery being cast + * @return Total bonus to caster level + */ +int ShadowcastingFeats(object oShadow, int nMyst); + +/** + * Returns the boost to DC from nocturnal caster + * + * @param oShadow The caster + * @param nPath The path to check for + * @return Total bonus to caster level + */ +int GetHasNocturnal(object oShadow, int nPath); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_alterations" +#include "shd_inc_myst" +#include "shd_inc_mystknwn" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetShadowcastingClass(object oShadow = OBJECT_SELF) +{ + int nReturn = GetLocalInt(oShadow, PRC_SHADOWCASTING_CLASS) - 1; + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcastingClass: GetShadowcastingClass value is "+IntToString(nReturn), oShadow); + return nReturn; +} + +int GetShadowcasterLevel(object oShadow = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID) +{ + int nAdjust = GetLocalInt(oShadow, PRC_CASTERLEVEL_ADJUSTMENT); + int nLevel = GetLocalInt(oShadow, PRC_CASTERLEVEL_OVERRIDE); + int nMyst = PRCGetSpellId(); // The fact that this will return 0 sometimes is relied upon + if (GetIsFundamental(nMyst)) nSpecificClass = CLASS_TYPE_SHADOWCASTER; + + // For when you want to assign the caster level. + if(nLevel) + { + if(DEBUG) SendMessageToPC(oShadow, "GetShadowcasterLevel(): Forced-level shadowcasting at level " + IntToString(nLevel)); + //DelayCommand(1.0, DeleteLocalInt(oShadow, PRC_CASTERLEVEL_OVERRIDE)); + return nLevel + nAdjust; + } + + if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: "+GetName(oShadow)+" is a "+IntToString(nSpecificClass), oShadow); + // The function user needs to know the character's Shadowcaster level in a specific class + // instead of whatever the character last shadowcast a mystery as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: Class is Valid", oShadow); + if(GetIsShadowMagicClass(nSpecificClass)) + { + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: Class is Shadow Magic Class", oShadow); + // Shadowcaster level is class level + prestige + nLevel = GetLevelByClass(nSpecificClass, oShadow); + if(nLevel) + { + nLevel += GetShadowMagicPRCLevels(oShadow); + nLevel += ShadowcastingFeats(oShadow, nMyst); + if (GetLocalInt(oShadow, "CaptureMagic")) + { + nLevel += GetLocalInt(oShadow, "CaptureMagic"); + DeleteLocalInt(oShadow, "CaptureMagic"); + } + if (GetLocalInt(oShadow, "EldritchDisrupt")) + nLevel -= 4; + if (GetLocalInt(oShadow, "EldritchVortex")) + nLevel -= 4; + } + } + } + else if(GetShadowcastingClass(oShadow) != -1) + { + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass", oShadow); + nLevel = GetLevelByClass(GetShadowcastingClass(oShadow), oShadow); + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass level "+IntToString(nLevel), oShadow); + nLevel += GetShadowMagicPRCLevels(oShadow); + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass prestige level "+IntToString(nLevel), oShadow); + nLevel += ShadowcastingFeats(oShadow, nMyst); + //if (DEBUG) FloatingTextStringOnCreature("GetShadowcasterLevel: GetShadowcastingClass feat level "+IntToString(nLevel), oShadow); + if (GetLocalInt(oShadow, "CaptureMagic")) + { + nLevel += GetLocalInt(oShadow, "CaptureMagic"); + DeleteLocalInt(oShadow, "CaptureMagic"); + } + if (GetLocalInt(oShadow, "EldritchDisrupt")) + nLevel -= 4; + if (GetLocalInt(oShadow, "EldritchVortex")) + nLevel -= 4; + } + + if(DEBUG) FloatingTextStringOnCreature("Shadowcaster Level: " + IntToString(nLevel), oShadow, FALSE); + + return nLevel + nAdjust; +} + +int GetIsShadowMagicUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_SHADOWCASTER, oCreature) + || GetLevelByClass(CLASS_TYPE_SHADOWSMITH, oCreature)); +} + +int GetHighestShadowcasterLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetShadowcasterLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestShadowcasterLevel(object oCreature) +{ + return PRCMax(PRCMax(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetShadowcasterLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetShadowcasterLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetShadowcasterLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsShadowMagicClass(int nClass) +{ + return nClass == CLASS_TYPE_SHADOWCASTER + || nClass == CLASS_TYPE_SHADOWSMITH; +} + +int GetMysteryLevel(object oShadow, int nMystId = 0) +{ + if (nMystId > 0) return StringToInt(lookup_spell_innate(nMystId)); + int nLevel = GetLocalInt(oShadow, PRC_MYSTERY_LEVEL); + if (nLevel > 0) return nLevel; + + return 0; +} + +string GetPathName(int nPath) +{ + int nStrRef; + switch(nPath) + { + /* case PATH_DESERT_WIND: nStrRef = 16829714; break; + case PATH_DEVOTED_SPIRIT: nStrRef = 16829715; break; + case PATH_DIAMOND_MIND: nStrRef = 16829716; break; + case PATH_IRON_HEART: nStrRef = 16829717; break; + case PATH_SETTING_SUN: nStrRef = 16829718; break; + case PATH_SHADOW_HAND: nStrRef = 16829719; break; + case PATH_STONE_DRAGON: nStrRef = 16829720; break; + case PATH_TIGER_CLAW: nStrRef = 16829721; break; + case PATH_WHITE_RAVEN: nStrRef = 16829722; break;*/ + } + return GetStringByStrRef(nStrRef); +} + +int GetPathByMystery(int nMystId) +{ + // Shadowcaster has every mystery ever, so this is just the easy way out. + int i = GetPowerfileIndexFromRealSpellID(nMystId); + string sClass = GetAMSDefinitionFileName(CLASS_TYPE_SHADOWCASTER); + int nReturn = StringToInt(Get2DACache(sClass, "Path", i)); + /*if (DEBUG) DoDebug("GetPathByMystery() i "+IntToString(i)); + if (DEBUG) DoDebug("GetPathByMystery() sClass "+sClass); + if (DEBUG) DoDebug("GetPathByMystery() nReturn "+IntToString(nReturn)); */ + + return nReturn; +} + +int GetShadowMagicPRCLevels(object oShadow) +{ + int nLevel; + int nShadowClass = GetPrimaryShadowMagicClass(oShadow); + + if (nShadowClass == CLASS_TYPE_SHADOWCASTER) + { + if(GetHasFeat(FEAT_AOTS_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oShadow) + 1) / 2; + + if(GetHasFeat(FEAT_ALIENIST_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_ALIENIST, oShadow); + + if(GetHasFeat(FEAT_CHILDNIGHT_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oShadow) -1; //:: No increase @ 1st level + + if(GetHasFeat(FEAT_ASMODEUS_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oShadow) + 1) / 2; + + if(GetHasFeat(FEAT_DSONG_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oShadow) + 1) / 2; + + if(GetHasFeat(FEAT_ELESAVANT_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oShadow); + + if(GetHasFeat(FEAT_MASTERSHADOW_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oShadow) -1; //:: No increase @ 1st level + + if(GetHasFeat(FEAT_MYSTICTHEURGE_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oShadow); + + if(GetHasFeat(FEAT_NOCTUMANCER_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oShadow); + + if(GetHasFeat(FEAT_OLLAM_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_OLLAM, oShadow) + 1 / 2); + + if(GetHasFeat(FEAT_TIAMAT_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oShadow) + 1 / 2); + + if(GetHasFeat(FEAT_ORCUS_MYSTERY_SHADOWCASTER, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_ORCUS, oShadow) + 1 / 2); + } + + if (nShadowClass == CLASS_TYPE_SHADOWSMITH) + { + if(GetHasFeat(FEAT_AOTS_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_ACOLYTE, oShadow) + 1) / 2; + + if(GetHasFeat(FEAT_ALIENIST_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_ALIENIST, oShadow); + + if(GetHasFeat(FEAT_CHILDNIGHT_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oShadow) -1; //:: No increase @ 1st level + + if(GetHasFeat(FEAT_ASMODEUS_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_DISCIPLE_OF_ASMODEUS, oShadow) + 1) / 2; + + if(GetHasFeat(FEAT_DSONG_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_DRAGONSONG_LYRIST, oShadow) + 1) / 2; + + if(GetHasFeat(FEAT_ELESAVANT_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oShadow); + + if(GetHasFeat(FEAT_MASTERSHADOW_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oShadow) -1; //:: No increase @ 1st level + + if(GetHasFeat(FEAT_MYSTICTHEURGE_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_MYSTIC_THEURGE, oShadow); + + if(GetHasFeat(FEAT_NOCTUMANCER_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oShadow); + + if(GetHasFeat(FEAT_OLLAM_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_OLLAM, oShadow) + 1 / 2); + + if(GetHasFeat(FEAT_TIAMAT_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_TALON_OF_TIAMAT, oShadow) + 1 / 2); + + if(GetHasFeat(FEAT_ORCUS_MYSTERY_SHADOWSMITH, oShadow)) + nLevel += (GetLevelByClass(CLASS_TYPE_ORCUS, oShadow) + 1 / 2); + } + + return nLevel; +} + + +/* int GetShadowMagicPRCLevels(object oShadow) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_NOCTUMANCER, oShadow); + + // These two don't add at 1st level + if (GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_CHILD_OF_NIGHT, oShadow) - 1; + if (GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oShadow)) + nLevel += GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oShadow) - 1; + + return nLevel; +} */ + +int GetPrimaryShadowMagicClass(object oCreature = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nShadowMagicPos = GetFirstShadowMagicClassPosition(oCreature); + if (!nShadowMagicPos) return CLASS_TYPE_INVALID; // no Blade Magic shadowcasting class + + nClass = GetClassByPosition(nShadowMagicPos, oCreature); + } + else + { + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsShadowMagicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsShadowMagicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsShadowMagicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsShadowMagicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsShadowMagicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsShadowMagicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsShadowMagicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsShadowMagicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstShadowMagicClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsShadowMagicClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsShadowMagicClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsShadowMagicClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsShadowMagicClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsShadowMagicClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsShadowMagicClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsShadowMagicClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsShadowMagicClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int GetHasPathFocus(object oShadow, int nPath) +{ + //if (DEBUG) DoDebug("GetHasPathFocus() nPath "+IntToString(nPath)); + int nFocus, nGRFocus, nReturn; + switch(nPath) + { + case PATH_CLOAK_SHADOWS: nFocus = FEAT_PATH_FOCUS_CLOAK_SHADOWS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_CLOAK_SHADOWS ; break; + case PATH_DARK_TERRAIN: nFocus = FEAT_PATH_FOCUS_DARK_TERRAIN ; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARK_TERRAIN ; break; + case PATH_EBON_WHISPERS: nFocus = FEAT_PATH_FOCUS_EBON_WHISPERS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EBON_WHISPERS ; break; + case PATH_EYES_DARKNESS: nFocus = FEAT_PATH_FOCUS_EYES_DARKNESS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EYES_DARKNESS ; break; + case PATH_SHUTTERS_CLOUDS: nFocus = FEAT_PATH_FOCUS_SHUTTERS_CLOUDS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_SHUTTERS_CLOUDS ; break; + case PATH_TOUCH_TWILIGHT: nFocus = FEAT_PATH_FOCUS_TOUCH_TWILIGHT ; nGRFocus = FEAT_GREATER_PATH_FOCUS_TOUCH_TWILIGHT ; break; + case PATH_UMBRAL_MIND: nFocus = FEAT_PATH_FOCUS_UMBRAL_MIND ; nGRFocus = FEAT_GREATER_PATH_FOCUS_UMBRAL_MIND ; break; + case PATH_BLACK_MAGIC: nFocus = FEAT_PATH_FOCUS_BLACK_MAGIC ; nGRFocus = FEAT_GREATER_PATH_FOCUS_BLACK_MAGIC ; break; + case PATH_BODY_SOUL: nFocus = FEAT_PATH_FOCUS_BODY_SOUL ; nGRFocus = FEAT_GREATER_PATH_FOCUS_BODY_SOUL ; break; + case PATH_DARK_REFLECTIONS: nFocus = FEAT_PATH_FOCUS_DARK_REFLECTIONS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARK_REFLECTIONS ; break; + case PATH_EBON_ROADS: nFocus = FEAT_PATH_FOCUS_EBON_ROADS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EBON_ROADS ; break; + case PATH_ELEMENTAL_SHADOWS: nFocus = FEAT_PATH_FOCUS_ELEMENTAL_SHADOWS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_ELEMENTAL_SHADOWS ; break; + case PATH_UNBINDING_SHADE: nFocus = FEAT_PATH_FOCUS_UNBINDING_SHADE ; nGRFocus = FEAT_GREATER_PATH_FOCUS_UNBINDING_SHADE ; break; + case PATH_VEIL_SHADOWS: nFocus = FEAT_PATH_FOCUS_VEIL_SHADOWS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_VEIL_SHADOWS ; break; + case PATH_BREATH_TWILIGHT: nFocus = FEAT_PATH_FOCUS_BREATH_TWILIGHT ; nGRFocus = FEAT_GREATER_PATH_FOCUS_BREATH_TWILIGHT ; break; + case PATH_DARK_METAMORPHOSIS: nFocus = FEAT_PATH_FOCUS_DARK_METAMORPHOSIS; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARK_METAMORPHOSIS; break; + case PATH_EBON_WALLS: nFocus = FEAT_PATH_FOCUS_EBON_WALLS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EBON_WALLS ; break; + case PATH_EYES_NIGHT_SKY: nFocus = FEAT_PATH_FOCUS_EYES_NIGHT_SKY ; nGRFocus = FEAT_GREATER_PATH_FOCUS_EYES_NIGHT_SKY ; break; + case PATH_HEART_SOUL: nFocus = FEAT_PATH_FOCUS_HEART_SOUL ; nGRFocus = FEAT_GREATER_PATH_FOCUS_HEART_SOUL ; break; + case PATH_SHADOW_CALLING: nFocus = FEAT_PATH_FOCUS_SHADOW_CALLING ; nGRFocus = FEAT_GREATER_PATH_FOCUS_SHADOW_CALLING ; break; + case PATH_NIGHTS_LONG_FINGERS: nFocus = FEAT_PATH_FOCUS_NIGHTS_LONG_FINGERS; nGRFocus = FEAT_GREATER_PATH_FOCUS_NIGHTS_LONG_FINGERS; break; + case PATH_DARKENED_ALLEYS: nFocus = FEAT_PATH_FOCUS_DARKENED_ALLEYS ; nGRFocus = FEAT_GREATER_PATH_FOCUS_DARKENED_ALLEYS ; break; + case PATH_SHADOWSCAPE: nFocus = FEAT_PATH_FOCUS_SHADOWSCAPE ; nGRFocus = FEAT_GREATER_PATH_FOCUS_SHADOWSCAPE ; break; + } + if(GetHasFeat(nFocus, oShadow)) + nReturn = 1; + if(GetHasFeat(nGRFocus, oShadow)) + nReturn = 2; + + //if (DEBUG) DoDebug("GetHasPathFocus() nReturn "+IntToString(nReturn)); + + // If none of those trigger. + return nReturn; +} + +int GetShadowAbilityOfClass(int nClass, int nType) +{ + if (nClass == CLASS_TYPE_SHADOWSMITH) return ABILITY_INTELLIGENCE; + // Intelligence for max mystery known + if (nClass == CLASS_TYPE_SHADOWCASTER && nType == 1) return ABILITY_INTELLIGENCE; + // Charisma for DC + if (nClass == CLASS_TYPE_SHADOWCASTER && nType == 2) return ABILITY_CHARISMA; + + // Technically, never gets here but the compiler does not realise that + return -1; +} + +int GetShadowcasterDC(object oShadow = OBJECT_SELF) +{ + // Things we need for DC Checks + int nMystId = PRCGetSpellId(); + int nShadEvo = GetLocalInt(oShadow, "ShadowEvoking"); + if (nShadEvo > 0) + nMystId = nShadEvo; // This is used to get the proper DC for Shadow Evocation mysteries + + int nLevel = GetMysteryLevel(oShadow, nMystId); + int nClass = GetShadowcastingClass(oShadow); + int nShadow = GetShadowcasterLevel(oShadow); + int nAbi = GetAbilityModifier(GetShadowAbilityOfClass(nClass, 2), oShadow); + int nPath = GetPathByMystery(nMystId); + int nPFocus = GetHasPathFocus(oShadow, nPath); + int nNoct = GetHasNocturnal(oShadow, nPath); + nShadow -= nPFocus; // These don't count here + + // DC is 10 + Mystery level + ability + int nDC = 10 + nLevel + nAbi; + + // If total Shadowcaster level is >= 13, change the DC for level 3 and under mysteries + // DC is 10 + 1/2 Shadowcaster level + ability + if (GetIsMysterySupernatural(oShadow, nMystId, nClass)) + nDC = 10 + nShadow/2 + nAbi; + + nDC += nPFocus; + nDC += nNoct;// It's a 0 if it doesn't exist + + return nDC; +} + +int ShadowSRPen(object oShadow, int nShadowcasterLevel) +{ + return nShadowcasterLevel; +} + +void SetLocalMystery(object oObject, string sName, struct mystery myst) +{ + //SetLocal (oObject, sName + "_", ); + SetLocalObject(oObject, sName + "_oShadow", myst.oShadow); + + SetLocalInt(oObject, sName + "_bCanMyst", myst.bCanMyst); + SetLocalInt(oObject, sName + "_nShadowcasterLevel", myst.nShadowcasterLevel); + SetLocalInt(oObject, sName + "_nMystId", myst.nMystId); + SetLocalInt(oObject, sName + "_nPen", myst.nPen); + SetLocalInt(oObject, sName + "_bIgnoreSR", myst.bIgnoreSR); + + SetLocalInt(oObject, sName + "_bEmpower", myst.bEmpower); + SetLocalInt(oObject, sName + "_bExtend", myst.bExtend); + SetLocalInt(oObject, sName + "_bMaximize", myst.bMaximize); + SetLocalInt(oObject, sName + "_bQuicken", myst.bQuicken); + + SetLocalInt(oObject, sName + "_nSaveDC", myst.nSaveDC); + SetLocalFloat(oObject, sName + "_fDur", myst.fDur); +} + +struct mystery GetLocalMystery(object oObject, string sName) +{ + struct mystery myst; + myst.oShadow = GetLocalObject(oObject, sName + "_oShadow"); + + myst.bCanMyst = GetLocalInt(oObject, sName + "_bCanMyst"); + myst.nShadowcasterLevel = GetLocalInt(oObject, sName + "_nShadowcasterLevel"); + myst.nMystId = GetLocalInt(oObject, sName + "_nMystId"); + myst.nPen = GetLocalInt(oObject, sName + "_nPen"); + myst.bIgnoreSR = GetLocalInt(oObject, sName + "_bIgnoreSR"); + + myst.bEmpower = GetLocalInt(oObject, sName + "_bEmpower"); + myst.bExtend = GetLocalInt(oObject, sName + "_bExtend"); + myst.bMaximize = GetLocalInt(oObject, sName + "_bMaximize"); + myst.bQuicken = GetLocalInt(oObject, sName + "_bQuicken"); + + myst.nSaveDC = GetLocalInt(oObject, sName + "_nSaveDC"); + myst.fDur = GetLocalFloat(oObject, sName + "_fDur"); + + return myst; +} + +int ShadowcastingFeats(object oShadow, int nMyst) +{ + int nReturn = 0; + int nPath = GetPathByMystery(nMyst); + nReturn += GetHasPathFocus(oShadow, nPath); + + return nReturn; +} + +int GetHasNocturnal(object oShadow, int nPath) +{ + int nNocturnal, nReturn; + switch(nPath) + { + case PATH_CLOAK_SHADOWS: nNocturnal = FEAT_NOCTURNAL_CASTER_CLOAK_SHADOWS ; break; + case PATH_DARK_TERRAIN: nNocturnal = FEAT_NOCTURNAL_CASTER_DARK_TERRAIN ; break; + case PATH_EBON_WHISPERS: nNocturnal = FEAT_NOCTURNAL_CASTER_EBON_WHISPERS ; break; + case PATH_EYES_DARKNESS: nNocturnal = FEAT_NOCTURNAL_CASTER_EYES_DARKNESS ; break; + case PATH_SHUTTERS_CLOUDS: nNocturnal = FEAT_NOCTURNAL_CASTER_SHUTTERS_CLOUDS ; break; + case PATH_TOUCH_TWILIGHT: nNocturnal = FEAT_NOCTURNAL_CASTER_TOUCH_TWILIGHT ; break; + case PATH_UMBRAL_MIND: nNocturnal = FEAT_NOCTURNAL_CASTER_UMBRAL_MIND ; break; + case PATH_BLACK_MAGIC: nNocturnal = FEAT_NOCTURNAL_CASTER_BLACK_MAGIC ; break; + case PATH_BODY_SOUL: nNocturnal = FEAT_NOCTURNAL_CASTER_BODY_SOUL ; break; + case PATH_DARK_REFLECTIONS: nNocturnal = FEAT_NOCTURNAL_CASTER_DARK_REFLECTIONS ; break; + case PATH_EBON_ROADS: nNocturnal = FEAT_NOCTURNAL_CASTER_EBON_ROADS ; break; + case PATH_ELEMENTAL_SHADOWS: nNocturnal = FEAT_NOCTURNAL_CASTER_ELEMENTAL_SHADOWS ; break; + case PATH_UNBINDING_SHADE: nNocturnal = FEAT_NOCTURNAL_CASTER_UNBINDING_SHADE ; break; + case PATH_VEIL_SHADOWS: nNocturnal = FEAT_NOCTURNAL_CASTER_VEIL_SHADOWS ; break; + case PATH_BREATH_TWILIGHT: nNocturnal = FEAT_NOCTURNAL_CASTER_BREATH_TWILIGHT ; break; + case PATH_DARK_METAMORPHOSIS: nNocturnal = FEAT_NOCTURNAL_CASTER_DARK_METAMORPHOSIS; break; + case PATH_EBON_WALLS: nNocturnal = FEAT_NOCTURNAL_CASTER_EBON_WALLS ; break; + case PATH_EYES_NIGHT_SKY: nNocturnal = FEAT_NOCTURNAL_CASTER_EYES_NIGHT_SKY ; break; + case PATH_HEART_SOUL: nNocturnal = FEAT_NOCTURNAL_CASTER_HEART_SOUL ; break; + case PATH_SHADOW_CALLING: nNocturnal = FEAT_NOCTURNAL_CASTER_SHADOW_CALLING ; break; + case PATH_NIGHTS_LONG_FINGERS:nNocturnal = FEAT_NOCTURNAL_CASTER_NIGHTS_LONG_FINGERS; break; + case PATH_DARKENED_ALLEYS: nNocturnal = FEAT_NOCTURNAL_CASTER_DARKENED_ALLEYS ; break; + case PATH_SHADOWSCAPE: nNocturnal = FEAT_NOCTURNAL_CASTER_SHADOWSCAPE ; break; + } + if(GetHasFeat(nNocturnal, oShadow) && GetIsNight()) + nReturn = 1; + + // If none of those trigger. + return nReturn; +} \ No newline at end of file diff --git a/src/include/shd_myst_const.nss b/src/include/shd_myst_const.nss new file mode 100644 index 0000000..126c1ae --- /dev/null +++ b/src/include/shd_myst_const.nss @@ -0,0 +1,164 @@ +// Internal Constants +const string MYSTERY_USES = "PRC_MystUses"; +const string MYSTERY_BONUS_USES = "PRC_MystBonusUses"; +const string MYST_HOLD_MYST = "MYST_HOLD_MYST"; + +// AoE Constants +const int AOE_PER_CARPET_SHADOW = 108; +const int AOE_PER_DUSK_AND_DAWN = 107; +const int AOE_PER_BLACKFIRE = 106; +const int AOE_PER_CLINGING_DARKNESS = 105; +const int AOE_PER_CURTAIN_SHADOWS = 104; +const int AOE_PER_FLOOD_SHADOW = 103; +const int AOE_PER_PLAGUE_SHADOW = 102; + +// Paths +const int PATH_CLOAK_SHADOWS = 1; +const int PATH_DARK_TERRAIN = 2; +const int PATH_EBON_WHISPERS = 3; +const int PATH_EYES_DARKNESS = 4; +const int PATH_SHUTTERS_CLOUDS = 5; +const int PATH_TOUCH_TWILIGHT = 6; +const int PATH_UMBRAL_MIND = 7; +const int PATH_BLACK_MAGIC = 8; +const int PATH_BODY_SOUL = 9; +const int PATH_DARK_REFLECTIONS = 10; +const int PATH_EBON_ROADS = 11; +const int PATH_ELEMENTAL_SHADOWS = 12; +const int PATH_UNBINDING_SHADE = 13; +const int PATH_VEIL_SHADOWS = 14; +const int PATH_BREATH_TWILIGHT = 15; +const int PATH_DARK_METAMORPHOSIS = 16; +const int PATH_EBON_WALLS = 17; +const int PATH_EYES_NIGHT_SKY = 18; +const int PATH_HEART_SOUL = 19; +const int PATH_SHADOW_CALLING = 20; +const int PATH_NIGHTS_LONG_FINGERS = 21; +const int PATH_DARKENED_ALLEYS = 22; +const int PATH_SHADOWSCAPE = 23; + +// Fundamentals +const int FUND_ARROW_DUSK = 18521; +const int FUND_BLACK_CANDLE_LIGHT = 18523; +const int FUND_BLACK_CANDLE_DARK = 18524; +const int FUND_CAUL_SHADOW = 18525; +const int FUND_MYSTIC_REFLECTIONS = 18526; +const int FUND_SHADOW_HOOD = 18527; +const int FUND_SIGHT_OBSCURED = 18528; +const int FUND_UMBRAL_HAND = 18529; +const int FUND_WIDENED_EYES = 18530; + +// Level 1 Mysteries - Apprentice +const int MYST_BEND_PERSPECTIVE = 18352; +const int MYST_CARPET_SHADOW = 18353; +const int MYST_DUSK_AND_DAWN_DUSK = 18354; +const int MYST_DUSK_AND_DAWN_DAWN = 18355; +const int MYST_LIFE_FADES = 18356; +const int MYST_MESMERIZING_SHADE = 18357; +const int MYST_STEEL_SHADOWS = 18358; +const int MYST_VOICE_SHADOW_APPROACH = 18359; +const int MYST_VOICE_SHADOW_DROP = 18360; +const int MYST_VOICE_SHADOW_FALL = 18361; +const int MYST_VOICE_SHADOW_FLEE = 18362; +const int MYST_VOICE_SHADOW_HALT = 18363; +const int MYST_QUICKER_THAN_THE_EYE = 18580; + +// Level 2 Mysteries +const int MYST_BLACK_FIRE = 18364; +const int MYST_CONGRESS_SHADOWS = 18365; +const int MYST_FLESH_FAILS_STR = 18366; +const int MYST_FLESH_FAILS_DEX = 18367; +const int MYST_FLESH_FAILS_CON = 18368; +const int MYST_PIERCING_SIGHT = 18369; +const int MYST_SHADOW_SKIN = 18370; +const int MYST_SIGHT_ECLIPSED = 18371; +const int MYST_THOUGHTS_SHADOW_INT = 18372; +const int MYST_THOUGHTS_SHADOW_WIS = 18373; +const int MYST_THOUGHTS_SHADOW_CHA = 18374; +const int MYST_TRAIL_OF_HAZE = 18581; + +// Level 3 Mysteries +const int MYST_AFRAID_DARK = 18375; +const int MYST_CLINGING_DARKNESS = 18376; +const int MYST_DANCING_SHADOWS = 18377; +const int MYST_FLICKER = 18378; +const int MYST_KILLING_SHADOWS = 18379; +const int MYST_SHARP_SHADOWS = 18380; +const int MYST_UMBRAL_TOUCH = 18381; +const int MYST_UMBRAL_FIST = 18582; +const int MYST_UMBRAL_FIST_BULL_RUSH = 18606; +const int MYST_UMBRAL_FIST_DISARM = 18607; +const int MYST_UMBRAL_FIST_TRIP = 18608; + +// Level 4 Mysteries - Initiate +const int MYST_AURA_OF_SHADE = 18382; +const int MYST_BOLSTER = 18383; +const int MYST_SHADOW_EVOCATION = 18384; +const int MYST_SHADOW_EVOCATION_CONV = 18385; +const int MYST_SHADOW_VISION = 18386; +const int MYST_SHADOWS_FADE = 18387; +const int MYST_STEP_SHADOW_SELF = 18388; +const int MYST_STEP_SHADOW_PARTY = 18389; +const int MYST_WARP_SPELL = 18390; +const int MYST_FEARFUL_GLOOM = 18583; + +// Level 5 Mysteries +const int MYST_CURTAIN_SHADOWS = 18391; +const int MYST_DARK_AIR = 18392; +const int MYST_ECHO_SPELL = 18393; +const int MYST_FEIGN_LIFE = 18394; +const int MYST_LANGUOR_SLOW = 18395; +const int MYST_LANGUOR_HOLD = 18396; +const int MYST_PASS_SHADOW_SELF = 18397; +const int MYST_PASS_SHADOW_PARTY = 18398; +const int MYST_UNRAVEL_DWEOMER = 18399; +const int MYST_SICKENING_SHADOW = 18584; + +// Level 6 Mysteries +const int MYST_FLOOD_SHADOW = 18400; +const int MYST_GREATER_SHADOW_EVO = 18401; +const int MYST_GREATER_SHADOW_EVO_CONV = 18402; +const int MYST_SHADOW_INVESTITURE = 18403; +const int MYST_SHADOW_STORM = 18404; +const int MYST_SHADOWS_FADE_GREATER = 18405; +const int MYST_UNVEIL = 18406; +const int MYST_VOYAGE_SHADOW_SELF = 18407; +const int MYST_VOYAGE_SHADOW_PARTY = 18408; +const int MYST_DEADLY_SHADE_DR = 18585; +const int MYST_DEADLY_SHADE_NEG = 18586; + +// Level 7 Mysteries - Master +const int MYST_DARK_SOUL = 18409; +const int MYST_EPHEMERAL_IMAGE = 18410; +const int MYST_LIFE_FADES_GREATER = 18411; +const int MYST_PRISON_NIGHT = 18412; +const int MYST_UMBRAL_SERVANT = 18413; +const int MYST_TRUTH_REVEALED = 18414; +const int MYST_GRASPING_SHADOWS = 18587; + +// Level 8 Mysteries +const int MYST_FAR_SIGHT = 18415; +const int MYST_GR_FLESH_FAILS_STR = 18416; +const int MYST_GR_FLESH_FAILS_DEX = 18417; +const int MYST_GR_FLESH_FAILS_CON = 18418; +const int MYST_SHADOW_PLAGUE = 18419; +const int MYST_SOUL_PUPPET = 18420; +const int MYST_TOMB_NIGHT = 18421; +const int MYST_UMBRAL_BODY = 18422; +const int MYST_MENAGERIE_OF_DARKNESS = 18588; + +// Level 9 Mysteries +const int MYST_ARMY_SHADOW = 18423; +const int MYST_CONSUME_ESSENCE = 18424; +const int MYST_EPHEMERAL_STORM = 18425; +const int MYST_REFLECTIONS = 18426; +const int MYST_SHADOW_SURGE = 18427; +const int MYST_SHADOW_TIME = 18428; +const int MYST_BLACK_LABYRINTH = 18589; + +// Master of Shadow +const int MYST_BID_HEAL = 18539; +const int MYST_BID_ATTACK = 18540; +const int MYST_BID_COLD = 18541; +const int MYST_BID_DR = 18542; +const int MYST_BID_SPEED = 18543; \ No newline at end of file diff --git a/src/include/shd_mysthook.nss b/src/include/shd_mysthook.nss new file mode 100644 index 0000000..51c63be --- /dev/null +++ b/src/include/shd_mysthook.nss @@ -0,0 +1,280 @@ +//:://///////////////////////////////////////////// +//:: Shadowcasting Mystery Hook File. +//:: shd_mysthook.nss +//::////////////////////////////////////////////// +/* + + This file acts as a hub for all code that + is hooked into the mystery scripts for Shadowcasting + +*/ +//::////////////////////////////////////////////// +//:: Created By: Stratovarius +//:: Created On: 7-2-2019 +//::////////////////////////////////////////////// + +#include "x2_inc_spellhook" +#include "prc_inc_spells" +#include "inc_utility" +#include "prc_inc_itmrstr" + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int ShadPreMystCastCode(); + +// Does the counterspelling for Warp Spell, +// and the storing of the spell for later use +int WarpMyst(object oCaster, int nMyst) +{ + int nWarp = GetLocalInt(oCaster, "WarpSpell"); + // If Warp Spell isn't set, just keep going + if (!nWarp) return TRUE; + + object oShadow = GetLocalObject(oCaster, "WarpSpell"); // The one who cast Warp Spell + + DeleteLocalInt(oCaster, "WarpSpell"); + DeleteLocalObject(oCaster, "WarpSpell"); + + int nLevel = GetShadowcasterLevel(oCaster); + if (d20() + nWarp > d20() + nLevel) // Won the caster level check + { + // Set a marker on the Shadowcaster + SetLocalInt(oShadow, "WarpSpellSuccess", TRUE); + FloatingTextStringOnCreature("You have successfully warped your opponent's "+GetMysteryName(nMyst), oShadow, FALSE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SOUL_TRAP), oCaster); + return FALSE; + } + + FloatingTextStringOnCreature("Your warp spell has failed", oShadow, FALSE); + return TRUE; +} + +// Stores the spell for use with Echo Spell +void EchoMyst(object oCaster, int nMyst) +{ + int nEcho = GetLocalInt(oCaster, "EchoSpell"); + // If Echo Spell isn't set, just skip + if (nEcho) + { + object oShadow = GetLocalObject(oCaster, "EchoSpell"); // The one who cast Echo Spell + SetLocalInt(oShadow, "EchoedSpell", nMyst); + FloatingTextStringOnCreature("You have echoed " + GetMysteryName(nMyst) + " and have one round to cast it", oShadow, FALSE); + DelayCommand(9.0, DeleteLocalInt(oShadow, "EchoedSpell")); + DeleteLocalInt(oCaster, "EchoSpell"); + DeleteLocalObject(oCaster, "EchoSpell"); + } +} + +int MysterySpellFailure(object oShadow, int nMystId, int nMystLevel, int nClass) +{ + // Mysteries of both types ignore arcane spell failure + if (GetIsMysterySupernatural(oShadow, nMystId, nClass) || GetIsMysterySLA(oShadow, nMystId, nClass)) + return TRUE; + + int nASF = GetArcaneSpellFailure(oShadow); + + if(Random(100) < nASF) + { + int nFail = TRUE; + // Still spell helps + if((GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_1, oShadow) && nMystLevel <= 3) + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_2, oShadow) && nMystLevel <= 6) + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_3, oShadow) && nMystLevel <= 9)) + { + nFail = FALSE; + } + if(nFail) + { + //52946 = Spell failed due to arcane spell failure! + FloatingTextStrRefOnCreature(52946, oShadow, FALSE); + return FALSE; + } + } + return TRUE; +} + +int ShadowTime(object oCaster, object oTarget, int bSpellIsHostile) +{ + // PnP Timestop + if(GetPRCSwitch(PRC_TIMESTOP_NO_HOSTILE)) + { + if(GetHasSpellEffect(SPELL_TIME_STOP, oCaster) + || GetHasSpellEffect(4032, oCaster) //epic spell: Greater Timestop + || GetHasSpellEffect(14236, oCaster) //psionic power: Temporal Acceleration + || GetHasSpellEffect(18428, oCaster)) //Mystery MYST_SHADOW_TIME + { + if(!GetIsObjectValid(oTarget) + || oTarget != oCaster + || bSpellIsHostile) + { + return FALSE; + } + } + } + + return TRUE; +} + +//------------------------------------------------------------------------------ +// if FALSE is returned by this function, the spell will not be cast +// the order in which the functions are called here DOES MATTER, changing it +// WILL break the crafting subsystems +//------------------------------------------------------------------------------ +int ShadPreMystCastCode() +{ + object oShadow = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + int nMystID = PRCGetSpellId(); + int nMystLevel = GetMysteryLevel(oShadow); + int nShadowcastingClass = GetShadowcastingClass(oShadow); + int bMystIsHostile = Get2DACache("spells", "HostileSetting", nMystID) == "1"; + + int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oShadow); + + //--------------------------------------------------------------------------- + // Break any spell require maintaining concentration + //--------------------------------------------------------------------------- + X2BreakConcentrationSpells(); + + //--------------------------------------------------------------------------- + // Check for PRC spell effects + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PRCSpellEffects(oShadow, oTarget, nMystID, nMystLevel, nShadowcastingClass, bMystIsHostile, -1); + + if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #1: " + IntToString(nContinue)); + + //--------------------------------------------------------------------------- + // Run Arcane Spell Failure + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = MysterySpellFailure(oShadow, nMystID, nMystLevel, nShadowcastingClass); + + if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #2: " + IntToString(nContinue)); + + //--------------------------------------------------------------------------- + // Run Shadow Time + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = ShadowTime(oShadow, oTarget, bMystIsHostile); + + if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #3: " + IntToString(nContinue)); + + //--------------------------------------------------------------------------- + // Run Grappling Concentration Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = GrappleConc(oShadow, nMystLevel); + + if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #4: " + IntToString(nContinue)); + + //--------------------------------------------------------------------------- + // This stuff is only interesting for player characters we assume that use + // magic device always works and NPCs don't use the crafting feats or + // sequencers anyway. Thus, any NON PC spellcaster always exits this script + // with TRUE (unless they are DM possessed or in the Wild Magic Area in + // Chapter 2 of Hordes of the Underdark. + //--------------------------------------------------------------------------- + if(!GetIsPC(oShadow) + && !GetPRCSwitch(PRC_NPC_HAS_PC_SPELLCASTING)) + { + if(!GetIsDMPossessed(oShadow) && !GetLocalInt(GetArea(oShadow), "X2_L_WILD_MAGIC")) + { + return TRUE; + } + } + + //--------------------------------------------------------------------------- + // Run use magic device skill check + //--------------------------------------------------------------------------- + if (nContinue) + { + nContinue = X2UseMagicDeviceCheck(oShadow); + } + + //----------------------------------------------------------------------- + // run any user defined spellscript here + //----------------------------------------------------------------------- + if (nContinue) + { + nContinue = X2RunUserDefinedSpellScript(); + } + + //--------------------------------------------------------------------------- + // Check for the new restricted itemproperties + //--------------------------------------------------------------------------- + if(nContinue + && GetIsObjectValid(GetSpellCastItem()) + && !CheckPRCLimitations(GetSpellCastItem(), oShadow)) + { + SendMessageToPC(oShadow, "You cannot use "+GetName(GetSpellCastItem())); + nContinue = FALSE; + } + + //--------------------------------------------------------------------------- + // Warp Spell + //--------------------------------------------------------------------------- + if (nContinue) + { + nContinue = WarpMyst(oShadow, nMystID); + } + + if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #5: " + IntToString(nContinue)); + + EchoMyst(oShadow, nMystID); + + //--------------------------------------------------------------------------- + // The following code is only of interest if an item was targeted + //--------------------------------------------------------------------------- + if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + + //----------------------------------------------------------------------- + // Check if spell was used to trigger item creation feat + //----------------------------------------------------------------------- + if (nContinue) { + nContinue = !ExecuteScriptAndReturnInt("x2_pc_craft", oShadow); + } + + //----------------------------------------------------------------------- + // * Execute item OnSpellCast At routing script if activated + //----------------------------------------------------------------------- + if (GetModuleSwitchValue(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS) == TRUE) + { + SetUserDefinedItemEventNumber(X2_ITEM_EVENT_SPELLCAST_AT); + int nRet = ExecuteScriptAndReturnInt(GetUserDefinedItemEventScriptName(oTarget), oShadow); + if (nRet == X2_EXECUTE_SCRIPT_END) + { + return FALSE; + } + } + + //----------------------------------------------------------------------- + // Prevent any spell that has no special coding to handle targetting of items + // from being cast on items. We do this because we can not predict how + // all the hundreds spells in NWN will react when cast on items + //----------------------------------------------------------------------- + if (nContinue) { + nContinue = X2CastOnItemWasAllowed(oTarget); + } + } + + //Cleaning spell variables used for holding the charge + if(!GetLocalInt(oShadow, "PRC_SPELL_EVENT")) + { + DeleteLocalInt(oShadow, "PRC_SPELL_CHARGE_COUNT"); + DeleteLocalInt(oShadow, "PRC_SPELL_CHARGE_SPELLID"); + DeleteLocalObject(oShadow, "PRC_SPELL_CONC_TARGET"); + DeleteLocalInt(oShadow, "PRC_SPELL_METAMAGIC"); + DeleteLocalManifestation(oShadow, "PRC_POWER_HOLD_MANIFESTATION"); + DeleteLocalMystery(oShadow, "MYST_HOLD_MYST"); + } + else if(GetLocalInt(oShadow, "PRC_SPELL_CHARGE_SPELLID") != PRCGetSpellId()) + { //Sanity check, in case something goes wrong with the action queue + DeleteLocalInt(oShadow, "PRC_SPELL_EVENT"); + } + + if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #6: " + IntToString(nContinue)); + return nContinue; +} \ No newline at end of file diff --git a/src/include/spinc_bolt.nss b/src/include/spinc_bolt.nss new file mode 100644 index 0000000..03ed6da --- /dev/null +++ b/src/include/spinc_bolt.nss @@ -0,0 +1,386 @@ +///////////////////////////////////////////////////////////////////////// +// +// DoBolt - Function to apply an elemental bolt damage effect given +// the following arguments: +// +// nDieSize - die size to roll (d4, d6, or d8) +// nBonusDam - bonus damage per die, or 0 for none +// nDice = number of dice to roll. +// nBoltEffect - visual effect to use for bolt(s) +// nVictimEffect - visual effect to apply to target(s) +// nDamageType - elemental damage type of the cone (DAMAGE_TYPE_xxx) +// nSaveType - save type used for cone (SAVING_THROW_TYPE_xxx) +// nSchool - spell school, defaults to SPELL_SCHOOL_EVOCATION. +// fDoKnockdown - flag indicating whether spell does knockdown, defaults to FALSE. +// nSpellID - spell ID to use for events +// +///////////////////////////////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_add_spell_dc" + +//* fires a storm of nCap missiles at targets in area +void PRCDoMissileStorm(int nD6Dice, int nCap, int nSpell, int nMIRV = VFX_IMP_MIRV, int nVIS = VFX_IMP_MAGBLUE, int nDAMAGETYPE = DAMAGE_TYPE_MAGICAL, int nONEHIT = FALSE, int nReflexSave = FALSE); + +float GetVFXLength(location lCaster, float fLength, float fAngle); + +void DoBolt(int nCasterLevel, int nDieSize, int nBonusDam, int nDice, int nBoltEffect, + int nVictimEffect, int nDamageType, int nSaveType, + int nSchool = SPELL_SCHOOL_EVOCATION, int nDoKnockdown = FALSE, int nSpellID = -1, float fRangeFt = 120.0f) +{ + // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell + //if (!X2PreSpellCastCode()) return; + + PRCSetSchool(nSchool); + + object oCaster = OBJECT_SELF; + + // Get the spell ID if it was not given. + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + // Adjust the damage type if necessary. + nDamageType = PRCGetElementalDamageType(nDamageType, OBJECT_SELF); + + int nDamage; + int nSaveDC; + int bKnockdownTarget; + float fDelay; + + int nPenetr = nCasterLevel + SPGetPenetr(); + + // individual effect + effect eVis = EffectVisualEffect(nVictimEffect); + effect eKnockdown = EffectKnockdown(); + effect eDamage; + + // where is the caster? + location lCaster = GetLocation(oCaster); + + // where is the target? + location lTarget = PRCGetSpellTargetLocation(); + vector vOrigin = GetPosition(oCaster); + float fLength = FeetToMeters(fRangeFt); + + // run away! Vector maths coming up... + // VFX length + //float fAngle = GetRelativeAngleBetweenLocations(lCaster, lTarget); + //float fVFXLength = GetVFXLength(lCaster, fLength, fAngle); + //float fDuration = 3.0f; + + + /*BeamLineFromCenter(DURATION_TYPE_TEMPORARY, nBoltEffect, lCaster, fVFXLength, fAngle, fDuration, "prc_invisobj", 0.0f, "z", 0.0f, 0.0f, + -1, -1, 0.0f, 1.0f, // no secondary VFX + fDuration); + */ + // Do VFX. This is moderately heavy, so it isn't duplicated by Twin Power + float fAngle = GetRelativeAngleBetweenLocations(lCaster, lTarget); + float fSpiralStartRadius = FeetToMeters(1.0f); + float fRadius = FeetToMeters(5.0f); + float fDuration = 4.5f; + float fVFXLength = GetVFXLength(lCaster, fLength, GetRelativeAngleBetweenLocations(lCaster, lTarget)); + // A tube of beams, radius 5ft, starting 1m from manifester and running for the length of the line + BeamGengon(DURATION_TYPE_TEMPORARY, nBoltEffect, lCaster, fRadius, fRadius, + 1.0f, fVFXLength, // Start 1m from the manifester, end at LOS end + 8, // 8 sides + fDuration, "prc_invisobj", + 0.0f, // Drawn instantly + 0.0f, 0.0f, 45.0f, "y", fAngle, 0.0f, + -1, -1, 0.0f, 1.0f, // No secondary VFX + fDuration + ); + // spell damage effects + // Loop over targets in the spell shape + object oTarget = MyFirstObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, vOrigin); + while(GetIsObjectValid(oTarget)) + { + if(oTarget != oCaster && spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster)) + { + // Let the AI know + PRCSignalSpellEvent(oTarget, TRUE, nSpellID, oCaster); + // Reset the knockdown target flag. + bKnockdownTarget = FALSE; + // Make an SR check + if(!PRCDoResistSpell(oCaster, oTarget, nPenetr)) + { + // Roll damage + nDamage = PRCGetMetaMagicDamage(nDamageType, nDice, nDieSize, nBonusDam); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(nSpellID, DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, oCaster)) + nDamage += nDice; + // Adds damage per dice + nDamage += SpellDamagePerDice(oCaster, nDice); + int nFullDamage = nDamage; + + // Do save + nSaveDC = PRCGetSaveDC(oTarget,OBJECT_SELF); + if(nSaveType == SAVING_THROW_TYPE_COLD) + { + // Cold has a fort save for half + if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, nSaveType)) + { + if (GetHasMettle(oTarget, SAVING_THROW_FORT)) + nDamage = 0; + nDamage /= 2; + } + } + else + // Adjust damage according to Reflex Save, Evasion or Improved Evasion + nDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, nSaveType); + + if(nDamage > 0) + { + fDelay = GetDistanceBetweenLocations(lCaster, GetLocation(oTarget)) / 20.0f; + eDamage = PRCEffectDamage(oTarget, nDamage, nDamageType); + DelayCommand(1.0f + fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); + DelayCommand(1.0f + fDelay, PRCBonusDamage(oTarget)); + DelayCommand(1.0f + fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + }// end if - There was still damage remaining to be dealt after adjustments + + // Determine if the target needs to be knocked down. The target is knocked down + // if all of the following criteria are met: + // - Knockdown is enabled. + // - The damage from the spell didn't kill the creature + // - The creature is large or smaller + // - The creature failed it's reflex save. + // If the spell does knockdown we need to figure out whether the target made or failed + // the reflex save. If the target doesn't have improved evasion this is easy, if the + // damage is the same as the original damage then the target failed it's save. If the + // target has improved evasion then it's harder as the damage is halved even on a failed + // save, so we have to catch that case. + bKnockdownTarget = nDoKnockdown && !GetIsDead(oTarget) && + PRCGetCreatureSize(oTarget) <= CREATURE_SIZE_LARGE && + (nFullDamage == nDamage || (0 != nDamage && GetHasFeat(FEAT_IMPROVED_EVASION, oTarget))); + // If we're supposed to apply knockdown then do so for 1 round. + if (bKnockdownTarget) + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnockdown, oTarget, RoundsToSeconds(1),TRUE,-1,nCasterLevel); + + }// end if - SR check + }// end if - Target validity check + + // Get next target + oTarget = MyNextObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, vOrigin); + }// end while - Target loop + + PRCSetSchool(); +} + +// taken with minor modification from psi_power_enbolt + +float GetVFXLength(location lCaster, float fLength, float fAngle) +{ + float fLowerBound = 0.0f; + float fUpperBound = fLength; + float fVFXLength = fLength / 2; + vector vVFXOrigin = GetPositionFromLocation(lCaster); + vector vAngle = AngleToVector(fAngle); + vector vVFXEnd; + int bConverged = FALSE; + while(!bConverged) + { + // Create the test vector for this loop + vVFXEnd = vVFXOrigin + (fVFXLength * vAngle); + + // Determine which bound to move. + if(LineOfSightVector(vVFXOrigin, vVFXEnd)) + fLowerBound = fVFXLength; + else + fUpperBound = fVFXLength; + + // Get the new middle point + fVFXLength = (fUpperBound + fLowerBound) / 2; + + // Check if the locations have converged + if(fabs(fUpperBound - fLowerBound) < 2.5f) + bConverged = TRUE; + } + + return fVFXLength; +} + +//:://///////////////////////////////////////////// +//:: PRCDoMissileStorm +//:: Copyright (c) 2002 Bioware Corp. +//::////////////////////////////////////////////// +/* + Fires a volley of missiles around the area + of the object selected. + + Each missiles (nD6Dice)d6 damage. + There are casterlevel missiles (to a cap as specified) +*/ +//::////////////////////////////////////////////// +//:: Created By: Brent +//:: Created On: July 31, 2002 +//::////////////////////////////////////////////// +//:: Modified March 14 2003: Removed the option to hurt chests/doors +//:: was potentially causing bugs when no creature targets available. +void PRCDoMissileStorm(int nD6Dice, int nCap, int nSpell, int nMIRV = VFX_IMP_MIRV, int nVIS = VFX_IMP_MAGBLUE, int nDAMAGETYPE = DAMAGE_TYPE_MAGICAL, int nONEHIT = FALSE, int nReflexSave = FALSE) +{ + object oTarget = OBJECT_INVALID; + int nCasterLvl = PRCGetCasterLevel(OBJECT_SELF); +// int nDamage = 0; + int nMetaMagic = PRCGetMetaMagicFeat(); + int nCnt = 1; + effect eMissile = EffectVisualEffect(nMIRV); + effect eVis = EffectVisualEffect(nVIS); + float fDist = 0.0; + float fDelay = 0.0; + float fDelay2, fTime; + location lTarget = PRCGetSpellTargetLocation(); // missile spread centered around caster + int nMissiles = nCasterLvl; + + nCasterLvl +=SPGetPenetr(); + + if (nMissiles > nCap) + { + nMissiles = nCap; + } + + /* New Algorithm + 1. Count # of targets + 2. Determine number of missiles + 3. First target gets a missile and all Excess missiles + 4. Rest of targets (max nMissiles) get one missile + */ + int nEnemies = 0; + + oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_GARGANTUAN, lTarget, TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget) ) + { + // * caster cannot be harmed by this spell + if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, OBJECT_SELF) && (oTarget != OBJECT_SELF)) + { + // GZ: You can only fire missiles on visible targets + // 1.69 change + // If the firing object is a placeable (such as a projectile trap), + // we skip the line of sight check as placeables can't "see" things. + if ( ( GetObjectType(OBJECT_SELF) == OBJECT_TYPE_PLACEABLE ) || + GetObjectSeen(oTarget,OBJECT_SELF)) + { + nEnemies++; + } + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_GARGANTUAN, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + + if (nEnemies == 0) return; // * Exit if no enemies to hit + int nExtraMissiles = nMissiles / nEnemies; + + // April 2003 + // * if more enemies than missiles, need to make sure that at least + // * one missile will hit each of the enemies + if (nExtraMissiles <= 0) + { + nExtraMissiles = 1; + } + + // by default the Remainder will be 0 (if more than enough enemies for all the missiles) + int nRemainder = 0; + + if (nExtraMissiles >0) + nRemainder = nMissiles % nEnemies; + + if (nEnemies > nMissiles) + nEnemies = nMissiles; + + oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_GARGANTUAN, lTarget, TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget) && nCnt <= nEnemies) + { + // * caster cannot be harmed by this spell + if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, OBJECT_SELF) && (oTarget != OBJECT_SELF) && + (( GetObjectType(OBJECT_SELF) == OBJECT_TYPE_PLACEABLE ) || + (GetObjectSeen(oTarget,OBJECT_SELF)))) + { + //Fire cast spell at event for the specified target + SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, nSpell)); + + // * recalculate appropriate distances + fDist = GetDistanceBetween(OBJECT_SELF, oTarget); + fDelay = fDist/(3.0 * log(fDist) + 2.0); + + // Firebrand. + // It means that once the target has taken damage this round from the + // spell it won't take subsequent damage + if (nONEHIT == TRUE) + { + nExtraMissiles = 1; + nRemainder = 0; + } + + int i = 0; + //-------------------------------------------------------------- + // GZ: Moved SR check out of loop to have 1 check per target + // not one check per missile, which would rip spell mantels + // apart + //-------------------------------------------------------------- + if (!PRCDoResistSpell(OBJECT_SELF, oTarget,nCasterLvl, fDelay)) + { + for (i=1; i <= nExtraMissiles + nRemainder; i++) + { + //Roll damage + int nDam = d6(nD6Dice); + //Enter Metamagic conditions + if ((nMetaMagic & METAMAGIC_MAXIMIZE)) + { + nDam = nD6Dice*6;//Damage is at max + } + if ((nMetaMagic & METAMAGIC_EMPOWER)) + { + nDam = nDam + nDam/2; //Damage/Healing is +50% + } + + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(nSpell, DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF)) + nDam += nD6Dice; + nDam += SpellDamagePerDice(OBJECT_SELF, nD6Dice); + + if(i == 1) + { + //nDam += ApplySpellBetrayalStrikeDamage(oTarget, OBJECT_SELF); + DelayCommand(fDelay, PRCBonusDamage(oTarget)); + } + + // Jan. 29, 2004 - Jonathan Epp + // Reflex save was not being calculated for Firebrand + if(nReflexSave) + { + if(nDAMAGETYPE == DAMAGE_TYPE_FIRE) + nDam = PRCGetReflexAdjustedDamage(nDam, oTarget, PRCGetSaveDC(oTarget, OBJECT_SELF), SAVING_THROW_TYPE_FIRE); + else if(nDAMAGETYPE == DAMAGE_TYPE_ELECTRICAL) + nDam = PRCGetReflexAdjustedDamage(nDam, oTarget, PRCGetSaveDC(oTarget, OBJECT_SELF), SAVING_THROW_TYPE_ELECTRICITY); + else if(nDAMAGETYPE == DAMAGE_TYPE_COLD) + nDam = PRCGetReflexAdjustedDamage(nDam, oTarget, PRCGetSaveDC(oTarget, OBJECT_SELF), SAVING_THROW_TYPE_COLD); + else if(nDAMAGETYPE == DAMAGE_TYPE_ACID) + nDam = PRCGetReflexAdjustedDamage(nDam, oTarget, PRCGetSaveDC(oTarget, OBJECT_SELF), SAVING_THROW_TYPE_ACID); + else if(nDAMAGETYPE == DAMAGE_TYPE_SONIC) + nDam = PRCGetReflexAdjustedDamage(nDam, oTarget, PRCGetSaveDC(oTarget, OBJECT_SELF), SAVING_THROW_TYPE_SONIC); + } + + fTime = fDelay; + fDelay2 += 0.1; + fTime += fDelay2; + + //Set damage effect + effect eDam = PRCEffectDamage(oTarget, nDam, nDAMAGETYPE); + //Apply the MIRV and damage effect + DelayCommand(fTime, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget)); + DelayCommand(fDelay2, ApplyEffectToObject(DURATION_TYPE_INSTANT, eMissile, oTarget)); + DelayCommand(fTime, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget)); + } + } // for + else + { // * apply a dummy visual effect + ApplyEffectToObject(DURATION_TYPE_INSTANT, eMissile, oTarget); + } + nCnt++;// * increment count of missiles fired + nRemainder = 0; + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_GARGANTUAN, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + +} + +// Test main +//void main(){} diff --git a/src/include/spinc_burst.nss b/src/include/spinc_burst.nss new file mode 100644 index 0000000..e09dce9 --- /dev/null +++ b/src/include/spinc_burst.nss @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////////////// +// +// DoBurst() - Do a damaging burst at the spell's target location. +// nDieSize - size of die to roll. +// nBonusDam - bonus damage per die. +// nDice - number of dice to roll +// nBurstEffect - VFX_xxx or AOE_xxx of burst vfx. +// nVictimEffect - VFX_xxx of target impact. +// nDamageType - DAMAGE_TYPE_xxx of the type of damage dealt +// nSaveType - SAVING_THROW_xxx of type of save to use +// bCasterImmune - Indicates whether the caster is immune to the spell +// nSchool - SPELL_SCHOOL_xxx of the spell's school +// nSpellID - ID # of the spell, if -1 PRCGetSpellId() is used +// fAOEDuration - if > 0, then nBurstEffect should be an AOE_xxx vfx, it +// will be played at the target location for this duration. If this is +// 0 then nBurstEffect should be a VFX_xxx vfx. +// +//////////////////////////////////////////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_add_spell_dc" + +void DoBurst (int nCasterLvl, int nDieSize, int nBonusDam, int nDice, int nBurstEffect, + int nVictimEffect, float fRadius, int nDamageType, int nBonusDamageType, int nSaveType, + int bCasterImmune = FALSE, + int nSchool = SPELL_SCHOOL_EVOCATION, int nSpellID = -1, + float fAOEDuration = 0.0f) +{ + PRCSetSchool(nSchool); + + // Get the spell ID if it was not given. + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + // Get the spell target location as opposed to the spell target. + location lTarget = PRCGetSpellTargetLocation(); + + int nPenetr = nCasterLvl + SPGetPenetr(); + // Get the effective caster level and hand it to the SR engine. + + + // Adjust the damage type of necessary, if the damage & bonus damage types are the + // same we need to copy the adjusted damage type to the bonus damage type. + int nSameDamageType = nDamageType == nBonusDamageType; + nDamageType = PRCGetElementalDamageType(nDamageType, OBJECT_SELF); + if (nSameDamageType) nBonusDamageType = nDamageType; + + // Apply the specified vfx to the location. If we were given an aoe vfx then + // fAOEDuration will be > 0. + if (fAOEDuration > 0.0) + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, + EffectAreaOfEffect(nBurstEffect, "****", "****", "****"), lTarget, fAOEDuration); + else + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(nBurstEffect), lTarget); + + effect eVis = EffectVisualEffect(nVictimEffect); + effect eDamage, eBonusDamage; + float fDelay; + + // Declare the spell shape, size and the location. Capture the first target object in the shape. + // Cycle through the targets within the spell shape until an invalid object is captured. + object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, fRadius, lTarget, FALSE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + while (GetIsObjectValid(oTarget)) + { + // Filter out the caster if he is supposed to be immune to the burst. + if (!(bCasterImmune && OBJECT_SELF == oTarget)) + { + if(spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF)) + { + //Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, TRUE, nSpellID); + + fDelay = PRCGetSpellEffectDelay(lTarget, oTarget); + if (!PRCDoResistSpell(OBJECT_SELF, oTarget,nPenetr, fDelay)) + { + int nSaveDC = PRCGetSaveDC(oTarget, OBJECT_SELF); + + int nDam = 0; + int nDam2 = 0; + if (nSameDamageType) + { + // Damage damage type is the simple case, just get the total damage + // of the spell's type, apply metamagic and roll the save. + + // Roll damage for each target + nDam = PRCGetMetaMagicDamage(nDamageType, nDice, nDieSize, nBonusDam); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(nSpellID, DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF)) + nDam += nDice; + nDam += SpellDamagePerDice(OBJECT_SELF, nDice); + + // Adjust damage for reflex save / evasion / imp evasion + nDam = PRCGetReflexAdjustedDamage(nDam, oTarget, nSaveDC, nSaveType); + } + else + { + // Damage of different types is a bit more complicated, we need to + // calculate the bonus damage ourselves, figure out if the save was + // 1/2 or no damage, and apply appropriately to the secondary damage + // type. + + // Calculate base and bonus damage. + nDam = PRCGetMetaMagicDamage(nDamageType, nDice, nDieSize, 0); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(nSpellID, DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF)) + nDam += nDice; + nDam += SpellDamagePerDice(OBJECT_SELF, nDice); + nDam2 = nDice * nBonusDam; + + // Adjust damage for reflex save / evasion / imp evasion. We need to + // deal with damage being constant, damage being 0, and damage being + // some percentage of the total (should be 1/2). + int nAdjustedDam = PRCGetReflexAdjustedDamage(nDam, oTarget, nSaveDC, nSaveType); + if (0 == nAdjustedDam) + { + // Evasion zero'ed out the damage, clear both damage values. + nDam = 0; + nDam2 = 0; + } + else if (nAdjustedDam < nDam) + { + // Assume 1/2 damage, and half the bonus damage. + nDam = nAdjustedDam; + nDam2 /= 2; + } + } + + //Set the damage effect + if (nDam > 0) + { + eDamage = PRCEffectDamage(oTarget, nDam, nDamageType); + + // Apply effects to the currently selected target. + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); + PRCBonusDamage(oTarget); + + // This visual effect is applied to the target object not the location as above. + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + } + + // Apply bonus damage if it is a different type. + if (nDam2 > 0) + { + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, + PRCEffectDamage(oTarget, nDam2, nBonusDamageType), oTarget)); + } + } + } + } + + oTarget = MyNextObjectInShape(SHAPE_SPHERE, fRadius, lTarget, FALSE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + } + + // Let the SR engine know that we are done and clear out school local var. + + PRCSetSchool(); +} + +// Test main +//void main(){} diff --git a/src/include/spinc_cone.nss b/src/include/spinc_cone.nss new file mode 100644 index 0000000..4b7ce3b --- /dev/null +++ b/src/include/spinc_cone.nss @@ -0,0 +1,99 @@ +///////////////////////////////////////////////////////////////////// +// +// DoCone - Function to apply an elemental cone damage effect given +// the following arguments: +// +// nDieSize - die size to roll (d4, d6, or d8) +// nBonusDam - bonus damage per die, or 0 for none +// nConeEffect - unused (this is in 2da) +// nVictimEffect - visual effect to apply to target(s) +// nDamageType - elemental damage type of the cone (DAMAGE_TYPE_xxx) +// nSaveType - save type used for cone (SAVE_TYPE_xxx) +// nSchool - spell school, defaults to SPELL_SCHOOL_EVOCATION +// nSpellID - spell ID to use for events +// +///////////////////////////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_add_spell_dc" + +void DoCone (int nDieSize, int nBonusDam, int nDieCap, int nConeEffect /* unused */, + int nVictimEffect, int nDamageType, int nSaveType, + int nSchool = SPELL_SCHOOL_EVOCATION, int nSpellID = -1) +{ + PRCSetSchool(nSchool); + + // Get the spell ID if it was not given. + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + // Get effective caster level and hand it to the SR engine. Then + // cap it at our die cap. + int nCasterLvl = PRCGetCasterLevel(OBJECT_SELF); + int nPenetr = nCasterLvl + SPGetPenetr(); + + + if (nCasterLvl > nDieCap) nCasterLvl = nDieCap; + + // Figure out where the cone was targetted. + location lTargetLocation = PRCGetSpellTargetLocation(); + + // Adjust the damage type of necessary. + nDamageType = PRCGetElementalDamageType(nDamageType, OBJECT_SELF); + + + + //Declare major variables + int nDamage; + float fDelay; + object oTarget; + + // Declare the spell shape, size and the location. Capture the first target object in the shape. + // Cycle through the targets within the spell shape until an invalid object is captured. + oTarget = MyFirstObjectInShape(SHAPE_SPELLCONE, 11.0, lTargetLocation, FALSE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + while(GetIsObjectValid(oTarget)) + { + if(spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF)) + { + //Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, TRUE, nSpellID); + + //Get the distance between the target and caster to delay the application of effects + fDelay = PRCGetSpellEffectDelay(lTargetLocation, oTarget); + + //Make SR check, and appropriate saving throw(s). + if(!PRCDoResistSpell(OBJECT_SELF, oTarget,nPenetr, fDelay) && (oTarget != OBJECT_SELF)) + { + int nSaveDC = PRCGetSaveDC(oTarget,OBJECT_SELF); + // Roll damage for each target + int nDamage = PRCGetMetaMagicDamage(nDamageType, nCasterLvl, nDieSize, nBonusDam); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(nSpellID, DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF)) + nDamage += nCasterLvl; + nDamage += SpellDamagePerDice(OBJECT_SELF, nCasterLvl); + // Adjust damage according to Reflex Save, Evasion or Improved Evasion + nDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, nSaveType); + + // Apply effects to the currently selected target. + if(nDamage > 0) + { + effect eDamage = PRCEffectDamage(oTarget, nDamage, nDamageType); + effect eVis = EffectVisualEffect(nVictimEffect); + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget)); + DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); + PRCBonusDamage(oTarget); + } + } + } + + //Select the next target within the spell shape. + oTarget = MyNextObjectInShape(SHAPE_SPELLCONE, 11.0, lTargetLocation, FALSE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE); + } + + // Let the SR engine know that we are done and clear out school local var. + + PRCSetSchool(); +} + + +// Test main +//void main(){} diff --git a/src/include/spinc_dimdoor.nss b/src/include/spinc_dimdoor.nss new file mode 100644 index 0000000..4592c77 --- /dev/null +++ b/src/include/spinc_dimdoor.nss @@ -0,0 +1,372 @@ +//:://///////////////////////////////////////////// +//:: Spell Include: Dimension Door +//:: spinc_dimdoor +//:://///////////////////////////////////////////// +/** @file + Handles the internal functioning of the Dimension + Door -type spells, powers and SLAs. + + @author Ornedan + @date Created - 2005.10.07 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_inc_teleport" +//#include "prc_inc_listener" +#include "prc_inc_chat" +#include "x0_i0_position" + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +const int DIMENSIONDOOR_SELF = 0; +const int DIMENSIONDOOR_PARTY = 1; + + + +// Internal constants +const string DD_CASTERLVL = "PRC_DimensionDoor_CasterLvl"; +const string DD_TELEPORTINGPARTY = "PRC_DimensionDoor_TeleportingParty"; +const string DD_LOCATION = "PRC_DimensionDoor_Location"; +const string DD_DISTANCE = "PRC_DimensionDoor_Distance"; +const string DD_FIRSTSTAGE_DONE = "PRC_DimensionDoor_FirstStageDone"; +const string DD_SCRIPTTOCALL = "PRC_DimensionDoor_ScriptToCallOnTeleport"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Runs the using of a Dimension Door (-like) spell / power / SLA. + * The target location is either the location of a target object gotten + * with PRCGetSpellTargetObject() or if the target was a location on the + * ground, PRCGetSpellTargetLocation(). The actual target location may + * be different if bUseDirDist is TRUE. + * + * @param oCaster The creature using a spell / power / SLA to Dimension Door + * @param nCasterLvl The creature's caster / manifester level in regards to this use + * @param nSpellID The spellID currently in effect. If not specified, PRCGetSpellId() + * will be used to retrieve it. + * @param sScriptToCall Optionally, a script may be ExecuteScript'd for each of the teleportees + * after they have reached their destination. This is used to specify + * the name of such script. + * + * @param bSelfOrParty Determines whether this use of Dimension Door teleports only oCaster or + * also all it's faction memers within 10ft radius (subject to the general + * teleporting carry limits). + * Valid values: DIMENSIONDOOR_SELF and DIMENSIONDOOR_PARTY + * + * @param bUseDirDist If TRUE, the target location of the spell given via targeting cursor + * is used only to specify the direction of actual target location, + * relative to oCaster, and distance is specified via a listener. + * Otherwise, the target location specified via targeting cursor is used + * as the actual target. + */ +void DimensionDoor(object oCaster, int nCasterLvl, int nSpellID = -1, string sScriptToCall = "", + int bSelfOrParty = DIMENSIONDOOR_SELF, int bUseDirDist = FALSE + ); + + +/********************\ +* Internal Functions * +\********************/ + +/** + * Extracts data from local variables, calls DoDimensionDoorTeleport() using + * that data and then does CleanLocals(). + * + * @param oCaster creature using Dimension Door + */ +void DimensionDoorAux(object oCaster); + +/** + * Determines the target location of a Dimension Door. If using the + * direction & distance listener trick, the direction of the location + * is calculated from the base target location and distance is based + * on the caster's speech. + * Otherwise, the base target location is used. + * + * @param oCaster creature using Dimension Door + * @param nCasterLvl oCaster's caster or manifester level in regards to this + * Dimension Door use + * @param lBaseTarget the base target location, obtained via normal targeting + * @param fDistance the distance specified by speech for the direction & + * distance trick. If this is 0.0f, the trick is not used. + * + * @return The location where this Dimension Door is supposed jump + * it's targets to. + */ +location GetDimensionDoorLocation(object oCaster, int nCasterLvl, location lBaseTarget, float fDistance); + +/** + * Does the actual teleporting and VFX. + * + * @param oCaster The user of the Dimension Door being run + * @param lTarget The location to teleport to + * @param bTeleportingParty Whether this dimension door is teleporting only the user + * or also surrounding party. Determines the size of the VFX + * @param sScriptToCall The script to call for each teleporting object once it has + * reached the destination + */ +void DoDimensionDoorTeleport(object oCaster, location lTarget, int bTeleportingParty, string sScriptToCall); + +/** + * Deletes local variables used to preserve state over delays by these functions. + * + * @param oCaster creature on whom the data was stored + */ +void CleanLocals(object oCaster); + +/** + * A wrapper for assigning two commands at once after a delay from DoDimensionDoorTeleport(). + * First, the jump command and then, if the script is non-blank, a call to ExecuteScript + * the given post-jump script. + * + * @param oJumpee creature being teleported by Dimension Door + * @param lTarget the location to jump to + * @param sScriptToCall script for oJumpee to execute once it has jumped + */ +void AssignDimensionDoorCommands(object oJumpee, location lTarget, string sScriptToCall); + + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +void DimensionDoor(object oCaster, int nCasterLvl, int nSpellID = -1, string sScriptToCall = "", + int bSelfOrParty = DIMENSIONDOOR_SELF, int bUseDirDist = FALSE + ) +{ + if(DEBUG) DoDebug("spinc_dimdoor: Running DimensionDoor()" + (GetLocalInt(oCaster, DD_FIRSTSTAGE_DONE) ? ": ERROR: Called while in second stage!":"")); + + /* Main spellscript */ + if(!GetLocalInt(oCaster, DD_FIRSTSTAGE_DONE)) + { + // Get the spell's base target location + location lTarget = GetIsObjectValid(PRCGetSpellTargetObject()) ? // Are we teleporting to some object, or just at a spot on the ground? + GetLocation(PRCGetSpellTargetObject()) : // Teleporting to some object + PRCGetSpellTargetLocation(); // Teleporting to a spot on the ground + + + // Run the code to build an array of targets on the caster + GetTeleportingObjects(oCaster, nCasterLvl, bSelfOrParty == DIMENSIONDOOR_PARTY); + + if(!bUseDirDist) + { + SetLocalInt(oCaster, DD_FIRSTSTAGE_DONE, TRUE); + } + else + { + //SpawnListener("prc_dimdoor_aux", GetLocation(oCaster), "**", oCaster, 10.0f); + AddChatEventHook(oCaster, "prc_dimdoor_aux", 10.0f); + SendMessageToPCByStrRef(oCaster, 16825211); // "You have 10 seconds to speak the distance (in meters)" + DelayCommand(10.0f, CleanLocals(oCaster)); + } + + // Store the location in either case. For the direction and distance version, it's used to determine direction. + SetLocalLocation(oCaster, DD_LOCATION, lTarget); + + // Store various other data for use in DimensionDoorAux() + SetLocalInt(oCaster, DD_CASTERLVL, nCasterLvl); + SetLocalInt(oCaster, DD_TELEPORTINGPARTY, bSelfOrParty == DIMENSIONDOOR_PARTY); + SetLocalString(oCaster, DD_SCRIPTTOCALL, sScriptToCall); + } + if(GetLocalInt(oCaster, DD_FIRSTSTAGE_DONE)) + { + DimensionDoorAux(oCaster); + } +} + +void DimensionDoorAux(object oCaster) +{ + DoDimensionDoorTeleport(oCaster, + GetDimensionDoorLocation(oCaster, + GetLocalInt(oCaster, DD_CASTERLVL), + GetLocalLocation(oCaster, DD_LOCATION), + GetLocalFloat(oCaster, DD_DISTANCE) + ), + GetLocalInt(oCaster, DD_TELEPORTINGPARTY), + GetLocalString(oCaster, DD_SCRIPTTOCALL) + ); + CleanLocals(oCaster); +} + +location GetDimensionDoorLocation(object oCaster, int nCasterLvl, location lBaseTarget, float fDistance) +{ + if(DEBUG) DoDebug("spinc_dimdoor: GetDimensionDoorLocation(" + GetName(oCaster) + ", " + IntToString(nCasterLvl) + ", " + LocationToString(lBaseTarget) + ", " + FloatToString(fDistance) + ")"); + // Default to base target + location lTarget = lBaseTarget; + + // First, check if we are using the Direction & Distance mode + if(fDistance != 0.0f) + { + if(DEBUG) DoDebug("spinc_dimdoor: Calculating the new location based on direction and distance"); + // Make sure the distance jumped is in range + if(GetLocalInt(oCaster, "DimensionalJaunt") && fDistance > FeetToMeters(5.0 * (nCasterLvl))) + { + DeleteLocalInt(oCaster, "DimensionalJaunt"); + fDistance = FeetToMeters(5.0 * (nCasterLvl)); + string sPretty = FloatToString(fDistance); + sPretty = GetSubString(sPretty, 0, FindSubString(sPretty, ".") + 2); // Trunctate decimals to the last two + sPretty += "m"; // Note the unit. Since this is SI, the letter should be universal + // "You can't teleport that far, distance limited to" + SendMessageToPC(oCaster, GetStringByStrRef(16825210) + " " + sPretty); + } + else if(GetLocalInt(oCaster, "FleeTheScene") && fDistance > FeetToMeters(25.0 + 5.0 * (nCasterLvl / 2))) + { + DeleteLocalInt(oCaster, "FleeTheScene"); + fDistance = FeetToMeters(25.0 + 5.0 * (nCasterLvl / 2)); + string sPretty = FloatToString(fDistance); + sPretty = GetSubString(sPretty, 0, FindSubString(sPretty, ".") + 2); // Trunctate decimals to the last two + sPretty += "m"; // Note the unit. Since this is SI, the letter should be universal + // "You can't teleport that far, distance limited to" + SendMessageToPC(oCaster, GetStringByStrRef(16825210) + " " + sPretty); + } + else if(GetLocalInt(oCaster, "Treewalk") && fDistance > FeetToMeters(60.0)) + { + fDistance = FeetToMeters(60.0); + string sPretty = FloatToString(fDistance); + sPretty = GetSubString(sPretty, 0, FindSubString(sPretty, ".") + 2); // Trunctate decimals to the last two + sPretty += "m"; // Note the unit. Since this is SI, the letter should be universal + // "You can't teleport that far, distance limited to" + SendMessageToPC(oCaster, GetStringByStrRef(16825210) + " " + sPretty); + } + else if(fDistance > FeetToMeters(400.0 + 40.0 * nCasterLvl)) + { + fDistance = FeetToMeters(400.0 + 40.0 * nCasterLvl); + string sPretty = FloatToString(fDistance); + sPretty = GetSubString(sPretty, 0, FindSubString(sPretty, ".") + 2); // Trunctate decimals to the last two + sPretty += "m"; // Note the unit. Since this is SI, the letter should be universal + // "You can't teleport that far, distance limited to" + SendMessageToPC(oCaster, GetStringByStrRef(16825210) + " " + sPretty); + } + location lCaster = GetLocation(oCaster); + vector vCaster = GetPositionFromLocation(lCaster); + vector vBaseTarget = GetPositionFromLocation(lBaseTarget); + /*float fAngle = acos((vBaseTarget.x - vCaster.x) / GetDistanceBetweenLocations(lCaster, lBaseTarget)); + // The above formula only returns values [0, 180], so it needs to be mirrored if the caster is moving towards negative y + if((vBaseTarget.y - vCaster.y) < 0.0f) + fAngle = -fAngle; + */ + float fAngle = GetRelativeAngleBetweenLocations(lCaster, lBaseTarget); + if(DEBUG) DoDebug("spinc_dimdoor: Angle is " + FloatToString(fAngle)); + vector vTarget = Vector(vCaster.x + cos(fAngle) * fDistance, + vCaster.y + sin(fAngle) * fDistance, + vCaster.z + ); + // Sanity checks to make sure the location is not out of map bounds in the negative direction. + if(vTarget.x < 0.0f) vTarget.x = 0.0f; + if(vTarget.y < 0.0f) vTarget.y = 0.0f; + + lTarget = Location(GetAreaFromLocation(lBaseTarget), vTarget, GetFacingFromLocation(lBaseTarget)); + } + + if (GetLocalInt(oCaster, "BlackLabyrinth")) + return GenerateNewLocationFromLocation(lTarget, FeetToMeters(5.0*d4()), IntToFloat(Random(360)), IntToFloat(Random(360))); + /* This works, but it was replaced with the direction & distance trick above since that has more versatility in + selecting the target even though it is slower due to both not fitting on the radial. + Left here in case it needs to be brought back at a later date. + + else if(GetHasTeleportQuickSelection(oCaster, PRC_TELEPORT_ACTIVE_QUICKSELECTION)) + { + if(DEBUG) DoDebug("spinc_dimdoor: Quickselect is active"); + location lTest = MetalocationToLocation(GetActiveTeleportQuickSelection(oCaster, FALSE)); + + if(GetArea(oCaster) == GetAreaFromLocation(lTest)) + { + if(DEBUG) DoDebug("spinc_dimdoor: Quickselect is in same area"); + if(GetDistanceBetweenLocations(GetLocation(oCaster), lTest) <= FeetToMeters(400.0 + 40.0 * nCasterLvl)) + { + if(DEBUG) DoDebug("spinc_dimdoor: Quickselect is in range"); + // Used the active quickselection, so clear it + RemoveTeleportQuickSelection(oCaster, PRC_TELEPORT_ACTIVE_QUICKSELECTION); + lTarget = lTest; + } + } + }*/ + + // Return the target gotten after possible modifications + return lTarget; +} + +void DoDimensionDoorTeleport(object oCaster, location lTarget, int bTeleportingParty, string sScriptToCall) +{ + location lCaster = GetLocation(oCaster); + object oTarget; + int i; + + // Check if it's valid for the caster to teleport. If he can't go, no-one goes + if(GetCanTeleport(oCaster, lTarget, TRUE, TRUE)) + { + // Loop over the targets, checking if they can teleport. Redundant check on the caster, but shouldn't cause any trouble + for(i = 0; i < array_get_size(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY); i++) + { + oTarget = array_get_object(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY, i); + if(GetCanTeleport(oTarget, lTarget, TRUE)) + { + DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oTarget, 0.55)); + DelayCommand(1.5, AssignDimensionDoorCommands(oTarget, lTarget, sScriptToCall)); + } + } + + // VFX // + //DrawLineFromCenter(DURATION_TYPE_INSTANT, VFX_IMP_WIND, lCenter, 21.0, 0.0, 0.0, 29, 2.0, "z"); + //BeamPolygon(1, 73, lCenter, 5.0, 8, 3.0, "invisobj", 1.0, 0.0, 0.0, "z", -1, -1, 0.0, 1.0, 2.0); + + // Make the caster animate for the second of delay + AssignCommand(oCaster, ClearAllActions()); + AssignCommand(oCaster, SetFacingPoint(GetPositionFromLocation(lTarget))); + AssignCommand(oCaster, ActionPlayAnimation(ANIMATION_LOOPING_CONJURE1, 1.0f, 1.0f)); + + // First, spawn a circle of ligntning around the caster + BeamPolygon(DURATION_TYPE_PERMANENT, VFX_BEAM_LIGHTNING, lCaster, + bTeleportingParty ? FeetToMeters(10.0) : FeetToMeters(3.0), // Single TP: 3ft radius; Party TP: 10ft radius + bTeleportingParty ? 15 : 10, // More nodes for the group VFX + 1.5f, "prc_invisobj", 1.0f, 0.0f, 0.0f, "z", 0.0f, 0.0f, + -1, -1, 0.0f, 1.0f, // No secondary VFX + 2.0f // Non-zero lifetime, so the placeables eventually get removed + ); + + + //BeamPolygon(1, 73, lCaster, 5.0, 8, 3.0, "prc_invisobj", 1.0, 0.0, 0.0, "z", -1, -1, 0.0, 1.0, 2.0); + + // After a moment, draw a line from the caster to the destination + DelayCommand(1.0, DrawLineFromVectorToVector(DURATION_TYPE_INSTANT, VFX_IMP_WIND, GetArea(oCaster), GetPositionFromLocation(lCaster), GetPositionFromLocation(lTarget), 0.0, + FloatToInt(GetDistanceBetweenLocations(lCaster, lTarget)), // One VFX every meter + 0.5)); + // Then, spawn a circle of ligtning at the destination + DelayCommand(0.5, BeamPolygon(DURATION_TYPE_TEMPORARY, VFX_BEAM_LIGHTNING, lTarget, + bTeleportingParty ? FeetToMeters(10.0) : FeetToMeters(3.0), + bTeleportingParty ? 15 : 10, + 1.5, "prc_invisobj", 1.0, 0.0, 0.0, "z", 0.0f, 0.0f, -1, -1, 0.0, 1.0, 2.0)); + } + + // Cleanup + array_delete(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY); +} + +void AssignDimensionDoorCommands(object oJumpee, location lTarget, string sScriptToCall) +{ + AssignCommand(oJumpee, JumpToLocation(lTarget)); + if(sScriptToCall != "") + AssignCommand(oJumpee, ActionDoCommand(ExecuteScript(sScriptToCall, oJumpee))); + DelayCommand(0.5, ShadowPounce(oJumpee)); +} + +void CleanLocals(object oCaster) +{ + DeleteLocalInt (oCaster, DD_CASTERLVL); + DeleteLocalInt (oCaster, DD_TELEPORTINGPARTY); + DeleteLocalLocation(oCaster, DD_LOCATION); + DeleteLocalFloat (oCaster, DD_DISTANCE); + DeleteLocalInt (oCaster, DD_FIRSTSTAGE_DONE); + DeleteLocalString (oCaster, DD_SCRIPTTOCALL); +} + + +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/spinc_engimm.nss b/src/include/spinc_engimm.nss new file mode 100644 index 0000000..436a5a2 --- /dev/null +++ b/src/include/spinc_engimm.nss @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////// +// +// DoEnergyImmunity - Apply a 24 hour immunity to one element to the +// target. +// nDamageType - the damage type (DAMAGE_TYPE_xxx) to be immune to +// nVfx - The visual effect to use at cast time. +// +/////////////////////////////////////////////////////////////////////////// + +#include "prc_inc_spells" + +void DoEnergyImmunity (int nDamageType, int nVfx) +{ + PRCSetSchool(SPELL_SCHOOL_TRANSMUTATION); + + object oTarget = PRCGetSpellTargetObject(); + + // Determine the duration + float fDuration = PRCGetMetaMagicDuration(HoursToSeconds(24)); + + // Build a list of the duration effects which includes the actually immunity + // effect and all visual effects. + effect eList = EffectDamageResistance(nDamageType, 9999, 0); + effect eDur = EffectVisualEffect(VFX_DUR_PROTECTION_ELEMENTS); + eList = EffectLinkEffects(eList, eDur); + eDur = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE); + eList = EffectLinkEffects(eList, eDur); + + // Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, FALSE, SPELL_ENERGY_IMMUNITY); + + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eList, oTarget, fDuration, TRUE, -1, PRCGetCasterLevel(OBJECT_SELF)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nVfx), oTarget); + + PRCSetSchool(); +} + +// Test main +//void main(){} diff --git a/src/include/spinc_fdisk.nss b/src/include/spinc_fdisk.nss new file mode 100644 index 0000000..63f63bb --- /dev/null +++ b/src/include/spinc_fdisk.nss @@ -0,0 +1,32 @@ +void TransferItems(object oDisk, object oCaster) +{ + int bDrop = !GetIsObjectValid(oCaster); + if(bDrop) + { + oCaster = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_lootbag3", GetLocation(oDisk)); + SetLocalInt(oCaster, "NW_DO_ONCE", TRUE); + DestroyObject(oCaster, 0.4); + } + + object oInv; + int i; + for(i = 0; i < 14; i++) + { + oInv = GetItemInSlot(i, oDisk); + if(GetIsObjectValid(oInv)) + ActionGiveItem(oInv, oCaster); + } + + oInv = GetFirstItemInInventory(oDisk); + while(GetIsObjectValid(oInv)) + { + ActionGiveItem(oInv, oCaster); + oInv = GetNextItemInInventory(oDisk); + } +} + +void DestroyDisk(object oDisk) +{ + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_UNSUMMON), GetLocation(oDisk)); + DestroyObject(oDisk, 0.5); +} \ No newline at end of file diff --git a/src/include/spinc_greenfire.nss b/src/include/spinc_greenfire.nss new file mode 100644 index 0000000..abef2d8 --- /dev/null +++ b/src/include/spinc_greenfire.nss @@ -0,0 +1,80 @@ + +#include "prc_inc_spells" +#include "prc_add_spell_dc" +// +// Returns TRUE if the greenfire heartbeat has fired at least once. +// +int HasHeartbeatFired() +{ + return GetLocalInt(OBJECT_SELF, "SP_GREENFIRE_HBFIRED"); +} + +// +// Saves the fact that the greenfire heartbeat has fired. +// +void SetHeartbeatFired() +{ + SetLocalInt(OBJECT_SELF, "SP_GREENFIRE_HBFIRED", TRUE); +} + +// +// Gets the greenfire spell ID. +// +int GetGreenfireSpellID() +{ + return GetLocalInt(GetAreaOfEffectCreator(), "SP_GREENFIRE_SPELLID"); +} + +// +// Saves the specified spell ID as the greenfire spell ID. +// +void SetGreenfireSpellID(int nSpellID) +{ + SetLocalInt(OBJECT_SELF, "SP_GREENFIRE_SPELLID", nSpellID); +} + + +// +// Runs the greenfire spell effect against the specified target for the specified +// caster. +// +void DoGreenfire(int nDamageType, object oCaster, object oTarget) +{ + // Get the spell ID for greenfire, which is stored as a local int on the caster. + int nSpellID = GetLocalInt(oCaster, "SP_GREENFIRE_SPELLID"); + + // Get the amount of bonus damage, based on caster level. + int nCasterLevel = PRCGetCasterLevel(oCaster); + int nBonus = nCasterLevel; + if (nBonus > 10) nBonus = 10; + + if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster)) + { + // Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, TRUE, nSpellID, oCaster); + + int nPenetr = nCasterLevel + SPGetPenetr(); + + if (!PRCDoResistSpell(oCaster, oTarget,nPenetr)) + { + // Roll the damage and let the target make a reflex save if the + // heartbeat hasn't fired yet, once that happens targets get no save. + int nDamage = PRCGetMetaMagicDamage(nDamageType, 2, 6, 0, nBonus); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(nSpellID, DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, oCaster)) + nDamage += 2; + nDamage += SpellDamagePerDice(oCaster, 2); + if (!HasHeartbeatFired()) + nDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, + PRCGetSaveDC(oTarget,oCaster), SAVING_THROW_TYPE_ACID); + + // If we really did damage apply it to the target. + if (nDamage > 0) + SPApplyEffectToObject(DURATION_TYPE_INSTANT, + PRCEffectDamage(oTarget, nDamage, nDamageType), oTarget); + } + } +} + +// Test main +//void main(){} diff --git a/src/include/spinc_lessorb.nss b/src/include/spinc_lessorb.nss new file mode 100644 index 0000000..f5e7698 --- /dev/null +++ b/src/include/spinc_lessorb.nss @@ -0,0 +1,54 @@ + +#include "prc_inc_sp_tch" +//#include "prc_inc_combat" +//#include "prc_inc_spells" + +void DoLesserOrb(effect eVis, int nDamageType, int nSpellID = -1) +{ + PRCSetSchool(SPELL_SCHOOL_CONJURATION); + + object oCaster = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + int nCasterLvl = PRCGetCasterLevel(oCaster); + int nMetaMagic = PRCGetMetaMagicFeat(); + + int nDice = (nCasterLvl + 1)/2; + if (nDice > 5) nDice = 5; + + // Get the spell ID if it was not given. + if(-1 == nSpellID) nSpellID = PRCGetSpellId(); + + // Adjust the damage type of necessary. + nDamageType = PRCGetElementalDamageType(nDamageType, oCaster); + + if(spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster)) + { + // Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, TRUE, nSpellID); + + // Note that this spell has no spell resistance intentionally in the WotC Miniatures + // Handbook, bit powerful but that's how it is in the PnP book. + + // Make touch attack, saving result for possible critical + int nTouchAttack = PRCDoRangedTouchAttack(oTarget); + if (nTouchAttack > 0) + { + // Roll the damage, doing double damage on a crit. + int nDamage = PRCGetMetaMagicDamage(nDamageType, 1 == nTouchAttack ? nDice : (nDice * 2), 8); + nDamage += SpellSneakAttackDamage(OBJECT_SELF, oTarget); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(GetSpellId(), DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF)) + nDamage += nDice; + nDamage += SpellDamagePerDice(OBJECT_SELF, nDice); + // Apply the damage and the damage visible effect to the target. + SPApplyEffectToObject(DURATION_TYPE_INSTANT, PRCEffectDamage(oTarget, nDamage, nDamageType), oTarget); + PRCBonusDamage(oTarget); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + } + } + + PRCSetSchool(); +} + +// Test main +//void main(){} diff --git a/src/include/spinc_maze.nss b/src/include/spinc_maze.nss new file mode 100644 index 0000000..8e6a852 --- /dev/null +++ b/src/include/spinc_maze.nss @@ -0,0 +1,159 @@ +//::////////////////////////////////////////////// +//:: Maze scripts common functions +//:: spinc_maze +//::////////////////////////////////////////////// +/** @file + + + @author Ornedan + @date Created - 2005.10.18 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_inc_teleport" +#include "inc_dynconv" + +// Direction constants +const int NORTH = 0x1000; +const int SOUTH = 0x0100; +const int WEST = 0x0010; +const int EAST = 0x0001; + +// The escape DC. Should always be 20, but changeable for ease of testing +const int MAZE_ESCAPE_DC = 20; + +const int LOCAL_DEBUG = FALSE; + + + + +/** + * Debugging function for converting the direction constants to + * readable strings. + * + * @param n One of the direction constants in this file. + * @return Name of the constant. + */ +string DebugDir2String(int n) +{ + return (n == NORTH ? "NORTH": + n == SOUTH ? "SOUTH": + n == WEST ? "WEST" : + n == EAST ? "EAST" : + "ERROR" + ); +} + +/** + * Makes the creature move in the direction specified. + * + * @param oCreature The creature to command to move. + * @param nDirection One of the direction constants in this file. + */ +void GoDirection(object oCreature, int nDirection) +{ + if(LOCAL_DEBUG) DoDebug("GoDirection(): Direction is " + DebugDir2String(nDirection)); + // Generate location for the direction + location lDir; + vector v = GetPosition(oCreature); + if(nDirection == NORTH || nDirection == SOUTH) + { + v.y = nDirection == NORTH ? 160.0f : 0.0f; + lDir = Location(GetArea(oCreature), v, 0.0f); + } + else + { + v.x = nDirection == EAST ? 160.0f : 0.0f; + lDir = Location(GetArea(oCreature), v, 0.0f); + } + // Generate a location at the center of the trigger + location lTrigCenter; + v = GetPosition(OBJECT_SELF); + v.x = IntToFloat(((FloatToInt(v.x) / 10) * 10) + 5); + v.y = IntToFloat(((FloatToInt(v.y) / 10) * 10) + 5); + lTrigCenter = Location(GetArea(oCreature), v, GetFacing(oCreature)); + + // Nuke current action and move to the direction + AssignCommand(oCreature, ClearAllActions()); + AssignCommand(oCreature, ActionMoveToLocation(lTrigCenter)); + AssignCommand(oCreature, ActionMoveToLocation(lDir)); + // Turn the camera + AssignCommand(oCreature, SetCameraFacing((nDirection == NORTH ? 90.0f : + nDirection == SOUTH ? 270.0f : + nDirection == WEST ? 180.0f : + /*EAST*/ 0.0f + ), + -1.0f, -1.0f, CAMERA_TRANSITION_TYPE_FAST) + ); + + // Store the direction one should not move to from the next junction + switch(nDirection) + { + case NORTH: SetLocalInt(oCreature, "PRC_Maze_Direction", SOUTH); break; + case SOUTH: SetLocalInt(oCreature, "PRC_Maze_Direction", NORTH); break; + case WEST: SetLocalInt(oCreature, "PRC_Maze_Direction", EAST); break; + case EAST: SetLocalInt(oCreature, "PRC_Maze_Direction", WEST); break; + } +} + +void DoMazeVFX(location lLoc) +{ + DrawSpiral(DURATION_TYPE_INSTANT, VFX_IMP_HEAD_SONIC, lLoc, 1.0, 3.0, 0.0, 60, 2.5, 2.0, 0.0f, "z"/*, GetFacingFromLocation(lLoc)*/); +} + +void ReturnFromMaze(object oCreature) +{ + if(LOCAL_DEBUG) DoDebug("ReturnFromMaze() running\noCreature = '" + GetName(oCreature) + "'"); + location lReturn = GetLocalLocation(oCreature, "PRC_Maze_Return_Location"); + AssignCommand(oCreature, ClearAllActions(TRUE)); + DelayCommand(2.0f, AssignCommand(oCreature, JumpToLocation(lReturn))); + + // Do VFX + DoMazeVFX(GetLocation(oCreature)); +} + +void MazeEscapeHB(object oCreature, int nCountLeft) +{ + if(LOCAL_DEBUG) DoDebug("MazeEscapeHB() running\n" + + "oCreature = '" + GetName(oCreature) + "'\n" + + "nCountLeft = " + IntToString(nCountLeft) + "\n" + ); + + // If the counter has reached zero, ie. full 10 mins have passsed, return is automatic + if(nCountLeft <= 0) + { + if(LOCAL_DEBUG) DoDebug("MazeEscapeHB(): Timer has run out, returning from maze."); + ReturnFromMaze(oCreature); + return; + } + + // If it's a PC that hasn't made it's decision yet, don't run the int check + if(!GetLocalInt(oCreature, "PRC_Maze_PC_Waiting")) + { + if(LOCAL_DEBUG) DoDebug("MazeEscapeHB(): Running Int check."); + + // Int check versus MAZE_ESCAPE_DC to escape + int nD20 = d20(); + int nIntMod = GetAbilityModifier(ABILITY_INTELLIGENCE, oCreature); + int bResult = (nD20 + nIntMod) >= MAZE_ESCAPE_DC; + + // Inform the creature of the result + SendMessageToPC(oCreature, PRCGetRGB(7,7,15) + GetName(oCreature) + "" + // "Int check" "success" "failure" + PRCGetRGB(1,1,15) + " : " + GetStringByStrRef(16825701) + " : *" + (bResult ? GetStringByStrRef(5352) : GetStringByStrRef(5353)) + "* : (" + IntToString(nD20) + " + " + IntToString(nIntMod) + " = " + IntToString(nD20 + nIntMod) + " vs. DC: " + IntToString(MAZE_ESCAPE_DC) + ")"); + + // Return from the maze if the check was successfull + if(bResult) + { + ReturnFromMaze(oCreature); + return; + } + } + + // Schedule next check + DelayCommand(6.0f, MazeEscapeHB(oCreature, nCountLeft - 1)); +} + +// Test main +//void main(){} diff --git a/src/include/spinc_necro_cyst.nss b/src/include/spinc_necro_cyst.nss new file mode 100644 index 0000000..1e767bf --- /dev/null +++ b/src/include/spinc_necro_cyst.nss @@ -0,0 +1,124 @@ +//:://///////////////////////////////////////////// +//:: Necrotic Cyst spell includes +//:: spinc_necro_cyst +//::////////////////////////////////////////////// +/** @file + This file contains functions and constants + related to the Necrotic Cyst spell line. + + + @author Ornedan + @date Created - 2005.09.21 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +/////////////////////////////////////// +/* Function prototypes */ +/////////////////////////////////////// + +/** + * Checks if the given creature may cast Necrotic Cyst spells. + * + * @param oPC The creature whose eligibility to test. + * @return TRUE if the creature is allowed to cast + * Necrotic Cyst spells, FALSE otherwise. + */ +int GetCanCastNecroticSpells(object oPC); + +/** + * Checks if the given creature has a necrotic cyst. + * + * @param oCreature Creature to check. + * @return TRUE if the creature has a necrotic cyst, + * FALSE otherwise. + */ +int GetHasNecroticCyst(object oCreature); + + + +/////////////////////////////////////// +/* Constant declarations */ +/////////////////////////////////////// + +const string NECROTIC_EMPOWERMENT_MARKER = "HAS_NECROTIC_EMPOWER"; +const string NECROTIC_CYST_MARKER = "HAS_NECROTIC_CYST"; +const int nNoNecCyst = 16829317; +const int nNoMotherCyst = 16829318; +const int nNecEmpower = 16829319; +const int nGaveCyst = 16829316; + +/////////////////////////////////////// +/* Function declarations */ +/////////////////////////////////////// +//#include "prc_alterations" // No point in including prc_alteractions +//#include "inc_utility" +#include "prc_x2_itemprop" // required for - SetPersistantLocalInt +//#include "prc_spell_const" + +int GetCanCastNecroticSpells(object oPC) +{ + int bReturn = TRUE; + + // check for Necrotic Empowerment on caster + if(GetHasSpellEffect(SPELL_NECROTIC_EMPOWERMENT, oPC)) + { + // "You cannot cast spells utilizing your Mother Cyst while under the effect of Necrotic Empowerment." + FloatingTextStrRefOnCreature(nNecEmpower, oPC); + bReturn = FALSE; + } + // check for Mother Cyst + if(!GetHasFeat(FEAT_MOTHER_CYST, oPC) && (!GetIsObjectValid(GetSpellCastItem()))) + { + // "You must have a Mother Cyst to cast this spell." + FloatingTextStrRefOnCreature(nNoMotherCyst, oPC); + bReturn = FALSE; + } + + if(DEBUG) DoDebug("spinc_necrocyst: GetCanCastNecroticSpells():\n" + + "oPC = '" + GetName(oPC) + "'"); + + return bReturn; +} + +int GetHasNecroticCyst(object oCreature) +{ + return GetPersistantLocalInt(oCreature, NECROTIC_CYST_MARKER); + //if(DEBUG) DoDebug("spinc_necrocyst: GetHasNecroticCyst():\n" + // + "oCreature = '" + GetName(oCreature) + "'"); +} + +void GiveNecroticCyst(object oCreature) +{ + + SetPersistantLocalInt(oCreature, NECROTIC_CYST_MARKER, 1); + FloatingTextStrRefOnCreature(nGaveCyst, OBJECT_SELF); + itemproperty iCystDam = (ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1)); + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + if (GetIsObjectValid(oArmor)) + { + //add item prop with DURATION_TYPE_PERMANENT + IPSafeAddItemProperty(oArmor, iCystDam, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + } + + else + { + object oSkin = GetPCSkin(oCreature); + IPSafeAddItemProperty(oSkin, iCystDam, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + } + + //Keep property on armor or hide + ExecuteScript ("prc_keep_onhit_a", oCreature); + + if(DEBUG) DoDebug("spinc_necrocyst: GiveNecroticCyst():\n" + + "oCreature = '" + GetName(oCreature) + "'"); + +} + +void RemoveCyst(object oCreature) +{ + SetPersistantLocalInt(oCreature, NECROTIC_CYST_MARKER, 0); +} + +// Test main +//void main(){} diff --git a/src/include/spinc_orb.nss b/src/include/spinc_orb.nss new file mode 100644 index 0000000..709d9fc --- /dev/null +++ b/src/include/spinc_orb.nss @@ -0,0 +1,57 @@ +#include "prc_inc_sp_tch" +#include "prc_add_spell_dc" + +void DoOrb(effect eVis, effect eFailSave, int nSaveType, int nDamageType, int nSpellID = -1) +{ + PRCSetSchool(SPELL_SCHOOL_EVOCATION); + + object oTarget = PRCGetSpellTargetObject(); + int nCasterLvl = PRCGetCasterLevel(OBJECT_SELF); + int nAtk = PRCDoRangedTouchAttack(oTarget, TRUE, OBJECT_SELF); + + if(nAtk) + { + int nDice = nCasterLvl; + if (nDice > 15) nDice = 15; + + int nPenetr = nCasterLvl + SPGetPenetr(); + + // Get the spell ID if it was not given. + if (-1 == nSpellID) nSpellID = PRCGetSpellId(); + + // Adjust the damage type of necessary. + nDamageType = PRCGetElementalDamageType(nDamageType, OBJECT_SELF); + + effect eMissile = EffectVisualEffect(VFX_IMP_MIRV); + + if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF)) + { + //Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, TRUE, nSpellID); + + //Roll damage for each target + int nDamage = PRCGetMetaMagicDamage(nDamageType, nDice, 6); + // Acid Sheath adds +1 damage per die to acid descriptor spells + if (GetHasDescriptor(GetSpellId(), DESCRIPTOR_ACID) && GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF)) + nDamage += nDice; + nDamage += SpellDamagePerDice(OBJECT_SELF, nDice); + // Apply the damage and the damage visible effect to the target. + ApplyTouchAttackDamage(OBJECT_SELF, oTarget, nAtk, nDamage, nDamageType); + PRCBonusDamage(oTarget); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget); + + if(PRCGetIsAliveCreature(oTarget)) + { + // If the target failed it's save then apply the failed save effect as well for 1 round. + if (!PRCMySavingThrow(nSaveType, oTarget, PRCGetSaveDC(oTarget, OBJECT_SELF))) + { + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFailSave, oTarget, RoundsToSeconds(1),TRUE,-1,nCasterLvl); + } + } + } + } + PRCSetSchool(); +} + +// Test main +//void main(){} diff --git a/src/include/spinc_remeffct.nss b/src/include/spinc_remeffct.nss new file mode 100644 index 0000000..b513d3e --- /dev/null +++ b/src/include/spinc_remeffct.nss @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////// +//@file +//Include for spell removal checks +// +// +//void SpellRemovalCheck +// +//This function is used for the removal of effects and ending of spells that +//cannot be ended in a normal fashion. +// +/////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +void SpellRemovalCheck(object oCaster, object oTarget); + + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_spells" + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void SpellRemovalCheck(object oCaster, object oTarget) +{ + //Get Spell being cast + int nSpellID = PRCGetSpellId(); + + //Set up spell removals for individual spells + //Remove Curse + if(nSpellID == SPELL_REMOVE_CURSE) + { + //Ghoul Gauntlet + if(GetHasSpellEffect(SPELL_GHOUL_GAUNTLET, oTarget)) + PRCRemoveSpellEffects(SPELL_GHOUL_GAUNTLET, oCaster, oTarget); + + //Touch of Juiblex + if(GetHasSpellEffect(SPELL_TOUCH_OF_JUIBLEX, oTarget)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(3), DAMAGE_TYPE_MAGICAL), oTarget); + PRCRemoveSpellEffects(SPELL_TOUCH_OF_JUIBLEX, oCaster, oTarget); + } + + //Evil Eye + if(GetHasSpellEffect(SPELL_EVIL_EYE, oTarget)) + PRCRemoveSpellEffects(SPELL_EVIL_EYE, oCaster, oTarget); + } + + //Remove Disease + if(nSpellID == SPELL_REMOVE_DISEASE) + { + //Ghoul Gauntlet + if(GetHasSpellEffect(SPELL_GHOUL_GAUNTLET, oTarget)) + PRCRemoveSpellEffects(SPELL_GHOUL_GAUNTLET, oCaster, oTarget); + } + + //Heal + if(nSpellID == SPELL_HEAL + || nSpellID == SPELL_MASS_HEAL) + { + //Ghoul Gauntlet + if(GetHasSpellEffect(SPELL_GHOUL_GAUNTLET, oTarget)) + PRCRemoveSpellEffects(SPELL_GHOUL_GAUNTLET, oCaster, oTarget); + + //Energy Ebb + if(GetHasSpellEffect(SPELL_ENERGY_EBB, oTarget)) + PRCRemoveSpellEffects(SPELL_ENERGY_EBB, oCaster, oTarget); + + //Touch of Juiblex + if(GetHasSpellEffect(SPELL_TOUCH_OF_JUIBLEX, oTarget)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(3), DAMAGE_TYPE_MAGICAL), oTarget); + PRCRemoveSpellEffects(SPELL_TOUCH_OF_JUIBLEX, oCaster, oTarget); + } + } + + //Restoration + if(nSpellID == SPELL_RESTORATION) + { + //Ghoul Gauntlet + if(GetHasSpellEffect(SPELL_GHOUL_GAUNTLET, oTarget)) + PRCRemoveSpellEffects(SPELL_GHOUL_GAUNTLET, oCaster, oTarget); + + //Energy Ebb + if(GetHasSpellEffect(SPELL_ENERGY_EBB, oTarget)) + PRCRemoveSpellEffects(SPELL_ENERGY_EBB, oCaster, oTarget); + } + + //Greater Restoration + if(nSpellID == SPELL_GREATER_RESTORATION) + { + //Ghoul Gauntlet + if(GetHasSpellEffect(SPELL_GHOUL_GAUNTLET, oTarget)) + PRCRemoveSpellEffects(SPELL_GHOUL_GAUNTLET, oCaster, oTarget); + + //Energy Ebb + if(GetHasSpellEffect(SPELL_ENERGY_EBB, oTarget)) + PRCRemoveSpellEffects(SPELL_ENERGY_EBB, oCaster, oTarget); + + //Touch of Juiblex + if(GetHasSpellEffect(SPELL_TOUCH_OF_JUIBLEX, oTarget)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(3), DAMAGE_TYPE_MAGICAL), oTarget); + PRCRemoveSpellEffects(SPELL_TOUCH_OF_JUIBLEX, oCaster, oTarget); + } + } + + //Dispel Magic + //Greater Dispelling + //Mordenkainen's Disjunction + if(nSpellID == SPELL_DISPEL_MAGIC + || nSpellID == SPELL_GREATER_DISPELLING + || nSpellID == SPELL_MORDENKAINENS_DISJUNCTION) + { + //Ghoul Gauntlet + if(GetHasSpellEffect(SPELL_GHOUL_GAUNTLET, oTarget)) + PRCRemoveSpellEffects(SPELL_GHOUL_GAUNTLET, oCaster, oTarget); + + //Eternity of Torture + if(GetHasSpellEffect(SPELL_ETERNITY_OF_TORTURE, oTarget)) + { + AssignCommand(oTarget, SetCommandable(TRUE, oTarget)); + PRCRemoveSpellEffects(SPELL_ETERNITY_OF_TORTURE, oCaster, oTarget); + } + } + + //Limited Wish + //Wish + //Miracle +} + +// Checks if the effect is specific to a plot and should not be removed normally +int GetShouldNotBeRemoved(effect eEff) +{ + object oCreator = GetEffectCreator(eEff); + if(GetTag(oCreator) == "q6e_ShaorisFellTemple") + return TRUE; + + if(GetEffectSpellId(eEff) >= VESTIGE_AMON && VESTIGE_ABYSM >= GetEffectSpellId(eEff)) + return TRUE; + + if(GetEffectSpellId(eEff) >= MELD_ACROBAT_BOOTS && MELD_ELDER_SPIRIT >= GetEffectSpellId(eEff)) + return TRUE; + + return FALSE; +} + +// Test main +//void main(){} diff --git a/src/include/spinc_telecircle.nss b/src/include/spinc_telecircle.nss new file mode 100644 index 0000000..c3801d3 --- /dev/null +++ b/src/include/spinc_telecircle.nss @@ -0,0 +1,162 @@ +//:://///////////////////////////////////////////// +//:: Spell Include: Teleportation Circle +//:: spinc_telecircle +//:://///////////////////////////////////////////// +/** @file + Handles the internal functioning of the + Teleportation Circle -type spells, powers + and SLAs. + + @author Ornedan + @date Created - 2005.11.12 + @date Modified - 2006.06.04 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_inc_teleport" + + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +// Internal constants +const string TC_CASTERLEVEL = "PRC_TeleportCircle_CasterLvl"; +const string TC_ISVISIBLE = "PRC_TeleportCircle_IsVisible"; +const string TC_ISEXTENDED = "PRC_TeleportCircle_Extended"; +const string TC_FIRSTSTAGE_DONE = "PRC_TeleportCircle_FirstPartDone"; +const string TC_LOCATION = "PRC_TeleportCircle_TargetLocation"; + +const int TC_NUM_TRAPS = 4; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Runs the using of a Teleportation Circle (-like) spell / power / SLA. + * The destination is gotten using a conversation or, if active, the + * caster's quickselection. + * NOTE: You will need to call spellhook / powerhook / specific-whatever + * before this function. + * + * + * @param oCaster The creature using a spell / power / SLA create + * a Teleportation Circle + * @param nCasterLvl The creature's caster / manifester level in regards to + * this use + * @param bVisible Whether the circle should be visible or not. A visible circle + * is marked with VFX and has a detection DC of 0. A hidden circle + * has a detection DC of 34. + * @param bExtended Whether this use of Teleportation Circle had the Extend Metaeffect + * applied to it. + */ +void TeleportationCircle(object oCaster, int nCasterLvl, int bVisible, int bExtended); + + +/********************\ +* Internal Functions * +\********************/ + +/** + * Does the actual creation of the circle. Called once the user has specified + * the target location to use. + * + * @param oCaster creature using Teleportation Circle + */ +void TeleportationCircleAux(object oCaster); + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + + +void TeleportationCircleAux(object oCaster) +{ + // Retrieve the target location from the variable + location lCircleTarget = GetLocalLocation(oCaster, TC_LOCATION); + location lTarget; + int bVisible = GetLocalInt(oCaster, TC_ISVISIBLE); + int nCasterLvl = GetLocalInt(oCaster, TC_CASTERLEVEL); + int bExtended = GetLocalInt(oCaster, TC_ISEXTENDED); + float fDuration = nCasterLvl * 10 * 60.0f * (bExtended ? 2 : 1); + float fFacing = GetFacing(oCaster); + float fDistance = FeetToMeters(5.0f) + 0.2; + vector vTarget = GetPosition(oCaster); + vTarget.x += cos(fFacing) * fDistance; + vTarget.y += sin(fFacing) * fDistance; + lTarget = Location(GetArea(oCaster), vTarget, fFacing); + + // Create the actual circle, in front of the caster + ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, + EffectAreaOfEffect(AOE_PER_TELEPORTATIONCIRCLE, "prc_telecirc_oe"), + lTarget, + fDuration + ); + // Get an object reference to the newly created AoE + object oAoE = MyFirstObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + while(GetIsObjectValid(oAoE)) + { + // Test if we found the correct AoE + if(GetTag(oAoE) == Get2DACache("vfx_persistent", "LABEL", AOE_PER_TELEPORTATIONCIRCLE) && + !GetLocalInt(oAoE, "PRC_TeleCircle_AoE_Inited") + ) + { + break; + } + // Didn't find, get next + oAoE = MyNextObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT); + } + if(DEBUG) if(!GetIsObjectValid(oAoE)) DoDebug("ERROR: Can't find area of effect for Teleportation Circle!"); + + // Store data on the AoE + SetLocalLocation(oAoE, "TargetLocation", lCircleTarget); + SetLocalInt(oAoE, "IsVisible", bVisible); + + // Make the AoE initialise the trap trigger and possibly the VFX heartbeat + ExecuteScript("prc_telecirc_aux", oAoE); + + + // A VFX (momentary, circular, impressive :D ) at the circle's location. + // Do even if hidden circle so that the caster knows where it really ended up + DrawRhodonea(DURATION_TYPE_INSTANT, VFX_IMP_HEAD_MIND, lTarget, FeetToMeters(5.0f), 0.25, 0.0, 180, 12.0, 4.0, 0.0, "z"); + + // Cleanup + DeleteLocalInt(oCaster, TC_CASTERLEVEL); + DeleteLocalInt(oCaster, TC_ISVISIBLE); + DeleteLocalInt(oCaster, TC_ISEXTENDED); + DeleteLocalInt(oCaster, TC_FIRSTSTAGE_DONE); + DeleteLocalLocation(oCaster, TC_LOCATION); +} + +void TeleportationCircle(object oCaster, int nCasterLvl, int bVisible, int bExtended) +{ + if(DEBUG) DoDebug("spinc_telecircle: Running TeleportationCircle()" + (GetLocalInt(oCaster, TC_FIRSTSTAGE_DONE) ? ": ERROR: Called while in second stage!":"") + "\n" + + "oCaster = " + DebugObject2Str(oCaster) + "\n" + + "nCasterLvl = " + IntToString(nCasterLvl) + "\n" + + "bVisible = " + DebugBool2String(bVisible) + "\n" + + "bExtended = " + DebugBool2String(bExtended) + "\n" + ); + + // Get whether we are executing the first or the second part of the script + if(!GetLocalInt(oCaster, TC_FIRSTSTAGE_DONE)) + { + // Store the caster level + SetLocalInt(oCaster, TC_CASTERLEVEL, nCasterLvl); + // Store the spellID + SetLocalInt(oCaster, TC_ISVISIBLE, bVisible); + // Store whether the spell is extended + SetLocalInt(oCaster, TC_ISEXTENDED, bExtended); + // Mark the first part done + SetLocalInt(oCaster, TC_FIRSTSTAGE_DONE, TRUE); + // Now, get the location to have the circle point at. + ChooseTeleportTargetLocation(oCaster, "prc_telecirc_aux", TC_LOCATION, FALSE, TRUE); + } +} + + +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/spinc_teleport.nss b/src/include/spinc_teleport.nss new file mode 100644 index 0000000..9d06bc9 --- /dev/null +++ b/src/include/spinc_teleport.nss @@ -0,0 +1,191 @@ +//:://///////////////////////////////////////////// +//:: Spell Include: Teleport +//:: spinc_teleport +//:://///////////////////////////////////////////// +/** @file + Handles the internal functioning of the (Greater) + Teleport -type spells, powers and SLAs. + + @author Ornedan + @date Created - 2005.11.04 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_inc_teleport" + + +////////////////////////////////////////////////// +/* Constant defintions */ +////////////////////////////////////////////////// + +// Internal constants +const string TP_LOCATION = "PRC_Teleport_TargetLocation"; +const string TP_ERRORLESS = "PRC_Teleport_Errorless"; +const string TP_FIRSTSTAGE_DONE = "PRC_Teleport_FirstPartDone"; +const string TP_END_SCRIPT = "PRC_Teleport_ScriptToCallAtEnd"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Runs the using of a (Greater) Teleport (-like) spell / power / SLA. + * The destination is gotten using a conversation or, if active, the + * caster's quickselection. + * NOTE: You will need to call spellhook / powerhook / specific-whatever + * before this function. + * + * + * @param oCaster The creature using a spell / power / SLA to Teleport + * @param nCasterLvl The creature's caster / manifester level in regards to this use + * @param bTeleportParty Whether to teleport only the user or also faction members within + * 10ft of the user. If TRUE, teleports party in addition to the user, + * otherwise just the user. + * @param bErrorLess Whether this teleportation is subject to potential error a 'la Teleport + * or errorless like Greater Teleport. If TRUE, there is no chance of + * ending anywhere else other than the location selected. + * @param sScriptToCall Optionally, a script may be ExecuteScript'd for each of the teleportees + * after they have reached their destination. This is used to specify + * the name of such script. + */ +void Teleport(object oCaster, int nCasterLvl, int bTeleportParty, int bErrorLess, string sScriptToCall = ""); + + +/********************\ +* Internal Functions * +\********************/ + +/** + * Does the actual teleporting. Called once the user has specified + * the location to use. + * + * @param oCaster creature using Teleport + */ +void TeleportAux(object oCaster); + +/** + * A visual effects heartbeat that runs when using party teleport while + * waiting for the caster to decide the target location. + * Outlines the 10ft radius. The HB will cease when the caster + * makes the decision or moves from the location they were at at the + * beginning of the HB. + * + * @param oCaster User of a Teleport + * @param lCaster The location of the caster when they started the use + */ +void VFX_HB(object oCaster, location lCaster); + +/** + * A wrapper for assigning two commands at once after a delay from TeleportAux() + * First, the jump command and then, if the script is non-blank, a call to ExecuteScript + * the given post-jump script. + * + * @param oJumpee creature being teleported by Teleport + * @param lTarget the location to jump to + * @param sScriptToCall script for oJumpee to execute once it has jumped + */ +void AssignTeleportCommands(object oJumpee, location lTarget, string sScriptToCall); + + +////////////////////////////////////////////////// +/* Function defintions */ +////////////////////////////////////////////////// + +void VFX_HB(object oCaster, location lCaster) +{ + // End the VFX once the caster either finishes the spell or moves + if(GetLocalInt(oCaster, TP_FIRSTSTAGE_DONE) && GetLocation(oCaster) == lCaster) + { + // Draw to circles, going in the opposite directions + DrawCircle(DURATION_TYPE_INSTANT, VFX_IMP_CONFUSION_S, lCaster, FeetToMeters(10.0f), 0.0, 50, 1.0, 6.0, 0.0, "z"); + DrawCircle(DURATION_TYPE_INSTANT, VFX_IMP_CONFUSION_S, lCaster, FeetToMeters(10.0f), 0.0, 50, 1.0, 6.0, 180.0, "z"); + DelayCommand(6.0f, VFX_HB(oCaster, lCaster)); + } +} + +void TeleportAux(object oCaster) +{ + // Retrieve the target location from the variable + location lTarget = GetLocalLocation(oCaster, TP_LOCATION); + location lCaster = GetLocation(oCaster); + string sScriptToCall = GetLocalString(oCaster, TP_END_SCRIPT); + // Teleportation error handling code + lTarget = GetTeleportError(lTarget, oCaster, GetLocalInt(oCaster, TP_ERRORLESS)); + + int i; + object oTarget; + + // Check if it's valid for the caster to teleport. If he can't go, no-one goes + if(GetCanTeleport(oCaster, lTarget, TRUE, TRUE)) + { + // VFX on the starting location + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_TELEPORT_OUT), lCaster); + + // Loop over the targets, checking if they can teleport. Redundant check on the caster, but shouldn't cause any trouble + for(i = 0; i < array_get_size(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY); i++) + { + oTarget = array_get_object(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY, i); + if(GetCanTeleport(oTarget, lTarget, TRUE)) + { + DelayCommand(1.0f, AssignTeleportCommands(oTarget, lTarget, sScriptToCall)); + } + } + + // VFX at arrival location. May run out before the teleporting people arrive + DelayCommand(1.0f, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_TELEPORT_IN), lTarget)); + } + + // Cleanup + DeleteLocalInt(oCaster, TP_FIRSTSTAGE_DONE); + DeleteLocalLocation(oCaster, TP_LOCATION); + DeleteLocalInt(oCaster, TP_ERRORLESS); + DeleteLocalString(oCaster, TP_END_SCRIPT); + array_delete(oCaster, PRC_TELEPORTING_OBJECTS_ARRAY); +} + +void AssignTeleportCommands(object oJumpee, location lTarget, string sScriptToCall) +{ + AssignCommand(oJumpee, JumpToLocation(lTarget)); + if(sScriptToCall != "") + AssignCommand(oJumpee, ActionDoCommand(ExecuteScript(sScriptToCall, oJumpee))); + DelayCommand(0.5, ShadowPounce(oJumpee)); +} + +void Teleport(object oCaster, int nCasterLvl, int bTeleportParty, int bErrorLess, string sScriptToCall = "") +{ + if(DEBUG) DoDebug("spinc_teleport: Running Teleport()" + /*(GetLocalInt(oCaster, TP_FIRSTSTAGE_DONE) ? ": ERROR: Called while in second stage!":*/("\n" + + "oCaster = " + DebugObject2Str(oCaster) + "\n" + + "nCasterLvl = " + IntToString(nCasterLvl) + "\n" + + "bTeleportParty = " + DebugBool2String(bTeleportParty) + "\n" + + "bErrorLess = " + DebugBool2String(bErrorLess) + "\n" + + "sScriptToCall = '" + sScriptToCall + "'\n" + /*)*/)); + + // Get whether we are executing the first or the second part of the script + /*if(!GetLocalInt(oCaster, TP_FIRSTSTAGE_DONE)) + {*/ + location lCaster = GetLocation(oCaster); + + // Run the code to build an array of targets on the caster + GetTeleportingObjects(oCaster, nCasterLvl, bTeleportParty); + + // Do VFX while waiting for the location select. Only for party TP + if(bTeleportParty) + DelayCommand(0.01f, VFX_HB(oCaster, lCaster)); + + // Mark the first part done + SetLocalInt(oCaster, TP_FIRSTSTAGE_DONE, TRUE); + // Store whether this usage is errorless + SetLocalInt(oCaster, TP_ERRORLESS, bErrorLess); + // Store the name of the script to call at the end + SetLocalString(oCaster, TP_END_SCRIPT, sScriptToCall); + // Now, get the location to teleport to. + ChooseTeleportTargetLocation(oCaster, "prc_teleport_aux", TP_LOCATION, FALSE, TRUE); + //} +} + + +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/spinc_trans.nss b/src/include/spinc_trans.nss new file mode 100644 index 0000000..c05fd23 --- /dev/null +++ b/src/include/spinc_trans.nss @@ -0,0 +1,104 @@ +//:://///////////////////////////////////////////// +//:: Spell Include: Transposition +//:: spinc_trans +//:://///////////////////////////////////////////// +/** @file + Common code for Benign Transposition and + Baleful Transposition. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "prc_inc_teleport" +#include "prc_add_spell_dc" +///////////////////////// +// Function Prototypes // +///////////////////////// + +/** + * Changes the positions of current spellcaster and spell target. + * + * @param bAllowHostile If this flag is FALSE then the target creature + * must be a member of the caster's party (have the same faction + * leader). If this flag is false then it may be a party member + * or a hostile creature. + * @param bRunSpellhook By default, this is TRUE and the function runs the spellhook. + * But in some cases, such as feats or psionic powers, the script + * calling this function may need to handle the matter differently. + * In such cases, set this to FALSE, which will cause the spellhook + * not to be run from this function. + */ +void DoTransposition(int bAllowHostile, int bRunSpellhook = TRUE); + + +////////////////////////// +// Function Definitions // +////////////////////////// + +// +// Displays transposition VFX. +// +void TransposeVFX(object o1, object o2) +{ + // Apply vfx to the creatures moving. + effect eVis = EffectVisualEffect(VFX_IMP_HEALING_X); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, o1); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, o2); +} + +// +// Transposes the 2 creatures. +// +void Transpose(object o1, object o2) +{ + // Get the locations of the 2 creatures to swap, keeping the facings + // the same. + location loc1 = Location(GetArea(o1), GetPosition(o1), GetFacing(o2)); + location loc2 = Location(GetArea(o2), GetPosition(o2), GetFacing(o1)); + + // Make sure both creatures are capable of being teleported + if(!(GetCanTeleport(o1, loc2, TRUE) && GetCanTeleport(o2, loc1, TRUE))) + return; + + // Swap the creatures. + AssignCommand(o2, JumpToLocation(loc1)); + DelayCommand(0.1, AssignCommand(o2, ClearAllActions(TRUE))); + AssignCommand(o1, JumpToLocation(loc2)); + DelayCommand(0.1, AssignCommand(o1, ClearAllActions(TRUE))); + + DelayCommand(0.1, TransposeVFX(o1, o2)); +} + + +void DoTransposition(int bAllowHostile, int bRunSpellhook = TRUE) +{ + PRCSetSchool(SPELL_SCHOOL_CONJURATION); + // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell + if(bRunSpellhook) + if(!X2PreSpellCastCode()) return; + + object oCaster = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + if (!GetIsDead(oTarget)) + { + // Get the spell target. If he has the same faction leader we do (i.e. he's in the party) + // or he's a hostile target and hostiles are allowed then we will do the switch. + int bParty = GetFactionLeader(oTarget) == GetFactionLeader(oCaster); + if(bParty || (bAllowHostile && spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster))) + { + // Targets outside the party get a will save and SR to resist. + if (bParty || + (!PRCDoResistSpell(oCaster, oTarget) && !PRCMySavingThrow(SAVING_THROW_WILL, oTarget, PRCGetSaveDC(oTarget, oCaster)))) + { + //Fire cast spell at event for the specified target + PRCSignalSpellEvent(oTarget, !bParty); + + // Move the creatures. + DelayCommand(0.1, Transpose(oCaster, oTarget)); + } + } + } + + PRCSetSchool(); +} diff --git a/src/include/tob_inc_martlore.nss b/src/include/tob_inc_martlore.nss new file mode 100644 index 0000000..0365057 --- /dev/null +++ b/src/include/tob_inc_martlore.nss @@ -0,0 +1,143 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Martial Lore Skill +//:: tob_inc_martlore +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the Martial Lore skill + See page #28 of Tome of Battle + + Functions below are called by the initiator as + he makes a maneuver. + + @author Stratovarius + @date Created - 2007.3.19 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the maneuver that the Initiator just used + * @param oInitiator The maneuver initiator + * @param nSpellId maneuver to check + * + * @return nothing, uses SendMessageToPC to give results + */ +void IdentifyManeuver(object oInitiator, int nSpellId); + +/** + * Returns the disciplines that the Initiator has + * @param oInitiator The maneuver initiator + * + * @return nothing, uses SendMessageToPC to give results + */ +void IdentifyDiscipline(object oInitiator); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "tob_inc_tobfunc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _DoMartialLoreCheck(object oInitiator, object oCheck, int nManeuverLevel, int nSpellId) +{ + // NPCs wouldn't benefit from being told the name of the maneuver + if(!GetIsPC(oCheck)) + return; + + // No Bonus normally + int nSwordSage = 0; + + if(TOBGetHasDisciplineFocus(oInitiator, nSpellId)) nSwordSage = 2; + + // Roll the check, DC is reduced by Swordsage bonus instead of bonus on check. Same end result. + if(GetIsSkillSuccessful(oCheck, SKILL_MARTIAL_LORE, 10 + nManeuverLevel - nSwordSage)) + { // get the name of the initiator and maneuver + FloatingTextStringOnCreature(GetName(oInitiator) + " Initiates " + GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))), oCheck, FALSE); + } + else // Skill check failed + { + FloatingTextStringOnCreature(GetName(oInitiator) + " Initiates Unknown Maneuver", oCheck, FALSE); + } +} + +void _DoDisciplineCheck(object oInitiator, object oCheck, int nInitiatorLevel) +{ + // NPCs wouldn't benefit from being told the disciplines + if(!GetIsPC(oCheck)) + return; + + if(GetIsSkillSuccessful(oCheck, SKILL_MARTIAL_LORE, 20 + nInitiatorLevel)) + { + // Check the Disciplines, 1 to 9 + string sDiscipline = ""; + int i; + for(i = 1; i < 10; i++) + { + if(TOBGetHasDisciplineFocus(oInitiator, i)) + { + sDiscipline += GetDisciplineName(i); + sDiscipline += ", "; + } + } + // Send the Message + SendMessageToPC(oCheck, GetName(oInitiator) + " Knows Maneuvers From" + sDiscipline); + } + else // Skill check failed + { + SendMessageToPC(oCheck, GetName(oInitiator) + " Discipline Check Failed."); + } +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +void IdentifyManeuver(object oInitiator, int nSpellId) +{ + int nManeuverLevel = GetManeuverLevel(oInitiator); + + // The area to check for martial lore users + object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(oInitiator), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while (GetIsObjectValid(oTarget) && oTarget != oInitiator) + { + // If the target has points in the skill + if(GetSkillRank(SKILL_MARTIAL_LORE, oTarget) > 0) _DoMartialLoreCheck(oInitiator, oTarget, nManeuverLevel, nSpellId); + + //Select the next target within the area. + oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(oInitiator), TRUE, OBJECT_TYPE_CREATURE); + } +} + +void IdentifyDiscipline(object oInitiator) +{ + int nInitiatorLevel = GetInitiatorLevel(oInitiator); + + // The area to check for martial lore users + object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(oInitiator), TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while(GetIsObjectValid(oTarget)) + { + // If the target has points in the skill + if(GetSkillRank(SKILL_MARTIAL_LORE, oTarget) > 0 + && oTarget != oInitiator) + _DoDisciplineCheck(oInitiator, oTarget, nInitiatorLevel); + + //Select the next target within the area. + oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(oInitiator), TRUE, OBJECT_TYPE_CREATURE); + } +} \ No newline at end of file diff --git a/src/include/tob_inc_move.nss b/src/include/tob_inc_move.nss new file mode 100644 index 0000000..9a90c77 --- /dev/null +++ b/src/include/tob_inc_move.nss @@ -0,0 +1,747 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Initiating +//:: tob_inc_move +//:://///////////////////////////////////////////// +/** @file + Defines structures and functions for handling + initiating a maneuver + + @author Stratovarius + @date Created - 2007.3.20 + @thanks to Ornedan for his work on Psionics upon which this is based. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string TOB_DEBUG_IGNORE_CONSTRAINTS = "TOB_DEBUG_IGNORE_CONSTRAINTS"; + +/** + * The variable in which the maneuver token is stored. If no token exists, + * the variable is set to point at the initiator itself. That way OBJECT_INVALID + * means the variable is unitialised. + */ +const string PRC_MANEVEUR_TOKEN_VAR = "PRC_ManeuverToken"; +const string PRC_MANEVEUR_TOKEN_NAME = "PRC_MOVETOKEN"; +const float PRC_MANEVEUR_HB_DELAY = 0.5f; + + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure that contains common data used during maneuver. + */ +struct maneuver{ + /* Generic stuff */ + /// The creature Truespeaking the Maneuver + object oInitiator; + /// Whether the maneuver is successful or not + int bCanManeuver; + /// The creature's initiator level in regards to this maneuver + int nInitiatorLevel; + /// The maneuver's spell ID + int nMoveId; +}; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines if the maneuver that is currently being attempted to be TrueSpoken + * can in fact be truespoken. Determines metamaneuvers used. + * + * @param oInitiator A creature attempting to truespeak a maneuver at this moment. + * @param oTarget The target of the maneuver, if any. For pure Area of Effect. + * maneuvers, this should be OBJECT_INVALID. Otherwise the main + * target of the maneuver as returned by PRCGetSpellTargetObject(). + * + * @return A maneuver structure that contains the data about whether + * the maneuver was successfully initiated and some other + * commonly used data, like the PC's initiator level for this maneuver. + */ +struct maneuver EvaluateManeuver(object oInitiator, object oTarget = OBJECT_INVALID, int bTOBAbility = FALSE); + +/** + * Causes OBJECT_SELF to use the given maneuver. + * + * @param nManeuver The index of the maneuver to use in spells.2da or an UTTER_* + * @param nClass The index of the class to use the maneuver as in classes.2da or a CLASS_TYPE_* + * @param nLevelOverride An optional override to normal initiator level. + * Default: 0, which means the parameter is ignored. + */ +void UseManeuver(int nManeuver, int nClass, int nLevelOverride = 0); + +/** + * A debugging function. Takes a maneuver structure and + * makes a string describing the contents. + * + * @param move A set of maneuver data + * @return A string describing the contents of move + */ +string DebugManeuver2Str(struct maneuver move); + +/** + * Stores a maneuver structure as a set of local variables. If + * a structure was already stored with the same name on the same object, + * it is overwritten. + * + * @param oObject The object on which to store the structure + * @param sName The name under which to store the structure + * @param move The maneuver structure to store + */ +void SetLocalManeuver(object oObject, string sName, struct maneuver move); + +/** + * Retrieves a previously stored maneuver structure. If no structure is stored + * by the given name, the structure returned is empty. + * + * @param oObject The object from which to retrieve the structure + * @param sName The name under which the structure is stored + * @return The structure built from local variables stored on oObject under sName + */ +struct maneuver GetLocalManeuver(object oObject, string sName); + +/** + * Deletes a stored maneuver structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalManeuver(object oObject, string sName); + +/** + * Checks whether the maneuver is a class ability + * + * @param nMoveId The SpellID + * + * @return TRUE if it is a class ability, FALSE otherwise + */ +int GetIsTOBAbility(int nMoveId); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "tob_inc_martlore" +#include "tob_inc_recovery" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Deletes maneuver-related local variables. + * + * @param oInitiator The creature currently initiating a maneuver + */ +void _CleanManeuverVariables(object oInitiator) +{ + DeleteLocalInt(oInitiator, PRC_INITIATING_CLASS); + DeleteLocalInt(oInitiator, PRC_MANEUVER_LEVEL); + DeleteLocalInt(oInitiator, "PCIsInitiating"); +} + +/** Internal function. + * Determines whether a maneuver token exists. If one does, returns it. + * + * @param oInitiator A creature whose maneuver token to get + * @return The maneuver token if it exists, OBJECT_INVALID otherwise. + */ +object _GetManeuverToken(object oInitiator) +{ + object oMoveToken = GetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR); + + // If the token object is no longer valid, set the variable to point at initiator + if(!GetIsObjectValid(oMoveToken)) + { + oMoveToken = oInitiator; + SetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR, oMoveToken); + } + + + // Check if there is no token + if(oMoveToken == oInitiator) + oMoveToken = OBJECT_INVALID; + + return oMoveToken; +} + +/** Internal function. + * Destroys the given maneuver token and sets the creature's maneuver token variable + * to point at itself. + * + * @param oInitiator The initiator whose token to destroy + * @param oMoveToken The token to destroy + */ +void _DestroyManeuverToken(object oInitiator, object oMoveToken) +{ + DestroyObject(oMoveToken); + if(DEBUG) DoDebug("_DestroyManeuverToken(): Destroying Token"); + SetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR, oInitiator); +} + +/** Internal function. + * Destroys the previous maneuver token, if any, and creates a new one. + * + * @param oInitiator A creature for whom to create a maneuver token + * @return The newly created token + */ +object _CreateManeuverToken(object oInitiator) +{ + object oMoveToken = _GetManeuverToken(oInitiator); + object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oInitiator); + + // Delete any previous tokens + if(GetIsObjectValid(oMoveToken)) + _DestroyManeuverToken(oInitiator, oMoveToken); + + // Create new token and store a reference to it + oMoveToken = CreateItemOnObject(PRC_MANEVEUR_TOKEN_NAME, oStore); + SetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR, oMoveToken); + + Assert(GetIsObjectValid(oMoveToken), "GetIsObjectValid(oMoveToken)", "ERROR: Unable to create maneuver token! Store object: " + DebugObject2Str(oStore), "true_inc_Utter", "_CreateManeuverToken()"); + + return oMoveToken; +} + +/** Internal function. + * Determines whether the given initiator is doing something that would + * interrupt initiating a maneuver or affected by an effect that would do + * the same. + * + * @param oInitiator A creature on which _ManeuverHB() is running + * @return TRUE if the creature can continue initiating, + * FALSE otherwise + */ +int _ManeuverStateCheck(object oInitiator) +{ + if(GetIsDead(oInitiator)) return FALSE; + int nAction = GetCurrentAction(oInitiator); + // If the current action is not among those that could either be used to truespeak the maneuver or movement, the maneuver fails + if(!(nAction || ACTION_CASTSPELL || nAction == ACTION_INVALID || + nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT || + nAction || ACTION_USEOBJECT || nAction == ACTION_WAIT + ) ) + return FALSE; + + // Affected by something that prevents one from initiating + effect eTest = GetFirstEffect(oInitiator); + int nEType; + while(GetIsEffectValid(eTest)) + { + nEType = GetEffectType(eTest); + if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE || + nEType == EFFECT_TYPE_DAZED || + nEType == EFFECT_TYPE_PARALYZE || + nEType == EFFECT_TYPE_PETRIFY || + nEType == EFFECT_TYPE_SLEEP || + nEType == EFFECT_TYPE_STUNNED + ) + return FALSE; + + // Get next effect + eTest = GetNextEffect(oInitiator); + } + + return TRUE; +} + +/** Internal function. + * Runs while the given creature is initiating. If they move, take other actions + * that would cause them to interrupt initiating the maneuver or are affected by an + * effect that would cause such interruption, deletes the maneuver token. + * Stops if such condition occurs or something else destroys the token. + * + * @param oInitiator A creature initiating a maneuver + * @param lInitiator The location where the initiator was when starting the maneuver + * @param oMoveToken The maneuver token that controls the ongoing maneuver + */ +void _ManeuverHB(object oInitiator, location lInitiator, object oMoveToken) +{ + float fDistance; + if(DEBUG) DoDebug("_ManeuverHB() running:\n" + + "oInitiator = " + DebugObject2Str(oInitiator) + "\n" + + "lInitiator = " + DebugLocation2Str(lInitiator) + "\n" + + "oMoveToken = " + DebugObject2Str(oMoveToken) + "\n" + + "Distance between maneuver start location and current location: " + FloatToString(GetDistanceBetweenLocations(lInitiator, GetLocation(oInitiator))) + "\n" + ); + if(GetIsObjectValid(oMoveToken)) + { + // Continuance check + fDistance = GetDistanceBetweenLocations(lInitiator, GetLocation(oInitiator)); + if(fDistance > 2.0f || fDistance < 0.0 || // Allow some variance in the location to account for dodging and random fidgeting + !_ManeuverStateCheck(oInitiator) // Action and effect check + ) + { + if(DEBUG) DoDebug("_ManeuverHB(): initiator moved or lost concentration, destroying token"); + _DestroyManeuverToken(oInitiator, oMoveToken); + + // Inform initiator + FloatingTextStringOnCreature("You have lost concentration on the maneuver you were attempting to initiate!", oInitiator, FALSE); + } + // Schedule next HB + else + DelayCommand(PRC_MANEVEUR_HB_DELAY, _ManeuverHB(oInitiator, lInitiator, oMoveToken)); + } +} + +/** Internal function. + * Checks if the initiator is in range to use the maneuver they are trying to use. + * If not, queues commands to make the initiator to run into range. + * + * @param oInitiator A creature initiating a maneuver + * @param nManeuver SpellID of the maneuver being initiated + * @param lTarget The target location or the location of the target object + */ +void _ManeuverRangeCheck(object oInitiator, int nManeuver, location lTarget) +{ + float fDistance = GetDistanceBetweenLocations(GetLocation(oInitiator), lTarget); + float fRangeLimit; + string sRange = Get2DACache("spells", "Range", nManeuver); + + // Personal range maneuvers are always in range + if(sRange == "P") + return; + // Ranges according to the CCG spells.2da page + else if(sRange == "T") + fRangeLimit = 2.25f; + else if(sRange == "S") + fRangeLimit = 8.0f; + else if(sRange == "M") + fRangeLimit = 20.0f; + else if(sRange == "L") + fRangeLimit = 40.0f; + + // See if we are out of range + if(fDistance > fRangeLimit) + { + // Create waypoint for the movement + object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget); + + // Move into range, with a bit of fudge-factor + //ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f); + + // CleanUp + ActionDoCommand(DestroyObject(oWP)); + + // CleanUp, paranoia + AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f))); + } +} + +/** Internal function. + * Assigns the fakecast command that is used to display the conjuration VFX when using an maneuver. + * Separated from UseManeuver() due to a bug with ActionFakeCastSpellAtObject(), which requires + * use of ClearAllActions() to work around. + * The problem is that if the target is an item on the ground, if the actor is out of spell + * range when doing the fakecast, they will run on top of the item instead of to the edge of + * the spell range. This only happens if there was a "real action" in the actor's action queue + * immediately prior to the fakecast. + */ +void _AssignUseManeuverFakeCastCommands(object oInitiator, object oTarget, location lTarget, int nSpellID) +{ + // Nuke actions to prevent the fakecast action from bugging + ClearAllActions(); + + if(GetIsObjectValid(oTarget)) + ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT); + else + ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT); +} + + +/** Internal function. + * Places the cheatcasting of the real maneuver into the initiator's action queue. + */ +void _UseManeuverAux(object oInitiator, object oMoveToken, int nSpellId, + object oTarget, location lTarget, + int nManeuver, int nClass, int nLevelOverride) +{ + if(DEBUG) DoDebug("_UseManeuverAux() running:\n" + + "oInitiator = " + DebugObject2Str(oInitiator) + "\n" + + "oMoveToken = " + DebugObject2Str(oMoveToken) + "\n" + + "nSpellId = " + IntToString(nSpellId) + "\n" + + "oTarget = " + DebugObject2Str(oTarget) + "\n" + + "lTarget = " + DebugLocation2Str(lTarget) + "\n" + + "nManeuver = " + IntToString(nManeuver) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + ); + + // Make sure nothing has interrupted this maneuver + if(GetIsObjectValid(oMoveToken)) + { + if(DEBUG) DoDebug("_UseManeuverAux(): Token was valid, queueing actual maneuver"); + // Set the class to maneuver as + SetLocalInt(oInitiator, PRC_INITIATING_CLASS, nClass + 1); + + // Set the maneuver's level + SetLocalInt(oInitiator, PRC_MANEUVER_LEVEL, StringToInt(lookup_spell_innate(nSpellId))); + + if(nLevelOverride != 0) + AssignCommand(oInitiator, ActionDoCommand(SetLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride))); + if(GetIsObjectValid(oTarget)) + AssignCommand(oInitiator, ActionCastSpellAtObject(nManeuver, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + else + AssignCommand(oInitiator, ActionCastSpellAtLocation(nManeuver, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + if(nLevelOverride != 0) + AssignCommand(oInitiator, ActionDoCommand(DeleteLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE))); + + // Begins the Crusader Granting Maneuver process + if (nClass == CLASS_TYPE_CRUSADER) + { + BeginCrusaderGranting(oInitiator); + //if(DEBUG) DoDebug("_UseManeuverAux(): BeginCrusaderGranting"); + } + + SetLocalInt(oInitiator, "PCIsInitiating", TRUE); + // Destroy the maneuver token for this maneuver + _DestroyManeuverToken(oInitiator, oMoveToken); + } +} + +int _GetIsManeuverWeaponAppropriate(object oInitiator) +{ + object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator); + // If the initiator is empty handed, unarmed strikes are good + if (!GetIsObjectValid(oItem)) return TRUE; + // If melee weapon, all good. + if (IPGetIsMeleeWeapon(oItem)) return TRUE; + // Add other legal items in here, like Bloodstorm Blade throwing + + // If one of the other's hasn't tripped, fail here + return FALSE; +} + +void _StanceSpecificChecks(object oInitiator, int nMoveId) +{ + int nStanceToKeep = -1; + if (GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator) >= 20) + { + nStanceToKeep = GetHasActiveStance(oInitiator); + } + if (GetLevelByClass(CLASS_TYPE_DEEPSTONE_SENTINEL, oInitiator) >= 3 && + GetHasSpellEffect(MOVE_MOUNTAIN_FORTRESS, oInitiator) && + GetDisciplineByManeuver(nMoveId) == DISCIPLINE_STONE_DRAGON) + { + nStanceToKeep = GetHasActiveStance(oInitiator); + } + // Master of Nine can keep two stances active for 2 rounds per class level. + if (GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator) >= 3 && GetLocalInt(oInitiator, "MoNDualStance")) + { + nStanceToKeep = GetHasActiveStance(oInitiator); + float fDelay = 12.0 * GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator); + // Clean up the stance when the timer runs out + DelayCommand(fDelay, ClearStances(oInitiator, -1)); + DeleteLocalInt(oInitiator, "MoNDualStance"); + } + + //if(DEBUG) DoDebug("tob_inc_move: ClearStances"); + if(nStanceToKeep == nMoveId) + nStanceToKeep = -1; + // Can only have one stance active, except for a level 20+ Warblade + ClearStances(oInitiator, nStanceToKeep); + + // Mark current stance as atcive + MarkStanceActive(oInitiator, nMoveId); +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct maneuver EvaluateManeuver(object oInitiator, object oTarget = OBJECT_INVALID, int bTOBAbility = FALSE) +{ + /* Get some data */ + // initiator-related stuff + int nInitiatorLevel = GetInitiatorLevel(oInitiator); + int nManeuverLevel = GetManeuverLevel(oInitiator); + int nClass = GetInitiatingClass(oInitiator); + + /* Initialise the maneuver structure */ + struct maneuver move; + move.oInitiator = oInitiator; + move.bCanManeuver = TRUE; // Assume successfull maneuver by default + move.nInitiatorLevel = nInitiatorLevel; + move.nMoveId = PRCGetSpellId(); + + //if(DEBUG) DoDebug("move.bCanManeuver: " + IntToString(move.bCanManeuver)); + // Skip doing anything if something has prevented a successful maneuver already by this point + if(/*move.bCanManeuver && */bTOBAbility == FALSE) // set up identification later + { + // If you're this far in, you always succeed, there are very few checks. + // Deletes any active stances, and allows a Warblade 20 to have his two stances active. + /* GC - TMI is being caused from GetIsStance being run on the swordsage. Let's not run it twice.*/ + if (GetIsStance(move.nMoveId)) _StanceSpecificChecks(oInitiator, move.nMoveId); + else ExpendManeuver(move.oInitiator, nClass, move.nMoveId); + // Allows the Master of Nine to Counter Stance. + if(GetManeuverType(move.nMoveId) == MANEUVER_TYPE_COUNTER + && GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator) >= 4) + { + SetLocalInt(oInitiator, "MoNCounterStance", TRUE); + DelayCommand(6.0, DeleteLocalInt(oInitiator, "MoNCounterStance")); + } + //if(DEBUG) DoDebug("tob_inc_move: _StanceSpecificChecks"); + // Expend the Maneuver until recovered + //if (!GetIsStance(move.nMoveId)) ExpendManeuver(move.oInitiator, nClass, move.nMoveId); + //if(DEBUG) DoDebug("tob_inc_move: ExpendManeuver"); + // Do Martial Lore data + IdentifyManeuver(move.oInitiator, move.nMoveId); + //if(DEBUG) DoDebug("tob_inc_move: IdentifyManeuver"); + IdentifyDiscipline(move.oInitiator); + //if(DEBUG) DoDebug("tob_inc_move: IdentifyDiscipline"); + + }//end if + + if(DEBUG) DoDebug("EvaluateManeuver(): Final result:\n" + DebugManeuver2Str(move)); + + // Initiate maneuver-related variable CleanUp + DelayCommand(0.5f, _CleanManeuverVariables(oInitiator)); + + return move; +} + +void UseManeuver(int nManeuver, int nClass, int nLevelOverride = 0) +{ + object oInitiator = OBJECT_SELF; + object oSkin = GetPCSkin(oInitiator); + object oTarget = PRCGetSpellTargetObject(); + object oMoveToken; + location lTarget = PRCGetSpellTargetLocation(); + int nSpellID = PRCGetSpellId(); + + // Divine Surge Greater hardcoding to fix radial issue + // Yes it's deliberate that it's both + if (nSpellID == 15840 || nSpellID == 16051 || nSpellID == 16262) + { + nManeuver = MOVE_DS_GREATER_DIVINE_SURGE; + nSpellID = MOVE_DS_GREATER_DIVINE_SURGE; + } + + // Hardcoding to make Reth Dekala maneuvers work without forcing a massive redo of everything + if (nSpellID == 19331) nManeuver = MOVE_DS_MARTIAL_SPIRIT; + else if (nSpellID == 19332) nManeuver = MOVE_DS_THICKET_BLADES; + else if (nSpellID == 19333) nManeuver = MOVE_DS_DAUNTING_STRIKE; + else if (nSpellID == 19334) nManeuver = MOVE_TC_DEATH_FROM_ABOVE; + else if (nSpellID == 19335) nManeuver = MOVE_IH_DISARMING_STRIKE; + else if (nSpellID == 19336) nManeuver = MOVE_DS_ENTANGLING_BLADE; + else if (nSpellID == 19337) nManeuver = MOVE_IH_WALL_BLADES; + + if (DEBUG) DoDebug(GetName(oInitiator)+" initiating "+IntToString(nManeuver)+" on "+GetName(oTarget)); + + float fDistance = MetersToFeet(GetDistanceBetweenLocations(GetLocation(oInitiator), GetLocation(oTarget))); + float fDelay = FeetToMeters(fDistance)/10; + + // Moved the checks here, so they don't use an action. + + // Don't hit yourself, dumbass + if (Get2DACache("feat", "TARGETSELF", GetClassFeatFromPower(nManeuver, nClass)) != "1" && oInitiator == oTarget && nSpellID != 19331 && nSpellID != 19332 && nSpellID != 19337) + { + //if(DEBUG) DoDebug("tob_inc_move: _GetTargetSelfStrike"); + FloatingTextStringOnCreature("You cannot target yourself with this maneuver.", oInitiator, FALSE); + return; + } + // If the weapon is not appropriate, fail. + if (!_GetIsManeuverWeaponAppropriate(oInitiator)) + { + //if(DEBUG) DoDebug("tob_inc_move: _GetIsManeuverWeaponAppropriate"); + FloatingTextStringOnCreature("You do not have an appropriate weapon to initiate this maneuver.", oInitiator, FALSE); + return; + } + // If the maneuver is not readied, fail. + // Stances don't need to be readied + if (!GetIsManeuverReadied(oInitiator, nClass, nManeuver) && !GetIsStance(nManeuver) && !GetIsTOBAbility(nManeuver)) + { + //if(DEBUG) DoDebug("tob_inc_move: GetIsManeuverReadied"); + FloatingTextStringOnCreature(GetManeuverName(nManeuver) + " is not readied.", oInitiator, FALSE); + return; + } + // If the maneuver is expended, fail. + if (GetIsManeuverExpended(oInitiator, nClass, nManeuver) && !GetIsTOBAbility(nManeuver)) + { + //if(DEBUG) DoDebug("tob_inc_move: GetIsManeuverExpended"); + FloatingTextStringOnCreature(GetManeuverName(nManeuver) + " is already expended.", oInitiator, FALSE); + return; + } + // If the PC is in a Warblade recovery round, fail + if (GetIsWarbladeRecoveryRound(oInitiator)) + { + //if(DEBUG) DoDebug("tob_inc_move: GetIsWarbladeRecoveryRound"); + FloatingTextStringOnCreature(GetName(oInitiator) + " is recovering Warblade maneuvers.", oInitiator, FALSE); + return; + } + // Is the maneuver granted, and is the class a Crusader + if (nClass == CLASS_TYPE_CRUSADER && !GetIsManeuverGranted(oInitiator, nManeuver) && !GetIsStance(nManeuver) && !GetIsTOBAbility(nManeuver)) + { + //if(DEBUG) DoDebug("tob_inc_move: GetIsManeuverGranted"); + FloatingTextStringOnCreature(GetManeuverName(nManeuver) + " is not a granted maneuver.", oInitiator, FALSE); + return; + } + + // These maneuvers don't have a delay otherwise, because the distance is always 0 + if (Get2DACache("feat", "TARGETSELF", GetClassFeatFromPower(nManeuver, nClass)) == "1" && GetManeuverType(nManeuver) == MANEUVER_TYPE_STRIKE) + fDelay = 3.0; + + // Dual Boost check + if(GetManeuverType(nManeuver) == MANEUVER_TYPE_BOOST // If the maneuver is a boost + && GetLocalInt(oInitiator, "SSDualBoost")) // And the initiator can Dual boost. + { + // Set the maneuver time to 0 to skip VFX + DeleteLocalInt(oInitiator, "SSDualBoost"); + fDelay = 0.0; + } + // Stance of Alacrity check + // Only works on Counters, not Boosts + else if(GetManeuverType(nManeuver) == MANEUVER_TYPE_COUNTER // If the maneuver is counter + && GetHasSpellEffect(MOVE_DM_STANCE_ALACRITY, oInitiator)) // And the initiator has the stance + { + // Set the maneuver time to 0 to skip VFX + fDelay = 0.0; + } + else if((Get2DACache("feat", "Constant", GetClassFeatFromPower(nManeuver, nClass)) == "SWIFT_ACTION" // Normally swift action maneuvers check + || GetManeuverType(nManeuver) == MANEUVER_TYPE_BOOST + || GetManeuverType(nManeuver) == MANEUVER_TYPE_COUNTER + || GetManeuverType(nManeuver) == MANEUVER_TYPE_STANCE) // The maneuver is swift action to use + && GetLocalInt(oInitiator, "RKVDivineImpetus") // And the initiator can take a swift action now + ) + { + fDelay = 0.0; + DeleteLocalInt(oInitiator, "RKVDivineImpetus"); + } + else if(GetManeuverType(nManeuver) == MANEUVER_TYPE_STANCE && // The maneuver is a stance + GetLocalInt(oInitiator, "MoNCounterStance") // Has used a counter already this round + ) + { + fDelay = 0.0; + DeleteLocalInt(oInitiator, "MoNCounterStance"); + } + else if((Get2DACache("feat", "Constant", GetClassFeatFromPower(nManeuver, nClass)) == "SWIFT_ACTION" // Normally swift action maneuvers check + || GetManeuverType(nManeuver) == MANEUVER_TYPE_BOOST + || GetManeuverType(nManeuver) == MANEUVER_TYPE_COUNTER + || GetManeuverType(nManeuver) == MANEUVER_TYPE_STANCE)) // The maneuver is swift action to use + { + if (TakeSwiftAction(oInitiator)) // And the initiator can take a swift action now + fDelay = 0.0; + else + return; + } + + if (nManeuver == MOVE_DW_BURNING_BRAND) + fDelay = FeetToMeters(fDistance*2)/10; // Because of how it has to be implemented. + + if(DEBUG) DoDebug("UseManeuver(): initiator is " + DebugObject2Str(oInitiator) + "\n" + + "nManeuver = " + IntToString(nManeuver) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + + "maneuver duration = " + FloatToString(fDelay) + "ms \n" + //+ "Token exists = " + DebugBool2String(GetIsObjectValid(oMoveToken)) + ); + + // Create the maneuver token. Deletes any old tokens and cancels corresponding maneuvers as a side effect + oMoveToken = _CreateManeuverToken(oInitiator); + + /// @todo Hook to the initiator's OnDamaged event for the concentration checks to avoid losing the maneuver + + // Nuke action queue to prevent cheating with creative maneuver stacking. + // Probably not necessary anymore - Ornedan + if(DEBUG) SendMessageToPC(oInitiator, "Clearing all actions in preparation for second stage of the maneuver."); + ClearAllActions(); + + // If out of range, move to range + _ManeuverRangeCheck(oInitiator, nManeuver, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget); + + // Start the maneuver monitor HB + DelayCommand(fDelay, ActionDoCommand(_ManeuverHB(oInitiator, GetLocation(oInitiator), oMoveToken))); + if(DEBUG) DoDebug("Starting _ManeuverHB"); + + // Assuming the spell isn't used as a swift action, fakecast for visuals + if(fDelay > 0.0) + { + // Hack. Workaround of a bug with the fakecast actions. See function comment for details + ActionDoCommand(_AssignUseManeuverFakeCastCommands(oInitiator, oTarget, lTarget, nSpellID)); + if(DEBUG) DoDebug("Starting _AssignUseManeuverFakeCastCommands"); + } + + if(DEBUG) DoDebug("Starting _UseManeuverAux"); + // Action queue the function that will cheatcast the actual maneuver + DelayCommand(fDelay, AssignCommand(oInitiator, ActionDoCommand(_UseManeuverAux(oInitiator, oMoveToken, nSpellID, oTarget, lTarget, nManeuver, nClass, nLevelOverride)))); +} + +string DebugManeuver2Str(struct maneuver move) +{ + string sRet; + + sRet += "oInitiator = " + DebugObject2Str(move.oInitiator) + "\n"; + sRet += "bCanManeuver = " + DebugBool2String(move.bCanManeuver) + "\n"; + sRet += "nInitiatorLevel = " + IntToString(move.nInitiatorLevel); + + return sRet; +} + +void SetLocalManeuver(object oObject, string sName, struct maneuver move) +{ + //SetLocal (oObject, sName + "_", ); + SetLocalObject(oObject, sName + "_oInitiator", move.oInitiator); + + SetLocalInt(oObject, sName + "_bCanManeuver", move.bCanManeuver); + SetLocalInt(oObject, sName + "_nInitiatorLevel", move.nInitiatorLevel); + SetLocalInt(oObject, sName + "_nSpellID", move.nMoveId); +} + +struct maneuver GetLocalManeuver(object oObject, string sName) +{ + struct maneuver move; + move.oInitiator = GetLocalObject(oObject, sName + "_oInitiator"); + + move.bCanManeuver = GetLocalInt(oObject, sName + "_bCanManeuver"); + move.nInitiatorLevel = GetLocalInt(oObject, sName + "_nInitiatorLevel"); + move.nMoveId = GetLocalInt(oObject, sName + "_nSpellID"); + + return move; +} + +void DeleteLocalManeuver(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oInitiator"); + + DeleteLocalInt(oObject, sName + "_bCanManeuver"); + DeleteLocalInt(oObject, sName + "_nInitiatorLevel"); + DeleteLocalInt(oObject, sName + "_nSpellID"); +} + +void ManeuverDebugIgnoreConstraints(object oInitiator) +{ + SetLocalInt(oInitiator, TOB_DEBUG_IGNORE_CONSTRAINTS, TRUE); + DelayCommand(0.0f, DeleteLocalInt(oInitiator, TOB_DEBUG_IGNORE_CONSTRAINTS)); +} + +int GetIsTOBAbility(int nMoveId) +{ + // Shadow Sun Ninja and Eternal Blade class abilities + if(nMoveId == 19320 || + nMoveId == 19321 || + nMoveId == 19322 || + nMoveId == 19323 || + nMoveId == 19324 || + nMoveId == 19325 || + nMoveId == 19326 || + nMoveId == 19316 || + nMoveId == 19315 || + nMoveId == 19314 || + nMoveId == 19317 || + nMoveId == 19318 || + nMoveId == 19319) + return TRUE; + + return FALSE; +} + +// Test main +//void main(){} diff --git a/src/include/tob_inc_moveknwn.nss b/src/include/tob_inc_moveknwn.nss new file mode 100644 index 0000000..e89f36c --- /dev/null +++ b/src/include/tob_inc_moveknwn.nss @@ -0,0 +1,491 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Maneuvers Known +//:: tob_inc_moveknwn +//:://///////////////////////////////////////////// +/** @file + Defines functions for adding & removing + Maneuvers known. + + Data stored: + + - For each Discipline list + -- Total number of Maneuvers known + -- A modifier value to maximum Maneuvers known on this list to account for feats and classes that add Maneuvers + -- An array related to Maneuvers the knowledge of which is not dependent on character level + --- Each array entry specifies the spells.2da row of the known Maneuvers's class-specific entry + -- For each character level on which Maneuvers have been gained from this list + --- An array of Maneuvers gained on this level + ---- Each array entry specifies the spells.2da row of the known Maneuvers's class-specific entry + + @author Stratovarius + @date Created - 2007.03.19 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +// Included here to provide the values for the constants below +#include "prc_inc_spells" + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int MANEUVER_LIST_CRUSADER = CLASS_TYPE_CRUSADER; +const int MANEUVER_LIST_SWORDSAGE = CLASS_TYPE_SWORDSAGE; +const int MANEUVER_LIST_WARBLADE = CLASS_TYPE_WARBLADE; +const int MANEUVER_LIST_RETH_DEKALA = RACIAL_TYPE_RETH_DEKALA; // 67, so well away from the classes + +/// Special Maneuver list. Maneuvers gained via Martial Study or other sources. +const int MANEUVER_LIST_MISC = CLASS_TYPE_INVALID;//-1; + +const string _MANEUVER_LIST_NAME_BASE = "PRC_ManeuverList_"; +const string _MANEUVER_LIST_DISCIPLINE = "PRC_DisciplineTotal_"; +const string _MANEUVER_LIST_TOTAL_KNOWN = "_TotalKnown"; +const string _MANEUVER_LIST_MODIFIER = "_KnownModifier"; +const string _MANEUVER_LIST_MISC_ARRAY = "_ManeuversKnownMiscArray"; +const string _MANEUVER_LIST_LEVEL_ARRAY = "_ManeuversKnownLevelArray_"; +const string _MANEUVER_LIST_GENERAL_ARRAY = "_ManeuversKnownGeneralArray"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gives the creature the control feats for the given Maneuver and marks the Maneuver + * in a Maneuvers known array. + * If the Maneuver's data is already stored in one of the Maneuvers known arrays for + * the list or adding the Maneuver's data to the array fails, the function aborts. + * + * @param oCreature The creature to gain the Maneuver + * @param nList The list the Maneuver comes from. One of MANEUVER_LIST_* + * @param n2daRow The 2da row in the lists's 2da file that specifies the Maneuver. + * @param bLevelDependent If this is TRUE, the Maneuver is tied to a certain level and can + * be lost via level loss. If FALSE, the Maneuver is not dependent + * of a level and cannot be lost via level loss. + * @param nLevelToTieTo If bLevelDependent is TRUE, this specifies the level the Maneuver + * is gained on. Otherwise, it's ignored. + * The default value (-1) means that the current level of oCreature + * will be used. + * @param nDiscipline Type of the Maneuver: Evolving Mind, Crafted Tool, or Perfected Map + * + * @return TRUE if the Maneuver was successfully stored and control feats added. + * FALSE otherwise. + */ +int AddManeuverKnown(object oCreature, int nList, int n2daRow, int nType, int bLevelDependent = FALSE, int nLevelToTieTo = -1); + +/** + * Removes all Maneuvers gained from each list on the given level. + * + * @param oCreature The creature whose Maneuvers to remove + * @param nLevel The level to clear + */ +void RemoveManeuversKnownOnLevel(object oCreature, int nLevel); + +/** + * Gets the value of the Maneuvers known modifier, which is a value that is added + * to the 2da-specified maximum Maneuvers known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to get + * @param nList The list the maximum Maneuvers known from which the modifier + * modifies. One of MANEUVER_LIST_* + * @param nType MANEUVER_TYPE_ + */ +int GetKnownManeuversModifier(object oCreature, int nList, int nType); + +/** + * Sets the value of the Maneuvers known modifier, which is a value that is added + * to the 2da-specified maximum Maneuvers known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to set + * @param nList The list the maximum Maneuvers known from which the modifier + * modifies. One of MANEUVER_LIST_* + * @param nType MANEUVER_TYPE_ + */ +void SetKnownManeuversModifier(object oCreature, int nList, int nNewValue, int nType); + +/** + * Gets the number of Maneuvers a character character possesses from a + * specific list and lexicon + * + * @param oCreature The creature whose Maneuvers to check + * @param nList The list to check. One of MANEUVER_LIST_* + * @param nType MANEUVER_TYPE_ + * @return The number of Maneuvers known oCreature has from nList + */ +int GetManeuverCount(object oCreature, int nList, int nType); + +/** + * Gets the number of Maneuvers a character character possesses from a + * specific discipline + * + * @param oCreature The creature whose Maneuvers to check + * @param nDiscipline The discipline to check. One of DISCIPLINE_* + * @param nType MANEUVER_TYPE_ + * @return The number of Maneuvers known oCreature has from nDiscipline + */ +int GetManeuverCountByDiscipline(object oCreature, int nDiscipline, int nType); + +/** + * Gets the maximum number of Maneuvers a character may posses from a given list + * at this time. Calculated based on class levels, feats and a misceallenous + * modifier. There are three Types of Maneuvers, so it checks each seperately. + * + * @param oCreature Character to determine maximum Maneuvers for + * @param nList MANEUVER_LIST_* of the list to determine maximum Maneuvers for + * @param nType MANEUVER_TYPE_ + * @return Maximum number of Maneuvers that oCreature may know from the given list. + */ +int GetMaxManeuverCount(object oCreature, int nList, int nType); + +/** + * Determines whether a character has a given Maneuver, gained via some Maneuver list. + * + * @param nManeuver MANEUVER_* of the Maneuver to test + * @param nClass Class to check for + * @param oCreature Character to test for the possession of the Maneuver + * @return TRUE if the character has the Maneuver, FALSE otherwise + */ +int GetHasManeuver(int nManeuver, int nClass, object oCreature = OBJECT_SELF); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +//#include "tob_inc_tobfunc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _ManeuverRecurseRemoveArray(object oCreature, string sArrayName, string sUtterFile, int nArraySize, int nCurIndex) +{ + if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "sArrayName = '" + sArrayName + "'\n" + + "sUtterFile = '" + sUtterFile + "'\n" + + "nArraySize = " + IntToString(nArraySize) + "\n" + + "nCurIndex = " + IntToString(nCurIndex) + "\n" + ); + + // Determine whether we've already parsed the whole array or not + if(nCurIndex >= nArraySize) + { + if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray(): Running itemproperty removal loop."); + // Loop over itemproperties on the skin and remove each match + object oSkin = GetPCSkin(oCreature); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + GetLocalInt(oCreature, "PRC_MoveFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))) + ) + { + if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + DeleteLocalInt(oCreature, "PRC_MoveFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))); + } + + ipTest = GetNextItemProperty(oSkin); + } + } + // Still parsing the array + else + { + int nMoveID = GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArrayName, nCurIndex)); + // Set the marker + string sName = "PRC_MoveFeatRemovalMarker_" + Get2DACache(sUtterFile, "IPFeatID", nMoveID); + if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray(): Recursing through array, marker set:\n" + sName); + SetLocalInt(oCreature, sName, TRUE); + + string sType = Get2DACache(sUtterFile, "Type", nMoveID); + if(sType != "1") sType = "2";//if not MANEUVER_TYPE_STANCE use MANEUVER_TYPE_MANEUVER + string sDisciplineArray = _MANEUVER_LIST_DISCIPLINE + sType + "_" + Get2DACache(sUtterFile, "Discipline", nMoveID); + SetPersistantLocalInt(oCreature, sDisciplineArray, + GetPersistantLocalInt(oCreature, sDisciplineArray) - 1); + + // Recurse to next array index + _ManeuverRecurseRemoveArray(oCreature, sArrayName, sUtterFile, nArraySize, nCurIndex + 1); + } +} + +void _RemoveManeuverArray(object oCreature, int nList, int nLevel, int nType) +{ + if(DEBUG) DoDebug("_RemoveManeuverArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + ); + + string sBase = _MANEUVER_LIST_NAME_BASE + IntToString(nList) + IntToString(nType); + string sArray = sBase + _MANEUVER_LIST_LEVEL_ARRAY + IntToString(nLevel); + int nSize = persistant_array_get_size(oCreature, sArray); + + // Reduce the total by the array size + SetPersistantLocalInt(oCreature, sBase + _MANEUVER_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _MANEUVER_LIST_TOTAL_KNOWN) - nSize + ); + + // Remove each Maneuver in the array + _ManeuverRecurseRemoveArray(oCreature, sArray, GetAMSDefinitionFileName(nList), nSize, 0); + + // Remove the array itself + persistant_array_delete(oCreature, sArray); +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int AddManeuverKnown(object oCreature, int nList, int n2daRow, int nType, int bLevelDependent = FALSE, int nLevelToTieTo = -1) +{ + string sBase = _MANEUVER_LIST_NAME_BASE + IntToString(nList) + IntToString(nType); + string sArray = sBase; + string sPowerFile = GetAMSDefinitionFileName(/*PowerListToClassType(*/nList/*)*/); + string sTestArray; + int i, j, nSize, bReturn; + + // Get the spells.2da row corresponding to the cls_psipw_*.2da row + int nSpells2daRow = StringToInt(Get2DACache(sPowerFile, "SpellID", n2daRow)); + + // Determine the array name. + if(bLevelDependent) + { + // If no level is specified, default to the creature's current level + if(nLevelToTieTo == -1) + nLevelToTieTo = GetHitDice(oCreature); + + sArray += _MANEUVER_LIST_LEVEL_ARRAY + IntToString(nLevelToTieTo); + } + else + { + sArray += _MANEUVER_LIST_GENERAL_ARRAY; + } + + // Make sure the power isn't already in an array. If it is, abort and return FALSE + // Loop over each level array and check that it isn't there. + for(i = 1; i <= GetHitDice(oCreature); i++) + { + sTestArray = sBase + _MANEUVER_LIST_LEVEL_ARRAY + IntToString(i); + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + } + // Check the non-level-dependent array + sTestArray = sBase + _MANEUVER_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + + // All checks are made, now start adding the new power + // Create the array if it doesn't exist yet + if(!persistant_array_exists(oCreature, sArray)) + persistant_array_create(oCreature, sArray); + + // Store the power in the array + if(persistant_array_set_int(oCreature, sArray, persistant_array_get_size(oCreature, sArray), nSpells2daRow) != SDL_SUCCESS) + { + if(DEBUG) DoDebug("tob_inc_moveknwn: AddPowerKnown(): ERROR: Unable to add power to known array\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + + "n2daRow = " + IntToString(n2daRow) + "\n" + + "bLevelDependent = " + DebugBool2String(bLevelDependent) + "\n" + + "nLevelToTieTo = " + IntToString(nLevelToTieTo) + "\n" + + "nSpells2daRow = " + IntToString(nSpells2daRow) + "\n" + ); + return FALSE; + } + + // Increment Maneuvers known total + SetPersistantLocalInt(oCreature, sBase + _MANEUVER_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _MANEUVER_LIST_TOTAL_KNOWN) + 1 + ); + + // Increment Maneuvers known by discipline + string sDisciplineArray = _MANEUVER_LIST_DISCIPLINE + IntToString(nType) + "_" + Get2DACache(sPowerFile, "Discipline", n2daRow); + SetPersistantLocalInt(oCreature, sDisciplineArray, + GetPersistantLocalInt(oCreature, sDisciplineArray) + 1); + + // Give the power's control feats + object oSkin = GetPCSkin(oCreature); + string sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID", n2daRow); + itemproperty ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + // Second power feat, if any + /*sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID2", n2daRow); + if(sPowerFeatIP != "") + { + ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + }*/ + + return TRUE; +} + +void RemoveManeuversKnownOnLevel(object oCreature, int nLevel) +{ + if(DEBUG) DoDebug("tob_inc_moveknwn: RemoveManeuverKnownOnLevel():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + + string sPostFix = _MANEUVER_LIST_LEVEL_ARRAY + IntToString(nLevel); + // For each Maneuver list and type, determine if an array exists for this level. + // There's only 2 types + int nType; + for(nType = 1; nType <= 2; nType++) + { + if(persistant_array_exists(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(MANEUVER_LIST_CRUSADER) + sPostFix)) + // If one does exist, clear it + _RemoveManeuverArray(oCreature, MANEUVER_LIST_CRUSADER, nLevel, nType); + + if(persistant_array_exists(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(MANEUVER_LIST_SWORDSAGE) + sPostFix)) + _RemoveManeuverArray(oCreature, MANEUVER_LIST_SWORDSAGE, nLevel, nType); + + if(persistant_array_exists(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(MANEUVER_LIST_WARBLADE) + sPostFix)) + _RemoveManeuverArray(oCreature, MANEUVER_LIST_WARBLADE, nLevel, nType); + + if(persistant_array_exists(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(MANEUVER_LIST_MISC) + sPostFix)) + _RemoveManeuverArray(oCreature, MANEUVER_LIST_MISC, nLevel, nType); + } +} + +int GetKnownManeuversModifier(object oCreature, int nList, int nType) +{ + return GetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + IntToString(nType) + _MANEUVER_LIST_MODIFIER); +} + +void SetKnownManeuversModifier(object oCreature, int nList, int nNewValue, int nType) +{ + SetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + IntToString(nType) + _MANEUVER_LIST_MODIFIER, nNewValue); +} + +int GetManeuverCount(object oCreature, int nList, int nType) +{ + return GetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + IntToString(nType) + _MANEUVER_LIST_TOTAL_KNOWN); +} + +int GetManeuverCountByDiscipline(object oCreature, int nDiscipline, int nType) +{ + int nReturn = GetPersistantLocalInt(oCreature, _MANEUVER_LIST_DISCIPLINE + IntToString(nType) + "_" + IntToString(nDiscipline)); + + // Doesn't use the normal method, so is added here for the purpose of prereqs + if (GetRacialType(oCreature) == RACIAL_TYPE_RETH_DEKALA && nDiscipline == DISCIPLINE_DEVOTED_SPIRIT && nType == MANEUVER_TYPE_MANEUVER) nReturn += 2; + else if (GetRacialType(oCreature) == RACIAL_TYPE_RETH_DEKALA && nDiscipline == DISCIPLINE_DEVOTED_SPIRIT && nType == MANEUVER_TYPE_STANCE) nReturn += 2; + else if (GetRacialType(oCreature) == RACIAL_TYPE_RETH_DEKALA && nDiscipline == DISCIPLINE_IRON_HEART) nReturn += 2; + else if (GetRacialType(oCreature) == RACIAL_TYPE_RETH_DEKALA && nDiscipline == DISCIPLINE_TIGER_CLAW) nReturn += 1; + return nReturn; +} + +int GetMaxManeuverCount(object oCreature, int nList, int nType) +{ + if(DEBUG) DoDebug("tob_moveconv: MaxManeuver nType: " + IntToString(nType)); + int nMaxManeuvers = 0; + + if(nList == MANEUVER_LIST_MISC) + { + if(DEBUG) DoDebug("GetMaxManeuverCount(): ERROR: Using unfinished power list!"); + } + else + { + int nLevel = GetLevelByClass(nList, oCreature); + string sFile = GetAMSKnownFileName(nList); + if(nType == MANEUVER_TYPE_MANEUVER) + nMaxManeuvers = StringToInt(Get2DACache(sFile, "ManeuverKnown", nLevel - 1)); + else if(nType == MANEUVER_TYPE_STANCE) + nMaxManeuvers = StringToInt(Get2DACache(sFile, "StancesKnown", nLevel - 1)); + + // Calculate feats + + // Add in the custom modifier + nMaxManeuvers += GetKnownManeuversModifier(oCreature, nList, nType); + if(DEBUG) DoDebug("GetMaxManeuverCount(): " + IntToString(nList) + " Maneuvers: " + IntToString(nMaxManeuvers)); + } + + return nMaxManeuvers; +} + +int GetHasManeuver(int nManeuver, int nClass, object oCreature = OBJECT_SELF) +{ + if(GetLevelByClass(nClass, oCreature) && GetHasFeat(GetClassFeatFromPower(nManeuver, nClass), oCreature)) + return TRUE; + + return FALSE; +} + +string DebugListKnownManeuvers(object oCreature) +{ + string sReturn = "Maneuvers known by " + DebugObject2Str(oCreature) + ":\n"; + int i, j, k, numPowerLists = 6; + int nPowerList, nSize; + string sTemp, sArray, sArrayBase, sPowerFile; + // Loop over all power lists + for(i = 1; i <= numPowerLists; i++) + { + // Some padding + sReturn += " "; + // Get the power list for this loop + switch(i) + { + case 1: nPowerList = MANEUVER_LIST_CRUSADER; sReturn += "Crusader"; break; + case 2: nPowerList = MANEUVER_LIST_SWORDSAGE; sReturn += "Swordsage"; break; + case 3: nPowerList = MANEUVER_LIST_WARBLADE; sReturn += "Warblade"; break; + case 4: nPowerList = MANEUVER_LIST_RETH_DEKALA; sReturn += "Reth Dekala"; break; + + // This should always be last + case 6: nPowerList = MANEUVER_LIST_MISC; sReturn += "Misceallenous"; break; + } + sReturn += " Maneuvers known:\n"; + + // Determine if the character has any Maneuvers from this list + sPowerFile = GetAMSDefinitionFileName(nPowerList); + sArrayBase = _MANEUVER_LIST_NAME_BASE + IntToString(nPowerList); + + // Loop over levels + for(j = 1; j <= GetHitDice(oCreature); j++) + { + sArray = sArrayBase + _MANEUVER_LIST_LEVEL_ARRAY + IntToString(j); + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Gained on level " + IntToString(j) + ":\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + // Non-leveldependent Maneuvers + sArray = sArrayBase + _MANEUVER_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Non-leveldependent:\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + return sReturn; +} +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/tob_inc_recovery.nss b/src/include/tob_inc_recovery.nss new file mode 100644 index 0000000..333fe29 --- /dev/null +++ b/src/include/tob_inc_recovery.nss @@ -0,0 +1,650 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Maneuver Recovery +//:: tob_inc_martlore +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to recovery and readying maneuvers + See page #28 of Tome of Battle + + Functions below are called by the initiator as + he makes a maneuver, or when recovering or readying + + @author Stratovarius + @date Created - 2007.3.25 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int MANEUVER_READIED = 1; +const int MANEUVER_RECOVERED = 2; +const int MANEUVER_GRANTED = 3; +const int MANEVUER_WITHHELD = 4; + +const string _MANEUVER_LIST_RDYMODIFIER = "_ReadyModifier"; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gets the number of Maneuvers a character has readied + * + * @param oPC The creature whose Maneuvers to check + * @param nList The list to check. One of MANEUVER_LIST_* + * @return The number of Maneuvers readied + */ +int GetReadiedCount(object oPC, int nList); + +/** + * Gets the maximum number of Maneuvers a character may ready. + * + * @param oPC Character to determine maximum Maneuvers readied + * @param nList MANEUVER_LIST_* of the list to determine maximum Maneuvers for + * @return Maximum number of Maneuvers that oPC may ready + */ +int GetMaxReadiedCount(object oPC, int nList); + +/** + * Gets the value of the Maneuvers readied modifier, which is a value that is added + * to the 2da-specified maximum Maneuvers readied to determine the actual maximum. + * + * @param oCreature The creature whose modifier to get + * @param nList The list the maximum Maneuvers readied from which the modifier + * modifies. One of MANEUVER_LIST_* + */ +int GetReadiedManeuversModifier(object oCreature, int nList); + +/** + * Sets the value of the Maneuvers readied modifier, which is a value that is added + * to the 2da-specified maximum Maneuvers readied to determine the actual maximum. + * + * @param oCreature The creature whose modifier to set + * @param nList The list the maximum Maneuvers readied from which the modifier + * modifies. One of MANEUVER_LIST_* + */ +void SetReadiedManeuversModifier(object oCreature, int nList, int nNewValue); + +/** + * Readies the chosen Maneuver. Also checks to see if there are any slots left + * + * @param oPC Character readying maneuver + * @param nList MANEUVER_LIST_* of the list to ready + * @param nMoveId Maneuver to ready + */ +void ReadyManeuver(object oPC, int nList, int nMoveId); + +/** + * Returns whether maneuver is readied or not + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to check + * @return TRUE or FALSE + */ +int GetIsManeuverReadied(object oPC, int nList, int nMoveId); + +/** + * Returns whether maneuver is expended or not + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to check + * @return TRUE or FALSE + */ +int GetIsManeuverExpended(object oPC, int nList, int nMoveId); + +/** + * Expends the chosen Maneuver. + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to expend + */ +void ExpendManeuver(object oPC, int nList, int nMoveId); + +/** + * Clears all local ints marking maneuvers as expended + * + * @param oPC Character to clear + * @param nList MANEUVER_LIST_* + */ +void RecoverExpendedManeuvers(object oPC, int nList); + +/** + * Recovers the chosen Maneuver. + * + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to recover + */ +void RecoverManeuver(object oPC, int nList, int nMoveId); + +/** + * Checks to see if the PC is in a Warblade recovery round + * This prevents all use of maneuvers or stances during that round. + * + * @param oPC Character to clear + * @return TRUE or FALSE + */ +int GetIsWarbladeRecoveryRound(object oPC); + +/** + * Marks maneuvers as granted or withheld. + * + * @param oPC Character to grant maneuvers to + * @param nList MANEUVER_LIST_* + */ +void GrantManeuvers(object oPC, int nList); + +/** + * Clears all local ints marking maneuvers as readied + * + * @param oPC Character to clear + * @param nList MANEUVER_LIST_* + */ +void ClearReadiedManeuvers(object oPC, int nList); + +/** + * Grants a withheld maneuver + * Only works on Crusaders + * + * @param oPC Character to grant maneuvers to + * @param nList MANEUVER_LIST_* + * @param nMoveId Maneuver to grant + */ +void GrantWithheldManeuver(object oPC, int nList, int nMoveId = -1); + +/** + * Returns whether maneuver is granted or not + * Only works on Crusaders + * + * @param oPC Character to check + * @param nMoveId Maneuver to check + * @return TRUE or FALSE + */ +int GetIsManeuverGranted(object oPC, int nMoveId); + +/** + * Clears all local ints marking maneuvers as granted or withheld + * Only works on Crusaders + * + * @param oPC Character to clear + */ +void ClearGrantedWithheldManeuvers(object oPC); + +/** + * Starting function for Crusader recovery, calls DoCrusaderGranting + * Only works on Crusaders + * + * @param oPC Crusader + */ +void BeginCrusaderGranting(object oPC); + +/** + * Recursive function granting maneuvers each round in combat + * Will end when combat ends + * Only works on Crusaders + * + * @param oPC Crusader + * @param nTrip Round of combat. Takes values from 1 to 5, always starts with 1. + */ +void DoCrusaderGranting(object oPC, int nTrip); + + +/** + * Returns TRUE if a maneuver was expended, FALSE otherwise + * @param oPC Character to check + * @param nList MANEUVER_LIST_* + * @param nDiscipline DISCIPLINE_* the maneuver has to be from + * + * @return TRUE or FALSE + */ +int ExpendRandomManeuver(object oPC, int nList, int nDiscipline = -1); + +/** + * Clears all local ints marking maneuvers as expended + * + * @param oPC Character to clear + * @param nPRC Specific PRC to recover, else all. + */ +void RecoverPrCAbilities(object oPC); + +/** + * Heals 3 + 1 point per character level ones per minute + * + * @param oPC Character to heal + */ +void VitalRecovery(object oPC); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_lookups" +#include "tob_inc_tobfunc" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetReadiedCount(object oPC, int nList) +{ + return GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList)); +} + +int GetMaxReadiedCount(object oPC, int nList) +{ + int nLevel = GetLevelByClass(nList, oPC); + // 2das start at Row 0 + int nMaxReadied = StringToInt(Get2DACache(GetAMSKnownFileName(nList), "ManeuversReadied", nLevel-1)); + // Add in the custom modifier + nMaxReadied += GetReadiedManeuversModifier(oPC, nList); + if(nList == MANEUVER_LIST_SWORDSAGE) + nMaxReadied += GetHasFeat(FEAT_EXTRA_GRANTED_MANEUVER, oPC); + + if(DEBUG) DoDebug("tob_inc_recovery: MaxManeuvers Readied: " +IntToString(nMaxReadied)); + return nMaxReadied; +} + +int GetReadiedManeuversModifier(object oCreature, int nList) +{ + return GetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + _MANEUVER_LIST_RDYMODIFIER); +} + +void SetReadiedManeuversModifier(object oCreature, int nList, int nNewValue) +{ + SetPersistantLocalInt(oCreature, _MANEUVER_LIST_NAME_BASE + IntToString(nList) + _MANEUVER_LIST_RDYMODIFIER, nNewValue); +} + +void ReadyManeuver(object oPC, int nList, int nMoveId) +{ + int nCount = GetReadiedCount(oPC, nList); + int nMaxCount = GetMaxReadiedCount(oPC, nList); + + // If the PC can ready a maneuver and hasn't filled them all up + if(nMaxCount > nCount) + { + nCount++; + SetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(nCount), nMoveId); + SetLocalInt(oPC, "ManeuverReadied" + IntToString(nList), nCount); + if(DEBUG) DoDebug("tob_inc_recovery: ReadyManeuver: " +IntToString(nMoveId)); + } + else + FloatingTextStringOnCreature("All maneuvers are readied", oPC, FALSE); +} + +int GetIsManeuverReadied(object oPC, int nList, int nMoveId) +{ + // Counting through the local ints to determine if this one is readied + int i, nMax = GetReadiedCount(oPC, nList); + for(i = 1; i <= nMax; i++) + { + // If the value is valid, return true + if(GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)) == nMoveId) + { + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverReadied: " + IntToString(nMoveId)); + return TRUE; + } + } + return FALSE; +} + +int GetIsManeuverExpended(object oPC, int nList, int nMoveId) +{ + // Counting through the local ints to determine if this one is expended + int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // returns if the maneuver is expended + if(GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)) == nMoveId) + { + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverExpended: " +IntToString(nMoveId)); + return TRUE; + } + } + return FALSE; +} + +void ExpendManeuver(object oPC, int nList, int nMoveId) +{ + int nCount = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)) + 1; + + // This will mark the Maneuver Expended + SetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(nCount), nMoveId); + SetLocalInt(oPC, "ManeuverExpended" + IntToString(nList), nCount); + if(DEBUG) DoDebug("tob_inc_recovery: Expending Maneuver: " + IntToString(nMoveId)); +} + +void RecoverExpendedManeuvers(object oPC, int nList) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Clearing expended maneuvers"); + // Counting through the local ints to clear them all + int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // Clear them all + DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)); + } + // Do Grant/Withheld Maneuvers whenever this is called on a Crusader + if (nList == MANEUVER_LIST_CRUSADER) + { + // Make sure to clear them all first + ClearGrantedWithheldManeuvers(oPC); + // Then re-grant/withhold them + GrantManeuvers(oPC, nList); + } + if (GetHasFeat(FEAT_VITAL_RECOVERY, oPC)) VitalRecovery(oPC); +} + +void RecoverManeuver(object oPC, int nList, int nMoveId) +{ + // Counting through the local ints to determine if this one is expended + int i, nMax = GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // If it has been expended, clear that + if(GetLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)) == nMoveId) + { + DeleteLocalInt(oPC, "ManeuverExpended" + IntToString(nList) + IntToString(i)); + if(DEBUG) DoDebug("tob_inc_recovery: Recovering Maneuver: " + IntToString(nMoveId)); + } + } + if (GetHasFeat(FEAT_VITAL_RECOVERY, oPC)) VitalRecovery(oPC); +} + +int GetIsWarbladeRecoveryRound(object oPC) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Warblade recovery check"); + return GetLocalInt(oPC, "WarbladeRecoveryRound"); +} + +void GrantRandomManeuver(object oPC, int nList = MANEUVER_LIST_CRUSADER) +{ + int nMax = GetLocalInt(oPC, "GrantRand#"); + if(!nMax) return;//nothing to grant + + SetLocalInt(oPC, "GrantRand#", nMax - 1); + int x = Random(nMax)+1; + int nMoveId = GetLocalInt(oPC, "GrantRand#" + IntToString(x)); + if(x != nMax) + SetLocalInt(oPC, "GrantRand#" + IntToString(x), GetLocalInt(oPC, "GrantRand#" + IntToString(nMax))); + DeleteLocalInt(oPC, "GrantRand#" + IntToString(nMax)); + + //GrantWithheldManeuver(oPC, MANEUVER_LIST_CRUSADER, MoveId); + // No point in granting an expended maneuver + if(GetIsManeuverExpended(oPC, nList, nMoveId)) + RecoverManeuver(oPC, nList, nMoveId); + + int i = 1; + while(i) + { + // If it hits a non-valid, break + if(!GetLocalInt(oPC, "ManeuverGranted" + IntToString(i))) break; + i++; + } + SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId); +} + +void ListGrantedManeuvers(object oPC) +{ + int i; + for(i = 1; i <= 4; i++) + { + int nMoveId = GetLocalInt(oPC, "ManeuverGranted" + IntToString(i)); + int nExpended = GetIsManeuverExpended(oPC, MANEUVER_LIST_CRUSADER, nMoveId); + if (nMoveId > 0 && !nExpended) FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE); + } +} + +void GrantManeuvers(object oPC, int nList = MANEUVER_LIST_CRUSADER) +{ + // Only crusader level matters for this + int nLevel = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC); + // 2das start at Row 0 + int nGranted = StringToInt(Get2DACache(GetAMSKnownFileName(nList), "ManeuversGranted", nLevel-1)); + nGranted += GetReadiedManeuversModifier(oPC, nList); + nGranted += GetHasFeat(FEAT_EXTRA_GRANTED_MANEUVER, oPC); + + // Counting through the local ints to determine how many are readied + int i, nMaxReadied = GetReadiedCount(oPC, nList); + SetLocalInt(oPC, "GrantRand#", nMaxReadied); + for(i = 1; i <= nMaxReadied; i++) + { + // build temporary array for GrantRandomManeuver() function + int nMoveId = GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)); + if(nMoveId) + SetLocalInt(oPC, "GrantRand#" + IntToString(i), nMoveId); + } + for(i = 1; i <= nGranted; i++) + { + GrantRandomManeuver(oPC); + } + ListGrantedManeuvers(oPC); +} + +void ClearReadiedManeuvers(object oPC, int nList) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Clearing readied maneuvers"); + // Counting through the local ints to clear them all + int i, nMax = GetReadiedCount(oPC, nList); + DeleteLocalInt(oPC, "ManeuverReadied" + IntToString(nList)); + for(i = 1; i <= nMax; i++) + { + // Clear them all + DeleteLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)); + } +} + +/*void GrantWithheldManeuver(object oPC, int nList, int nMoveId = -1) +{ + int i; + string sPsiFile = GetAMSKnownFileName(nList); + // 2das start at Row 0 + int nLevel = GetInitiatorLevel(oPC, nList); + int nGranted = StringToInt(Get2DACache(sPsiFile, "ManeuversGranted", nLevel-1)); + int nReadied = StringToInt(Get2DACache(sPsiFile, "ManeuversReadied", nLevel-1)); + if(DEBUG) DoDebug("tob_inc_recovery: Maneuvers Granted: " + IntToString(nGranted)); + if(DEBUG) DoDebug("tob_inc_recovery: Maneuvers Readied: " + IntToString(nReadied)); + + // If someone input a maneuver + if (nMoveId > 0) + { + // No point in granting an expended maneuver + if (GetIsManeuverExpended(oPC, nList, nMoveId)) + RecoverManeuver(oPC, nList, nMoveId); + + // 3 is always the number withheld + for(i = nGranted; i < nReadied; i++) + { + // Making sure it gets marked properly + int nGrantId = GetLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + // If it exists, mark it as ready and break out + if (nMoveId == nGrantId) + { + if(DEBUG) DoDebug("tob_inc_recovery: Withheld Maneuver Granted: " + IntToString(nMoveId)); + DeleteLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE); + SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId); + break; + } + } + } + else + { + // 3 is always the number withheld + for(i = nGranted; i < nReadied; i++) + { + nMoveId = GetLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + // If it exists, mark it as ready and break out + if (nMoveId > 0) + { + if(DEBUG) DoDebug("tob_inc_recovery: Withheld Maneuver Granted: " + IntToString(nMoveId)); + DeleteLocalInt(oPC, "ManeuverWithheld" + IntToString(i)); + FloatingTextStringOnCreature(GetManeuverName(nMoveId) + " is granted", oPC, FALSE); + SetLocalInt(oPC, "ManeuverGranted" + IntToString(i), nMoveId); + break; + } + } + } +}*/ + +int GetIsManeuverGranted(object oPC, int nMoveId) +{ + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverGranted Start"); + // Counting through the local ints to determine if this one is expended + int i, nMax = GetReadiedCount(oPC, MANEUVER_LIST_CRUSADER); + for(i = 1; i <= nMax; i++) + { + // returns if the maneuver is expended + if(GetLocalInt(oPC, "ManeuverGranted" + IntToString(i)) == nMoveId) + { + if(DEBUG) DoDebug("tob_inc_recovery: GetIsManeuverGranted: " + IntToString(nMoveId)); + return TRUE; + } + } + return FALSE; +} + +void ClearGrantedWithheldManeuvers(object oPC) +{ + if(DEBUG) DoDebug("tob_inc_recovery: Clearing Granted and Withheld Maneuvers"); + // Counting through the local ints to clear them all + int i, nMax = GetReadiedCount(oPC, MANEUVER_LIST_CRUSADER); + for(i = 1; i <= nMax; i++) + { + // Clear them all + DeleteLocalInt(oPC, "ManeuverGranted" + IntToString(i)); + } +} + +void BeginCrusaderGranting(object oPC) +{ + if(DEBUG) DoDebug("BeginCrusaderGranting(): Entered Function"); + // Stops it from being called more than once. + if(GetLocalInt(oPC, "CrusaderGrantLoop")) return; + SetLocalInt(oPC, "CrusaderGrantLoop", TRUE); + + // Starts the granting process + if(DEBUG) DoDebug("BeginCrusaderGranting(): DoCrusaderGranting called"); + DoCrusaderGranting(oPC, 1); +} + +void DoCrusaderGranting(object oPC, int nTrip) +{ + if(DEBUG) DoDebug("DoCrusaderGranting(): Entered Function on Round #" + IntToString(nTrip)); + // First round of combat, no granting. + // Last round of the 5, clear and recover/grant maneuvers + if (nTrip >= 5) // Granted maneuvers empty, restart + { + if(DEBUG) DoDebug("DoCrusaderGranting(): RecoverExpendedManeuvers"); + RecoverExpendedManeuvers(oPC, MANEUVER_LIST_CRUSADER); + nTrip = 1; + } + else if (nTrip > 1) + { + // Rounds 2-4, grant a single maneuver + if(DEBUG) DoDebug("DoCrusaderGranting(): GrantWithheldManeuver"); + //GrantWithheldManeuver(oPC, MANEUVER_LIST_CRUSADER); + GrantRandomManeuver(oPC); + ListGrantedManeuvers(oPC); + } + + if(DEBUG) DoDebug("DoCrusaderGranting(): Above Recursive"); + // If in combat, keep the loop going + if (GetIsInCombat(oPC)) + { + if(DEBUG) DoDebug("DoCrusaderGranting(): In Combat"); + DelayCommand(6.0, DoCrusaderGranting(oPC, ++nTrip)); // Increment counter + } + else // Recover and stop loop otherwise. + { + if(DEBUG) DoDebug("DoCrusaderGranting(): Out of Combat Maneuver Recovery"); + RecoverExpendedManeuvers(oPC, MANEUVER_LIST_CRUSADER); + // Resent Int for next time out + DeleteLocalInt(oPC, "CrusaderGrantLoop"); + } + if(DEBUG) DoDebug("DoCrusaderGranting(): Ending"); +} + +int ExpendRandomManeuver(object oPC, int nList, int nDiscipline = -1) +{ + // Counting through the local ints to determine if maneuver can be expended + int i, nMax = GetReadiedCount(oPC, nList); + for(i = 1; i <= nMax; i++) + { + // If the value is valid, next step + int nMoveId = GetLocalInt(oPC, "ManeuverReadied" + IntToString(nList) + IntToString(i)); + if(nMoveId > 0) + { + // Make sure the disciplines match + if(nDiscipline == -1 || GetDisciplineByManeuver(nMoveId) == nDiscipline) + { + // If not expended + if(!GetIsManeuverExpended(oPC, nList, nMoveId)) + { + // Expend the damn thing and go home + ExpendManeuver(oPC, nList, nMoveId); + return TRUE; + } + } + } + } + + // If we're here, failed. + return FALSE; +} + +void RecoverPrCAbilities(object oPC) +{ + int i; + for(i = 2; i <= 8; i++) // PrC abilities: check last seven slots + { + int nClass = GetClassByPosition(i, oPC); + if(DEBUG) DoDebug("RecoverPrCAbilities" + IntToString(nClass)); + switch(nClass) + { + case CLASS_TYPE_INVALID: + if(DEBUG) DoDebug("RecoverPrCAbilities: no class to recover"); + break; + case CLASS_TYPE_JADE_PHOENIX_MAGE: + DeleteLocalInt(oPC, "JPM_Empowering_Strike_Expended"); + DeleteLocalInt(oPC, "JPM_Quickening_Strike_Expended"); + break; + case CLASS_TYPE_DEEPSTONE_SENTINEL: + DeleteLocalInt(oPC, "DPST_Awaken_Stone_Dragon_Expended"); + break; + case CLASS_TYPE_ETERNAL_BLADE: + DeleteLocalInt(oPC, "ETBL_Eternal_Training_Expended"); + DeleteLocalInt(oPC, "ETBL_Island_In_Time_Expended"); + // Remove bonus to racial type from eternal training + PRCRemoveEffectsFromSpell(oPC, ETBL_RACIAL_TYPE); + break; + } + } +} + +void VitalRecovery(object oPC) +{ + if (GetLocalInt(oPC, "VitalRecovery")) return; //Once a minute + int nHD = GetHitDice(oPC); + effect eHeal = EffectHeal(nHD+3); // That's it + effect eVis = EffectVisualEffect(VFX_IMP_HEALING_M); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oPC); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + + SetLocalInt(oPC, "VitalRecovery", TRUE); + DelayCommand(60.0, DeleteLocalInt(oPC, "VitalRecovery")); +} \ No newline at end of file diff --git a/src/include/tob_inc_tobfunc.nss b/src/include/tob_inc_tobfunc.nss new file mode 100644 index 0000000..3d74cbd --- /dev/null +++ b/src/include/tob_inc_tobfunc.nss @@ -0,0 +1,1293 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle include: Miscellaneous +//:: tob_inc_tobfunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the Tome of Battle implementation. + + Also acts as inclusion nexus for the general + tome of battle includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2007.3.19 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/11 + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int DISCIPLINE_DESERT_WIND = 1; +const int DISCIPLINE_DEVOTED_SPIRIT = 2; +const int DISCIPLINE_DIAMOND_MIND = 4; +const int DISCIPLINE_IRON_HEART = 8; +const int DISCIPLINE_SETTING_SUN = 16; +const int DISCIPLINE_SHADOW_HAND = 32; +const int DISCIPLINE_STONE_DRAGON = 64; +const int DISCIPLINE_TIGER_CLAW = 128; +const int DISCIPLINE_WHITE_RAVEN = 256; + +const string PRC_INITIATING_CLASS = "PRC_CurrentManeuver_InitiatingClass"; +const string PRC_MANEUVER_LEVEL = "PRC_CurrentManeuver_Level"; + +const int MANEUVER_TYPE_STANCE = 1; +const int MANEUVER_TYPE_STRIKE = 2; +const int MANEUVER_TYPE_COUNTER = 3; +const int MANEUVER_TYPE_BOOST = 4; +//global constant (strike & counter & boost) +const int MANEUVER_TYPE_MANEUVER = 5; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's maneuver list the currently being initiated + * maneuver is initiated from. + * + * @param oInitiator A creature initiating a maneuver at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetInitiatingClass(object oInitiator = OBJECT_SELF); + +/** + * Determines the given creature's Initiator level. If a class is specified, + * then returns the Initiator level for that class. Otherwise, returns + * the Initiator level for the currently active maneuver. + * + * @param oInitiator The creature whose Initiator level to determine + * @param nSpecificClass The class to determine the creature's Initiator + * level in. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * Initiator level in regards to an ongoing maneuver + * is determined instead. + * @return The Initiator level + */ +int GetInitiatorLevel(object oInitiator = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID); + +/** + * Determines whether a given creature uses BladeMagic. + * Requires either levels in a BladeMagic-related class or + * natural BladeMagic ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use BladeMagics, FALSE otherwise. + */ +int GetIsBladeMagicUser(object oCreature); + +/** + * Determines the given creature's highest undmodified Initiator level among it's + * initiating classes. + * + * @param oCreature Creature whose highest Initiator level to determine + * @return The highest unmodified Initiator level the creature can have + */ +int GetHighestInitiatorLevel(object oCreature); + +/** + * Determines whether a given class is a BladeMagic-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a BladeMagic-related class, FALSE otherwise + */ +int GetIsBladeMagicClass(int nClass); + +/** + * Gets the level of the maneuver being currently initiated or the level + * of the maneuver ID passed to it. + * + * @param oInitiator The creature currently initiating a maneuver + * @return The level of the maneuver being initiated + */ +int GetManeuverLevel(object oInitiator, int nMoveId = 0); + +/** + * Returns the type of the maneuver + * + * @param nSpellId SpellId of the maneuver + */ +int GetManeuverType(int nSpellId); + +/** + * Returns the name of the maneuver + * + * @param nSpellId SpellId of the maneuver + */ +string GetManeuverName(int nSpellId); + +/** + * Returns the name of the Discipline + * + * @param nDiscipline DISCIPLINE_* to name + */ +string GetDisciplineName(int nDiscipline); + +/** + * Returns the Discipline the maneuver is in + * @param nMoveId maneuver to check + * + * @return DISCIPLINE_* + */ +int GetDisciplineByManeuver(int nMoveId); + +/** + * Returns true or false if the initiator has the Discipline + * @param oInitiator Person to check + * @param nDiscipline Discipline to check + * + * @return TRUE or FALSE + */ +int TOBGetHasDiscipline(object oInitiator, int nDiscipline); + +/** + * Returns true or false if the swordsage has Discipline + * focus in the chosen discipline + * @param oInitiator Person to check + * @param nDiscipline Discipline to check + * + * @return TRUE or FALSE + */ +int TOBGetHasDisciplineFocus(object oInitiator, int nDiscipline); + +/** + * Calculates how many initiator levels are gained by a given creature from + * it's levels in prestige classes. + * + * @param oCreature Creature to calculate added initiator levels for + * @return The number of initiator levels gained + */ +int GetBladeMagicPRCLevels(object oInitiator); + +/** + * Determines which of the character's classes is their highest or first blade magic + * initiating class, if any. This is the one which gains initiator level raise benefits + * from prestige classes. + * + * @param oCreature Creature whose classes to test + * @return CLASS_TYPE_* of the first blade magic initiating class, + * CLASS_TYPE_INVALID if the creature does not possess any. + */ +int GetPrimaryBladeMagicClass(object oCreature = OBJECT_SELF); + +/** + * Determines the position of a creature's first blade magic initiating class, if any. + * + * @param oCreature Creature whose classes to test + * @return The position of the first blade magic class {1, 2, 3} or 0 if + * the creature possesses no levels in blade magic classes. + */ +int GetFirstBladeMagicClassPosition(object oCreature = OBJECT_SELF); + +/** + * Checks whether the PC has the prereqs for the maneuver + * + * @param nClass The class that is trying to learn the feat + * @param nFeat The maneuver's FeatId + * @param oPC The creature whose feats to check + * @return TRUE if the PC possesses the prerequisite feats AND does not + * already posses nFeat, FALSE otherwise. + */ +int CheckManeuverPrereqs(int nClass, int nPrereqs, int nDiscipline, object oPC); + +/** + * Checks whether the maneuver is supernatural or not + * Mainly used to check for AMF areas. + * Mostly from Swordsage maneuvers + * + * @param nMoveId The Maneuver to Check + * @return TRUE if Maneuver is (Su), else FALSE + */ +int GetIsManeuverSupernatural(int nMoveId); + +/** + * Checks whether the initiator has an active stance + * + * @param oInitiator The Initiator + * @return The SpellId or FALSE + */ +int GetHasActiveStance(object oInitiator); + +/** + * Clears spell effects for Stances + * Will NOT clear nDontClearMove + * + * @param oInitiator The Initiator + * @param nDontClearMove A single Stance not to clear + */ +void ClearStances(object oInitiator, int nDontClearMove); + +/** + * Marks a stance active via local ints + * + * @param oInitiator The Initiator + * @param nStance The stance to mark active + */ +void MarkStanceActive(object oInitiator, int nStance); + +/** + * This will take an effect that is supposed to be based on size + * And use vs racial effects to approximate it + * + * @param oInitiator The Initiator + * @param eEffect The effect to scale + * @param nSize 0 affects creature one size or more smaller. + * 1 affects creatures one size or more larger + */ +effect VersusSizeEffect(object oInitiator, effect eEffect, int nSize); + +/** + * Checks every 6 seconds whether an adept has moved too far for a stance + * Or whether the adept has moved far enough to get a bonus from a stance + * + * @param oPC The Initiator + * @param nMoveId The stance + * @param fFeet The distance to check + */ +void InitiatorMovementCheck(object oPC, int nMoveId, float fFeet = 10.0); + +/** + * Checks whether the maneuver is a stance + * + * @param nMoveId The Maneuver + * @return TRUE or FALSE + */ +int GetIsStance(int nMoveId); + +/** + * Sets up everything for the Damage boosts (xd6 + IL fire damage) + * That the Desert Wind discipline has. + * + * @param oPC The PC + */ +void DoDesertWindBoost(object oPC); + +/** + * Determines which PC in the area is weakest, and + * returns that PC. + * + * @param oPC The PC + * @param fDistance The distance to check in feet + * @return The Target + */ +object GetCrusaderHealTarget(object oPC, float fDistance); + +/** + * Returns true or false if the swordsage has Insightful Strike in the chosen discipline + * @param oInitiator Person to check + * + * @return TRUE or FALSE + */ +int GetHasInsightfulStrike(object oInitiator); + +/** + * Returns true or false if the swordsage has Defensive Stance + * ONLY CALL THIS FROM WITHIN STANCES + * @param oInitiator Person to check + * @param nDiscipline DISCIPLINE_ constant of the school of the maneuver. + * + * @return TRUE or FALSE + */ +int GetHasDefensiveStance(object oInitiator, int nDiscipline); + +/** + * Returns true if it is a weapon of the appropriate discipline + * @param oWeapon Weapon to check + * @param nDiscipline DISCIPLINE_ constant of the school of the maneuver. + * + * @return TRUE or FALSE + */ +int GetIsDisciplineWeapon(object oWeapon, int nDiscipline); + +/** + * Returns a numerical bonus to attacks for use in strikes + * @param oInitiator Person to check + * @param nDiscipline DISCIPLINE_ constant of the school of the maneuver. + * @param nClass CLASS_TYPE_ constant + * + * @return Bonus total + */ +int TOBSituationalAttackBonuses(object oInitiator, int nDiscipline, int nClass = CLASS_TYPE_INVALID); + +/** + * Returns the skill for the named discipline + * @param nDiscipline DISCIPLINE_ constant + * + * @return Discipline skill + */ +int GetDisciplineSkill(int nDiscipline); + +/** + * Returns the discipline for the Blade Meditation feat + * @param oInitiator Person to check + * + * @return DISCIPLINE_ constant + */ +int BladeMeditationFeat(object oInitiator); + +/** + * Returns 1 if feat and maneuver match + * @param oInitiator Person to check + * @param nMoveId Maneuver to check + * + * @return 1 or -1 + */ +int BladeMeditationDamage(object oInitiator, int nMoveId); + + +/** + * Returns 1 if Blade Meditation & Discipline match + * @param oInitiator Person to check + * @param nDiscipline Discipline to match + * + * @return 1 or 0 + */ + int HasBladeMeditationForDiscipline(object oInitiator, int nDiscipline); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "tob_move_const" +#include "prc_alterations" +#include "tob_inc_moveknwn" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + + +int _CheckPrereqsByDiscipline(object oPC, int nDiscipline, int nCount = 1) +{ + int nPrereqCount = GetManeuverCountByDiscipline(oPC, nDiscipline, MANEUVER_TYPE_MANEUVER) + + GetManeuverCountByDiscipline(oPC, nDiscipline, MANEUVER_TYPE_STANCE); + + if(nPrereqCount >= nCount) + return nPrereqCount; + + return 0; +} + +void _RecursiveStanceCheck(object oPC, object oTestWP, int nMoveId, float fFeet = 10.0) +{ + // Seeing if this works better + string sWPTag = "PRC_BMWP_" + GetName(oPC) + IntToString(nMoveId); + oTestWP = GetWaypointByTag(sWPTag); + // Distance moved in the last round + float fDist = FeetToMeters(GetDistanceBetween(oPC, oTestWP)); + // Giving them a little extra distance because of NWN's dance of death + float fCheck = FeetToMeters(fFeet); + if(DEBUG) DoDebug("_RecursiveStanceCheck: fDist: " + FloatToString(fDist)); + if(DEBUG) DoDebug("_RecursiveStanceCheck: fCheck: " + FloatToString(fCheck)); + if(DEBUG) DoDebug("_RecursiveStanceCheck: nMoveId: " + IntToString(nMoveId)); + + + // Moved the distance + if (fDist >= fCheck) + { + if(DEBUG) DoDebug("_RecursiveStanceCheck: fDist > fCheck"); + // Stances that clean up + if (nMoveId == MOVE_SD_STONEFOOT_STANCE) + { + PRCRemoveEffectsFromSpell(oPC, nMoveId); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Moved too far, cancelling stances."); + // Clean up the test WP as well + DestroyObject(oTestWP); + } + // Stances that clean up + else if (nMoveId == MOVE_MOUNTAIN_FORTRESS) + { + PRCRemoveEffectsFromSpell(oPC, nMoveId); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Moved too far, cancelling stances."); + // Clean up the test WP as well + DestroyObject(oTestWP); + } + // Stances that clean up + else if (nMoveId == MOVE_SD_ROOT_MOUNTAIN) + { + PRCRemoveEffectsFromSpell(oPC, nMoveId); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Moved too far, cancelling stances."); + // Clean up the test WP as well + DestroyObject(oTestWP); + } + else if (nMoveId == MOVE_SH_CHILD_SHADOW) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectConcealment(20)), oPC, 6.0); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Applying bonuses."); + // Clean up the test WP + DestroyObject(oTestWP); + // Create waypoint for the movement for next round + CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oPC), FALSE, sWPTag); + } + else if (nMoveId == MOVE_IH_ABSOLUTE_STEEL) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(2)), oPC, 6.0); + if(DEBUG) DoDebug("_RecursiveStanceCheck: Applying bonuses."); + // Clean up the test WP + DestroyObject(oTestWP); + // Create waypoint for the movement for next round + CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oPC), FALSE, sWPTag); + } + + else if (nMoveId == MOVE_SD_GIANTS_STANCE) + { + DeleteLocalInt(oPC, "DWGiantsStance"); + DeleteLocalInt(oPC, "PRC_Power_Expansion_SizeIncrease"); + PRCRemoveEffectsFromSpell(oPC, nMoveId); + DestroyObject(oTestWP); + } + + else if (nMoveId == MOVE_IH_DANCING_BLADE_FORM) + { + DeleteLocalInt(oPC, "DWDancingBladeForm"); + DestroyObject(oTestWP); + } + + } + // If they still have the spell, keep going + if (GetHasSpellEffect(nMoveId, oPC)) + { + DelayCommand(6.0, _RecursiveStanceCheck(oPC, oTestWP, nMoveId)); + if(DEBUG) DoDebug("_RecursiveStanceCheck: DelayCommand(6.0, _RecursiveStanceCheck(oPC, oTestWP, nMoveId))."); + } + + if(DEBUG) DoDebug("_RecursiveStanceCheck: Exiting"); +} + +int _AllowedDiscipline(object oInitiator, int nClass, int nDiscipline) +{ + //maneuver choice for prestige classes is restricted only to those disciplines + int nOverride = GetPersistantLocalInt(oInitiator, "AllowedDisciplines"); + if(nOverride == 0) + { + switch(nClass) + { + case CLASS_TYPE_CRUSADER: nOverride = 322; break;//DISCIPLINE_DEVOTED_SPIRIT + DISCIPLINE_STONE_DRAGON + DISCIPLINE_WHITE_RAVEN + case CLASS_TYPE_SWORDSAGE: nOverride = 245; break;//DISCIPLINE_DESERT_WIND + DISCIPLINE_DIAMOND_MIND + DISCIPLINE_SETTING_SUN + DISCIPLINE_SHADOW_HAND + DISCIPLINE_STONE_DRAGON + DISCIPLINE_TIGER_CLAW + case CLASS_TYPE_WARBLADE: nOverride = 460; break;//DISCIPLINE_DIAMOND_MIND + DISCIPLINE_IRON_HEART + DISCIPLINE_STONE_DRAGON + DISCIPLINE_TIGER_CLAW + DISCIPLINE_WHITE_RAVEN + } + } + return nOverride & nDiscipline; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetInitiatingClass(object oInitiator = OBJECT_SELF) +{ + return GetLocalInt(oInitiator, PRC_INITIATING_CLASS) - 1; +} + +int GetInitiatorLevel(object oInitiator = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID) +{ + int nAdjust = GetLocalInt(oInitiator, PRC_CASTERLEVEL_ADJUSTMENT); + int nLevel = GetLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE); + + // For when you want to assign the caster level. + if(nLevel) + { + if(DEBUG) SendMessageToPC(oInitiator, "GetInitiatorLevel(): Forced-level initiating at level " + IntToString(nLevel)); + //DelayCommand(1.0, DeleteLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE)); + return nLevel + nAdjust; + } + + int nTotalHD = GetHitDice(oInitiator); + + // The function user needs to know the character's Initiator level in a specific class + // instead of whatever the character last initiated a maneuver as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + if(GetIsBladeMagicClass(nSpecificClass)) + { + // Initiator level is class level + 1/2 levels in all other classes + // See ToB p39 + // Max level is therefor the level plus 1/2 of remaining levels + // Prestige classes are stuck in here + int nClassLevel = GetLevelByClass(nSpecificClass, oInitiator); + if(nClassLevel) + { + nClassLevel += GetBladeMagicPRCLevels(oInitiator); + nLevel = nClassLevel + ((nTotalHD - nClassLevel)/2); + } + } + } + else if(GetInitiatingClass(oInitiator) != -1) + { + int nClassLevel = GetLevelByClass(GetInitiatingClass(oInitiator), oInitiator); + nClassLevel += GetBladeMagicPRCLevels(oInitiator); + nLevel = nClassLevel + ((nTotalHD - nClassLevel)/2); + } + + // A character with no initiator levels has an init level of 1/2 HD (min 1) + if(!nLevel) + nLevel = PRCMax(1, nTotalHD/2); + + // This spam is technically no longer necessary once the Initiator level getting mechanism has been confirmed to work +// if(DEBUG) FloatingTextStringOnCreature("Initiator Level: " + IntToString(nLevel), oInitiator, FALSE); + + return nLevel + nAdjust; +} + +int GetIsBladeMagicUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_CRUSADER, oCreature) + || GetLevelByClass(CLASS_TYPE_SWORDSAGE, oCreature) + || GetLevelByClass(CLASS_TYPE_WARBLADE, oCreature)); +} + +int GetHighestInitiatorLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetInitiatorLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestInitiatorLevel(object oCreature) +{ + return PRCMax(PRCMax(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetInitiatorLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetInitiatorLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetInitiatorLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsBladeMagicClass(int nClass) +{ + return nClass == CLASS_TYPE_CRUSADER + || nClass == CLASS_TYPE_SWORDSAGE + || nClass == CLASS_TYPE_WARBLADE; +} + +int GetManeuverLevel(object oInitiator, int nMoveId = 0) +{ + int nLevel = GetLocalInt(oInitiator, PRC_MANEUVER_LEVEL); + if (nLevel > 0) return nLevel; + else if (nMoveId > 0) return StringToInt(lookup_spell_innate(nMoveId)); + + return 0; +} + +string GetManeuverName(int nSpellId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))); +} + +int GetManeuverType(int nSpellId) +{ + return StringToInt(GetStringRight(Get2DACache("spells", "MetaMagic", nSpellId), 1)); +} + +int GetIsStance(int nMoveId) +{ + return GetManeuverType(nMoveId) == MANEUVER_TYPE_STANCE; +} + +string GetDisciplineName(int nDiscipline) +{ + int nStrRef; + switch(nDiscipline) + { + case DISCIPLINE_DESERT_WIND: nStrRef = 16829714; break; + case DISCIPLINE_DEVOTED_SPIRIT: nStrRef = 16829715; break; + case DISCIPLINE_DIAMOND_MIND: nStrRef = 16829716; break; + case DISCIPLINE_IRON_HEART: nStrRef = 16829717; break; + case DISCIPLINE_SETTING_SUN: nStrRef = 16829718; break; + case DISCIPLINE_SHADOW_HAND: nStrRef = 16829719; break; + case DISCIPLINE_STONE_DRAGON: nStrRef = 16829720; break; + case DISCIPLINE_TIGER_CLAW: nStrRef = 16829721; break; + case DISCIPLINE_WHITE_RAVEN: nStrRef = 16829722; break; + } + return GetStringByStrRef(nStrRef); +} + +int GetDisciplineByManeuver(int nMoveId) +{ + string sSpellSchool = Get2DACache("spells", "School", nMoveId); + int nDiscipline; + + if (sSpellSchool == "A") nDiscipline = DISCIPLINE_DEVOTED_SPIRIT; + else if (sSpellSchool == "C") nDiscipline = DISCIPLINE_SETTING_SUN; + else if (sSpellSchool == "D") nDiscipline = DISCIPLINE_IRON_HEART; + else if (sSpellSchool == "E") nDiscipline = DISCIPLINE_DIAMOND_MIND; + else if (sSpellSchool == "V") nDiscipline = DISCIPLINE_DESERT_WIND; + else if (sSpellSchool == "I") nDiscipline = DISCIPLINE_SHADOW_HAND; + else if (sSpellSchool == "N") nDiscipline = DISCIPLINE_WHITE_RAVEN; + else if (sSpellSchool == "T") nDiscipline = DISCIPLINE_TIGER_CLAW; + else if (sSpellSchool == "G") nDiscipline = DISCIPLINE_STONE_DRAGON; + + return nDiscipline; +} + +int GetBladeMagicPRCLevels(object oInitiator) +{ + int nRace = GetRacialType(oInitiator); + + int nLevel = GetLevelByClass(CLASS_TYPE_DEEPSTONE_SENTINEL, oInitiator) + + GetLevelByClass(CLASS_TYPE_BLOODCLAW_MASTER, oInitiator) + + GetLevelByClass(CLASS_TYPE_RUBY_VINDICATOR, oInitiator) + + GetLevelByClass(CLASS_TYPE_JADE_PHOENIX_MAGE, oInitiator) + + GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator) + + GetLevelByClass(CLASS_TYPE_ETERNAL_BLADE, oInitiator) + + GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oInitiator); + + if (nRace == RACIAL_TYPE_RETH_DEKALA) + { + nLevel += GetLevelByClass(CLASS_TYPE_OUTSIDER, oInitiator); + } + + + return nLevel; +} + +int GetPrimaryBladeMagicClass(object oCreature = OBJECT_SELF) +{ + int nClass = CLASS_TYPE_INVALID; + + if(GetPRCSwitch(PRC_CASTERLEVEL_FIRST_CLASS_RULE)) + { + int nBladeMagicPos = GetFirstBladeMagicClassPosition(oCreature); + if (!nBladeMagicPos) return CLASS_TYPE_INVALID; // no Blade Magic initiating class + + nClass = GetClassByPosition(nBladeMagicPos, oCreature); + } + else + { + /*int i, nLevel, nTest, nTestLevel; + for(i = 1; i < 4; i++) + { + nTest = GetClassByPosition(i, oCreature); + if(GetIsBladeMagicClass(nTest)) + { + nTestLevel = GetLevelByClass(nTest, oCreature); + if(nTestLevel > nLevel) + { + nClass = nTest; + nLevel = nTestLevel; + } + } + }*/ + + int nClassLvl; + int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8; + int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl; + + nClass1 = GetClassByPosition(1, oCreature); + nClass2 = GetClassByPosition(2, oCreature); + nClass3 = GetClassByPosition(3, oCreature); + nClass4 = GetClassByPosition(4, oCreature); + nClass5 = GetClassByPosition(5, oCreature); + nClass6 = GetClassByPosition(6, oCreature); + nClass7 = GetClassByPosition(7, oCreature); + nClass8 = GetClassByPosition(8, oCreature); + + if(GetIsBladeMagicClass(nClass1)) nClass1Lvl = GetLevelByClass(nClass1, oCreature); + if(GetIsBladeMagicClass(nClass2)) nClass2Lvl = GetLevelByClass(nClass2, oCreature); + if(GetIsBladeMagicClass(nClass3)) nClass3Lvl = GetLevelByClass(nClass3, oCreature); + if(GetIsBladeMagicClass(nClass4)) nClass4Lvl = GetLevelByClass(nClass4, oCreature); + if(GetIsBladeMagicClass(nClass5)) nClass5Lvl = GetLevelByClass(nClass5, oCreature); + if(GetIsBladeMagicClass(nClass6)) nClass6Lvl = GetLevelByClass(nClass6, oCreature); + if(GetIsBladeMagicClass(nClass7)) nClass7Lvl = GetLevelByClass(nClass7, oCreature); + if(GetIsBladeMagicClass(nClass8)) nClass8Lvl = GetLevelByClass(nClass8, oCreature); + + nClass = nClass1; + nClassLvl = nClass1Lvl; + + if(nClass2Lvl > nClassLvl) + { + nClass = nClass2; + nClassLvl = nClass2Lvl; + } + if(nClass3Lvl > nClassLvl) + { + nClass = nClass3; + nClassLvl = nClass3Lvl; + } + if(nClass4Lvl > nClassLvl) + { + nClass = nClass4; + nClassLvl = nClass4Lvl; + } + if(nClass5Lvl > nClassLvl) + { + nClass = nClass5; + nClassLvl = nClass5Lvl; + } + if(nClass6Lvl > nClassLvl) + { + nClass = nClass6; + nClassLvl = nClass6Lvl; + } + if(nClass7Lvl > nClassLvl) + { + nClass = nClass7; + nClassLvl = nClass7Lvl; + } + if(nClass8Lvl > nClassLvl) + { + nClass = nClass8; + nClassLvl = nClass8Lvl; + } + + if(nClassLvl == 0) + nClass = CLASS_TYPE_INVALID; + } + + return nClass; +} + +int GetFirstBladeMagicClassPosition(object oCreature = OBJECT_SELF) +{ + if (GetIsBladeMagicClass(GetClassByPosition(1, oCreature))) + return 1; + if (GetIsBladeMagicClass(GetClassByPosition(2, oCreature))) + return 2; + if (GetIsBladeMagicClass(GetClassByPosition(3, oCreature))) + return 3; + if (GetIsBladeMagicClass(GetClassByPosition(4, oCreature))) + return 4; + if (GetIsBladeMagicClass(GetClassByPosition(5, oCreature))) + return 5; + if (GetIsBladeMagicClass(GetClassByPosition(6, oCreature))) + return 6; + if (GetIsBladeMagicClass(GetClassByPosition(7, oCreature))) + return 7; + if (GetIsBladeMagicClass(GetClassByPosition(8, oCreature))) + return 8; + + return 0; +} + +int CheckManeuverPrereqs(int nClass, int nPrereqs, int nDiscipline, object oPC) +{ + // Checking to see what the name of the feat is, and the row number + /*if (DEBUG) + { + DoDebug("CheckManeuverPrereqs: nFeat: " + IntToString(nFeat)); + string sFeatName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat))); + DoDebug("CheckManeuverPrereqs: sFeatName: " + sFeatName); + }*/ + + // Prestige classes can only access certain disciplines + if(!_AllowedDiscipline(oPC, nClass, nDiscipline)) + return FALSE; + + // If this maneuver has a prereq, check for it + if(nPrereqs) + // if it returns false, exit, otherwise they can take the maneuver + return _CheckPrereqsByDiscipline(oPC, nDiscipline, nPrereqs); + + // if you've reached this far then return TRUE + return TRUE; +} + +int GetIsManeuverSupernatural(int nMoveId) +{ + if(nMoveId == MOVE_DW_BLISTERING_FLOURISH + || nMoveId == MOVE_DW_BURNING_BLADE + || nMoveId == MOVE_DW_BURNING_BRAND + || nMoveId == MOVE_DW_DEATH_MARK + || nMoveId == MOVE_DW_DISTRACTING_EMBER + || nMoveId == MOVE_DW_DRAGONS_FLAME + || nMoveId == MOVE_DW_FAN_FLAMES + || nMoveId == MOVE_DW_FIERY_ASSAULT + || nMoveId == MOVE_DW_FIRE_RIPOSTE + || nMoveId == MOVE_DW_FIRESNAKE + || nMoveId == MOVE_DW_FLAMES_BLESSING + || nMoveId == MOVE_DW_HATCHLINGS_FLAME + || nMoveId == MOVE_DW_HOLOCAUST_CLOAK + || nMoveId == MOVE_DW_INFERNO_BLADE + || nMoveId == MOVE_DW_INFERNO_BLAST + || nMoveId == MOVE_DW_LEAPING_FLAME + || nMoveId == MOVE_DW_LINGERING_INFERNO + || nMoveId == MOVE_DW_RING_FIRE + || nMoveId == MOVE_DW_RISING_PHOENIX + || nMoveId == MOVE_DW_SALAMANDER_CHARGE + || nMoveId == MOVE_DW_SEARING_BLADE + || nMoveId == MOVE_DW_SEARING_CHARGE + || nMoveId == MOVE_DW_WYRMS_FLAME + || nMoveId == MOVE_SH_BALANCE_SKY + || nMoveId == MOVE_SH_CHILD_SHADOW + || nMoveId == MOVE_SH_CLINGING_SHADOW + || nMoveId == MOVE_SH_CLOAK_DECEPTION + || nMoveId == MOVE_SH_ENERVATING_SHADOW + || nMoveId == MOVE_SH_FIVE_SHADOW_CREEPING + || nMoveId == MOVE_SH_GHOST_BLADE + || nMoveId == MOVE_SH_OBSCURING_SHADOW_VEIL + || nMoveId == MOVE_SH_SHADOW_BLADE_TECH + || nMoveId == MOVE_SH_SHADOW_GARROTTE + || nMoveId == MOVE_SH_SHADOW_NOOSE + || nMoveId == MOVE_SH_STRENGTH_DRAINING) + return TRUE; + + // If nothing returns TRUE, fail + return FALSE; +} + +int GetHasActiveStance(object oInitiator) +{ + int nStance = GetLocalInt(oInitiator, "TOBStanceOne"); + if(GetHasSpellEffect(nStance, oInitiator)) + return nStance; + + nStance = GetLocalInt(oInitiator, "TOBStanceTwo"); + if(GetHasSpellEffect(nStance, oInitiator)) + return nStance; + + return FALSE; +} + +void RemoveStance(object oInitiator, int nStance) +{ + PRCRemoveEffectsFromSpell(oInitiator, nStance); + + //stances with special handling goes here + if(nStance == MOVE_DS_AURA_CHAOS) + DeleteLocalInt(oInitiator, "DSChaos"); + else if(nStance == MOVE_DS_PERFECT_ORDER) + DeleteLocalInt(oInitiator, "DSPerfectOrder"); + else if(nStance == MOVE_DW_RISING_PHOENIX) + RemoveItemProperty(GetPCSkin(oInitiator), ItemPropertyBonusFeat(IP_CONST_FEAT_RISING_PHOENIX)); + else if(nStance == MOVE_SH_ASSASSINS_STANCE) + { + DelayCommand(0.1, ExecuteScript("prc_sneak_att", oInitiator)); + if (DEBUG) DoDebug("Cleaning assassin's stance"); + } + else if(nStance == MOVE_MYSTIC_PHOENIX || nStance == MOVE_MYSTIC_PHOENIX_AUG) + { + if(DEBUG) DoDebug("Removing Mystic Phoenix Stance"); + DeleteLocalInt(oInitiator, "ToB_JPM_MystP"); + } + else if(nStance == MOVE_FIREBIRD_STANCE || nStance == MOVE_FIREBIRD_STANCE_AUG) + { + if(DEBUG) DoDebug("Removing Firebird Stance"); + DeleteLocalInt(oInitiator, "ToB_JPM_FireB"); + } + else if(nStance == MOVE_CHILD_SL_STANCE) + { + DeleteLocalInt(oInitiator, "SSN_CHILDSL_SETP"); + RemoveEventScript(oInitiator, EVENT_ONHEARTBEAT, "tob_ssn_childsl", TRUE, FALSE); + } +} + +void ClearStances(object oInitiator, int nDontClearMove) +{ + // Clears spell effects, will not clear DontClearMove + // This is used to allow Warblades to have two stances. + int nStance = GetLocalInt(oInitiator, "TOBStanceOne"); + if(GetHasSpellEffect(nStance, oInitiator) && nStance != nDontClearMove) + { + RemoveStance(oInitiator, nStance); + DeleteLocalInt(oInitiator, "TOBStanceOne"); + } + + nStance = GetLocalInt(oInitiator, "TOBStanceTwo"); + if(GetHasSpellEffect(nStance, oInitiator) && nStance != nDontClearMove) + { + RemoveStance(oInitiator, nStance); + DeleteLocalInt(oInitiator, "TOBStanceTwo"); + } +} + +void MarkStanceActive(object oInitiator, int nStance) +{ + // If the first stance is active, use second + // This should only be called with the first active when it is legal to have two stances + if(GetLocalInt(oInitiator, "TOBStanceOne") > 0) SetLocalInt(oInitiator, "TOBStanceTwo", nStance); + else SetLocalInt(oInitiator, "TOBStanceOne", nStance); +} + +effect VersusSizeEffect(object oInitiator, effect eEffect, int nSize) +{ + // Right now this only deals with medium and small PCs + int nPCSize = PRCGetCreatureSize(oInitiator); + effect eLink; + // Creatures larger than PC + if (nSize == 1) + { + eLink = VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ABERRATION); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_CONSTRUCT)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_DRAGON)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ELEMENTAL)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_GIANT)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_OUTSIDER)); + if (nPCSize == CREATURE_SIZE_SMALL) + { + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ANIMAL)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_BEAST)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_DWARF)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_ELF)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HALFELF)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HALFORC)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMAN)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_GOBLINOID)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_MONSTROUS)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_ORC)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HUMANOID_REPTILIAN)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_MAGICAL_BEAST)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_OOZE)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_SHAPECHANGER)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_UNDEAD)); + } + }// Smaller + if (nSize == 0) + { + eLink = VersusRacialTypeEffect(eEffect, RACIAL_TYPE_FEY); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_VERMIN)); + if (nPCSize == CREATURE_SIZE_MEDIUM) + { + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_GNOME)); + eLink = EffectLinkEffects(eLink, VersusRacialTypeEffect(eEffect, RACIAL_TYPE_HALFLING)); + } + } + + return eLink; +} + +void InitiatorMovementCheck(object oPC, int nMoveId, float fFeet = 10.0) +{ + // Check to see if the WP is valid + string sWPTag = "PRC_BMWP_" + GetName(oPC) + IntToString(nMoveId); + object oTestWP = GetWaypointByTag(sWPTag); + if (!GetIsObjectValid(oTestWP)) + { + // Create waypoint for the movement + CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oPC), FALSE, sWPTag); + if(DEBUG) DoDebug("InitiatorMovementCheck: WP for " + DebugObject2Str(oPC) + " didn't exist, creating. Tag: " + sWPTag); + } + // Start the recursive HB check for movement + // Seeing if this solves some of the issues with it + DelayCommand(2.0, _RecursiveStanceCheck(oPC, oTestWP, nMoveId, fFeet)); +} + +void DoDesertWindBoost(object oPC) +{ + if(DEBUG) DoDebug("DoDesertWindBoost running"); + + object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (IPGetIsMeleeWeapon(oItem)) + { + effect eVis = EffectLinkEffects(EffectVisualEffect(VFX_IMP_FLAME_M), EffectVisualEffect(VFX_IMP_PULSE_FIRE)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + // Add eventhook to the item + AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE); + DelayCommand(6.0, RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE)); + // Add the OnHit and vfx + IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_FIRE), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + SetLocalInt(oPC, "DesertWindBoost", PRCGetSpellId()); + DelayCommand(6.0, DeleteLocalInt(oPC, "DesertWindBoost")); + } + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + if (IPGetIsMeleeWeapon(oItem)) + { + // Add eventhook to the item + AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE); + DelayCommand(6.0, RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_dw_onhit", TRUE, FALSE)); + // Add the OnHit and vfx + IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_FIRE), 6.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } +} + +object GetCrusaderHealTarget(object oPC, float fDistance) +{ + object oReturn; + int nTest, nCurrentMin = 100; + location lTarget = GetLocation(oPC); + object oTest = MyFirstObjectInShape(SHAPE_SPHERE, fDistance, lTarget); + while(GetIsObjectValid(oTest)) + { + if(GetIsFriend(oTest, oPC)) + { + nTest = (GetCurrentHitPoints(oTest) * 100) / GetMaxHitPoints(oTest); + // Check HP vs current biggest loss + if(nTest < nCurrentMin && GetCurrentHitPoints(oTest) > 0 && !GetIsDead(oTest)) + { + nCurrentMin = nTest; + oReturn = oTest; + } + } + //Get the next target in the specified area around the caster + oTest = MyNextObjectInShape(SHAPE_SPHERE, fDistance, lTarget); + } + if(DEBUG) DoDebug("GetCrusaderHealTarget: oReturn " + GetName(oReturn)); + return oReturn; +} + +int GetHasInsightfulStrike(object oInitiator) +{ + int nDiscToCheck = GetDisciplineByManeuver(PRCGetSpellId()); + int nFeat; + switch(nDiscToCheck) + { + case DISCIPLINE_DESERT_WIND: nFeat = FEAT_SS_DF_IS_DW; break; + case DISCIPLINE_DIAMOND_MIND: nFeat = FEAT_SS_DF_IS_DM; break; + case DISCIPLINE_SETTING_SUN: nFeat = FEAT_SS_DF_IS_SS; break; + case DISCIPLINE_SHADOW_HAND: nFeat = FEAT_SS_DF_IS_SH; break; + case DISCIPLINE_STONE_DRAGON: nFeat = FEAT_SS_DF_IS_SD; break; + case DISCIPLINE_TIGER_CLAW: nFeat = FEAT_SS_DF_IS_TC; break; + } + if(GetHasFeat(nFeat, oInitiator)) + return TRUE; + + return FALSE; +} + +int GetHasDefensiveStance(object oInitiator, int nDiscipline) +{ + // Because this is only called from inside the proper stances + // Its just a check to see if they should link in the save boost. + int nFeat; + switch(nDiscipline) + { + case DISCIPLINE_DESERT_WIND: nFeat = FEAT_SS_DF_DS_DW; break; + case DISCIPLINE_DIAMOND_MIND: nFeat = FEAT_SS_DF_DS_DM; break; + case DISCIPLINE_SETTING_SUN: nFeat = FEAT_SS_DF_DS_SS; break; + case DISCIPLINE_SHADOW_HAND: nFeat = FEAT_SS_DF_DS_SH; break; + case DISCIPLINE_STONE_DRAGON: nFeat = FEAT_SS_DF_DS_SD; break; + case DISCIPLINE_TIGER_CLAW: nFeat = FEAT_SS_DF_DS_TC; break; + } + if(GetHasFeat(nFeat, oInitiator)) + return TRUE; + + return FALSE; +} + +int TOBGetHasDiscipline(object oInitiator, int nDiscipline) +{ + switch(nDiscipline) + { + case DISCIPLINE_DEVOTED_SPIRIT: return GetLevelByClass(CLASS_TYPE_CRUSADER, oInitiator); + case DISCIPLINE_DESERT_WIND: + case DISCIPLINE_SETTING_SUN: + case DISCIPLINE_SHADOW_HAND: return GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator); + case DISCIPLINE_IRON_HEART: return GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + case DISCIPLINE_DIAMOND_MIND: + case DISCIPLINE_TIGER_CLAW: return GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator) || GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + case DISCIPLINE_WHITE_RAVEN: return GetLevelByClass(CLASS_TYPE_CRUSADER, oInitiator) || GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + case DISCIPLINE_STONE_DRAGON: return GetLevelByClass(CLASS_TYPE_CRUSADER, oInitiator) || GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator) || GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator); + } + return FALSE; +} + +int TOBGetHasDisciplineFocus(object oInitiator, int nDiscipline) +{ + int nFeat1, nFeat2, nFeat3; + switch(nDiscipline) + { + case DISCIPLINE_DESERT_WIND: nFeat1 = FEAT_SS_DF_DS_DW; nFeat2 = FEAT_SS_DF_IS_DW; nFeat3 = FEAT_SS_DF_WF_DW; break; + case DISCIPLINE_DIAMOND_MIND: nFeat1 = FEAT_SS_DF_DS_DM; nFeat2 = FEAT_SS_DF_IS_DM; nFeat3 = FEAT_SS_DF_WF_DM; break; + case DISCIPLINE_SETTING_SUN: nFeat1 = FEAT_SS_DF_DS_SS; nFeat2 = FEAT_SS_DF_IS_SS; nFeat3 = FEAT_SS_DF_WF_SS; break; + case DISCIPLINE_SHADOW_HAND: nFeat1 = FEAT_SS_DF_DS_SH; nFeat2 = FEAT_SS_DF_IS_SH; nFeat3 = FEAT_SS_DF_WF_SH; break; + case DISCIPLINE_STONE_DRAGON: nFeat1 = FEAT_SS_DF_DS_SD; nFeat2 = FEAT_SS_DF_IS_SD; nFeat3 = FEAT_SS_DF_WF_SD; break; + case DISCIPLINE_TIGER_CLAW: nFeat1 = FEAT_SS_DF_DS_TC; nFeat2 = FEAT_SS_DF_IS_TC; nFeat3 = FEAT_SS_DF_WF_TC; break; + } + if(GetHasFeat(nFeat1, oInitiator) || GetHasFeat(nFeat2, oInitiator) || GetHasFeat(nFeat3, oInitiator)) + return TRUE; + + // If none of those trigger. + return FALSE; +} + +int GetIsDisciplineWeapon(object oWeapon, int nDiscipline) +{ + int nType = GetBaseItemType(oWeapon); + if(nDiscipline == DISCIPLINE_DESERT_WIND) + { + if(nType == BASE_ITEM_SCIMITAR + || nType == BASE_ITEM_LIGHTMACE + || nType == BASE_ITEM_SHORTSPEAR + || nType == BASE_ITEM_LIGHT_PICK + || nType == BASE_ITEM_FALCHION) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_DEVOTED_SPIRIT) + { + if(nType == BASE_ITEM_LONGSWORD + || nType == BASE_ITEM_HEAVYFLAIL + || nType == BASE_ITEM_MAUL + || nType == BASE_ITEM_FALCHION) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_DIAMOND_MIND) + { + if(nType == BASE_ITEM_BASTARDSWORD + || nType == BASE_ITEM_KATANA + || nType == BASE_ITEM_SHORTSPEAR + || nType == BASE_ITEM_RAPIER) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_IRON_HEART) + { + if(nType == BASE_ITEM_BASTARDSWORD + || nType == BASE_ITEM_KATANA + || nType == BASE_ITEM_LONGSWORD + || nType == BASE_ITEM_TWOBLADEDSWORD + || nType == BASE_ITEM_DWARVENWARAXE) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_SETTING_SUN) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_INVALID + || nType == BASE_ITEM_QUARTERSTAFF + || nType == BASE_ITEM_SHORTSWORD + || nType == BASE_ITEM_NUNCHAKU) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_SHADOW_HAND) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_DAGGER + || nType == BASE_ITEM_INVALID + || nType == BASE_ITEM_SHORTSWORD + || nType == BASE_ITEM_SAI) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_STONE_DRAGON) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_GREATAXE + || nType == BASE_ITEM_INVALID + || nType == BASE_ITEM_GREATSWORD + || nType == BASE_ITEM_HEAVY_MACE) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_TIGER_CLAW) + { + // Invalid is empty handed / Unarmed strike + if(nType == BASE_ITEM_KUKRI + || nType == BASE_ITEM_KAMA + || nType == BASE_ITEM_HANDAXE + || nType == BASE_ITEM_GREATAXE + || nType == BASE_ITEM_INVALID) + return TRUE; + } + else if(nDiscipline == DISCIPLINE_WHITE_RAVEN) + { + if(nType == BASE_ITEM_BATTLEAXE + || nType == BASE_ITEM_LONGSWORD + || nType == BASE_ITEM_HALBERD + || nType == BASE_ITEM_WARHAMMER + || nType == BASE_ITEM_GREATSWORD) + return TRUE; + } + + // If none of those trigger. + return FALSE; +} + +int TOBSituationalAttackBonuses(object oInitiator, int nDiscipline, int nClass = CLASS_TYPE_INVALID) +{ + int nBonus = 0; + if(GetLevelByClass(CLASS_TYPE_BLOODCLAW_MASTER, oInitiator) >= 4 + && nDiscipline == DISCIPLINE_TIGER_CLAW) + nBonus += 1; + + return nBonus; +} + +int GetDisciplineSkill(int nDiscipline) +{ + if(nDiscipline == DISCIPLINE_DESERT_WIND) + { + return SKILL_TUMBLE; + } + else if(nDiscipline == DISCIPLINE_DEVOTED_SPIRIT) + { + return SKILL_INTIMIDATE; + } + else if(nDiscipline == DISCIPLINE_DIAMOND_MIND) + { + return SKILL_CONCENTRATION; + } + else if(nDiscipline == DISCIPLINE_IRON_HEART) + { + return SKILL_BALANCE; + } + else if(nDiscipline == DISCIPLINE_SETTING_SUN) + { + return SKILL_SENSE_MOTIVE; + } + else if(nDiscipline == DISCIPLINE_SHADOW_HAND) + { + return SKILL_HIDE; + } + else if(nDiscipline == DISCIPLINE_STONE_DRAGON) + { + return SKILL_BALANCE; + } + else if(nDiscipline == DISCIPLINE_TIGER_CLAW) + { + return SKILL_JUMP; + } + else if(nDiscipline == DISCIPLINE_WHITE_RAVEN) + { + return SKILL_PERSUADE; + } + + // If none of those trigger. + return -1; +} + +int BladeMeditationFeat(object oInitiator) +{ + if(GetHasFeat(FEAT_BLADE_MEDITATION_DESERT_WIND , oInitiator)) return DISCIPLINE_DESERT_WIND ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_DEVOTED_SPIRIT, oInitiator)) return DISCIPLINE_DEVOTED_SPIRIT; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_DIAMOND_MIND , oInitiator)) return DISCIPLINE_DIAMOND_MIND ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_IRON_HEART , oInitiator)) return DISCIPLINE_IRON_HEART ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_SETTING_SUN , oInitiator)) return DISCIPLINE_SETTING_SUN ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_SHADOW_HAND , oInitiator)) return DISCIPLINE_SHADOW_HAND ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_STONE_DRAGON , oInitiator)) return DISCIPLINE_STONE_DRAGON ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_TIGER_CLAW , oInitiator)) return DISCIPLINE_TIGER_CLAW ; + else if(GetHasFeat(FEAT_BLADE_MEDITATION_WHITE_RAVEN , oInitiator)) return DISCIPLINE_WHITE_RAVEN ; + + return -1; +} + +int BladeMeditationDamage(object oInitiator, int nMoveId) +{ + int nDisc = BladeMeditationFeat(oInitiator); + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator); + if (nDisc == GetDisciplineByManeuver(nMoveId) && GetIsDisciplineWeapon(oWeapon, nDisc)) + return 1; + + return -1; +} + +int HasBladeMeditationForDiscipline(object oInitiator, int nDiscipline) +{ + if (!GetIsObjectValid(oInitiator)) + return FALSE; + + int nFeatDiscipline = BladeMeditationFeat(oInitiator); + + // If the discipline for Blade Meditation matches the one we're checking, return true + if (nFeatDiscipline == nDiscipline) + return TRUE; + + return FALSE; +} \ No newline at end of file diff --git a/src/include/tob_move_const.nss b/src/include/tob_move_const.nss new file mode 100644 index 0000000..bdd2218 --- /dev/null +++ b/src/include/tob_move_const.nss @@ -0,0 +1,304 @@ +// Real Maneuver SpellId Constants +/* + DW_ Denotes Desert Wind + DS_ Denotes Devoted Spirit + DM_ Denotes Diamond Mind + IH_ Denotes Iron Heart + SS_ Denotes Setting Sun + SH_ Denotes Shadow Hand + SD_ Denotes Stone Dragon + TC_ Denotes Tiger Claw + WR_ Denotes White Raven +*/ + +// Level 1 Maneuvers +const int MOVE_DW_BLISTERING_FLOURISH = 17301; +const int MOVE_DW_BURNING_BLADE = 17302; +const int MOVE_DW_DISTRACTING_EMBER = 17306; +const int MOVE_DW_FLAMES_BLESSING = 17312; +const int MOVE_DW_WIND_STRIDE = 17325; +const int MOVE_DS_CRUSADERS_STRIKE = 17333; +const int MOVE_DS_IRON_GUARDS_GLARE = 17342; +const int MOVE_DS_MARTIAL_SPIRIT = 17344; +const int MOVE_DS_VANGUARD_STRIKE = 17353; +const int MOVE_DM_MOMENT_PERFECT_MIND = 17367; +const int MOVE_DM_SAPPHIRE_NIGHTMARE = 17372; +const int MOVE_DM_STANCE_OF_CLARITY = 17374; +const int MOVE_IH_PUNISHING_STANCE = 17390; +const int MOVE_IH_STEEL_WIND = 17392; +const int MOVE_IH_STEELY_STRIKE = 17393; +const int MOVE_SS_COUNTER_CHARGE = 17401; +const int MOVE_SS_MIGHTY_THROW = 17408; +const int MOVE_SS_STEP_WIND = 17414; +const int MOVE_SH_CHILD_SHADOW = 17420; +const int MOVE_SH_CLINGING_SHADOW = 17421; +const int MOVE_SH_ISLAND_BLADES = 17430; +const int MOVE_SH_SHADOW_BLADE_TECH = 17433; +const int MOVE_SD_CHARGING_MINOTAUR = 17447; +const int MOVE_SD_STONE_BONES = 17461; +const int MOVE_SD_STONEFOOT_STANCE = 17464; +const int MOVE_TC_BLOOD_WATER = 17469; +const int MOVE_TC_HUNTERS_SENSE = 17478; +const int MOVE_TC_SUDDEN_LEAP = 17486; +const int MOVE_TC_WOLF_FANG_STRIKE = 17489; +const int MOVE_WR_BOLSTERING_VOICE = 17493; +const int MOVE_WR_DOUSE_FLAMES = 17496; +const int MOVE_WR_LEADING_ATTACK = 17498; +const int MOVE_WR_LEADING_CHARGE = 17499; + +// Level 2 Maneuvers +const int MOVE_DW_BURNING_BRAND = 17303; +const int MOVE_DW_FIRE_RIPOSTE = 17310; +const int MOVE_DW_FLASHING_SUN = 17313; +const int MOVE_DW_HATCHLINGS_FLAME = 17314; +const int MOVE_DS_FOEHAMMER = 17340; +const int MOVE_DS_SHIELD_BLOCK = 17348; +const int MOVE_DM_ACTION_BEFORE_THOUGHT = 17354; +const int MOVE_DM_EMERALD_RAZOR = 17360; +const int MOVE_IH_DISARMING_STRIKE = 17380; +const int MOVE_IH_WALL_BLADES = 17396; +const int MOVE_SS_BAFFLING_DEFENSE = 17397; +const int MOVE_SS_CLEVER_POSITIONING = 17399; +const int MOVE_SH_CLOAK_DECEPTION = 17422; +const int MOVE_SH_DRAIN_VITALITY = 17425; +const int MOVE_SH_SHADOW_JAUNT = 17436; +const int MOVE_SD_MOUNTAIN_HAMMER = 17457; +const int MOVE_SD_STONE_VISE = 17463; +const int MOVE_TC_CLAW_MOON = 17470; +const int MOVE_TC_RABID_WOLF_STRIKE = 17483; +const int MOVE_WR_BATTLE_LEADERS_CHARGE = 17492; +const int MOVE_WR_TACTICAL_STRIKE = 17505; + +// Level 3 Maneuvers +const int MOVE_DW_DEATH_MARK = 17304; +const int MOVE_DW_FAN_FLAMES = 17308; +const int MOVE_DW_HOLOCAUST_CLOAK = 17315; +const int MOVE_DW_ZEPHYR_DANCE = 17327; +const int MOVE_DS_DEFENSIVE_REBUKE = 17335; +const int MOVE_DS_REVITALIZING_STRIKE = 17347; +const int MOVE_DS_THICKET_BLADES = 17351; +const int MOVE_DM_INSIGHTFUL_STRIKE = 17362; +const int MOVE_DM_MIND_OVER_BODY = 17364; +const int MOVE_DM_PEARL_BLACK_DOUBT = 17368; +const int MOVE_IH_ABSOLUTE_STEEL = 17376; +const int MOVE_IH_EXORCISM_STEEL = 17381; +const int MOVE_IH_IRON_HEART_SURGE = 17385; +const int MOVE_SS_DEVASTATING_THROW = 17402; +const int MOVE_SS_FEIGNED_OPENING = 17403; +const int MOVE_SS_GIANT_KILLING_STYLE = 17406; +const int MOVE_SH_ASSASSINS_STANCE = 17417; +const int MOVE_SH_DANCE_SPIDER = 17423; +const int MOVE_SH_SHADOW_GARROTTE = 17435; +const int MOVE_SH_STRENGTH_DRAINING = 17441; +const int MOVE_SD_BONECRUSHER = 17445; +const int MOVE_SD_CRUSHING_WEIGHT = 17450; +const int MOVE_SD_ROOT_MOUNTAIN = 17460; +const int MOVE_SD_STONE_DRAGONS_FURY = 17462; +const int MOVE_TC_FLESH_RIPPER = 17474; +const int MOVE_TC_LEAPING_DRAGON = 17479; +const int MOVE_TC_SOARING_RAPTOR_STRIKE = 17485; +const int MOVE_TC_WOLVERINE_STANCE = 17491; +const int MOVE_WR_LIONS_ROAR = 17500; +const int MOVE_WR_TACTICS_WOLF = 17506; +const int MOVE_WR_WHITE_RAVEN_TACTICS = 17511; + +// Level 4 Maneuvers +const int MOVE_DW_FIRESNAKE = 17311; +const int MOVE_DW_SEARING_BLADE = 17323; +const int MOVE_DW_SEARING_CHARGE = 17324; +const int MOVE_DS_DIVINE_SURGE = 17336; +const int MOVE_DS_ENTANGLING_BLADE = 17339; +const int MOVE_DM_BOUNDING_ASSAULT = 17356; +const int MOVE_DM_MIND_STRIKE = 17365; +const int MOVE_DM_RUBY_NIGHTMARE_BLADE = 17371; +const int MOVE_IH_LIGHTNING_RECOVERY = 17386; +const int MOVE_IH_MITHRAL_TORNADO = 17389; +const int MOVE_SS_COMET_THROW = 17400; +const int MOVE_SS_STRIKE_BROKEN_SHIELD = 17415; +const int MOVE_SH_HAND_DEATH = 17429; +const int MOVE_SH_OBSCURING_SHADOW_VEIL = 17431; +const int MOVE_SD_BONESPLITTING_STRIKE = 17444; +const int MOVE_SD_BOULDER_ROLL = 17446; +const int MOVE_SD_OVERWHELMING_MOUNTAIN = 17459; +const int MOVE_TC_DEATH_FROM_ABOVE = 17472; +const int MOVE_TC_FOUNTAIN_BLOOD = 17475; +const int MOVE_WR_COVERING_STRIKE = 17495; +const int MOVE_WR_WHITE_RAVEN_STRIKE = 17510; + +// Level 5 Maneuvers +const int MOVE_DW_DRAGONS_FLAME = 17307; +const int MOVE_DW_LEAPING_FLAME = 17318; +const int MOVE_DW_LINGERING_INFERNO = 17319; +const int MOVE_DS_DAUNTING_STRIKE = 17334; +const int MOVE_DS_DOOM_CHARGE = 17338; +const int MOVE_DS_LAW_BEARER = 17343; +const int MOVE_DS_RADIANT_CHARGE = 17345; +const int MOVE_DS_TIDE_CHAOS = 17352; +const int MOVE_DM_DISRUPTING_BLOW = 17359; +const int MOVE_DM_HEARING_AIR = 17361; +const int MOVE_DM_RAPID_COUNTER = 17370; +const int MOVE_IH_DANCING_BLADE_FORM = 17378; +const int MOVE_IH_DAZING_STRIKE = 17379; +const int MOVE_IH_IRON_HEART_FOCUS = 17384; +const int MOVE_SS_MIRRORED_PURSUIT = 17409; +const int MOVE_SS_SHIFTING_DEFENSE = 17411; +const int MOVE_SS_SOARING_THROW = 17412; +const int MOVE_SS_STALKING_SHADOW = 17413; +const int MOVE_SH_BLOODLETTING_STRIKE = 17419; +const int MOVE_SH_SHADOW_STRIDE = 17438; +const int MOVE_SH_STEP_DANCING_MOTH = 17440; +const int MOVE_SD_ELDER_MOUNTAIN_HAMMER = 17452; +const int MOVE_SD_GIANTS_STANCE = 17453; +const int MOVE_SD_MOUNTAIN_AVALANCHE = 17456; +const int MOVE_TC_DANCING_MONGOOSE = 17471; +const int MOVE_TC_POUNCING_CHARGE = 17480; +const int MOVE_WR_FLANKING_MANEUVER = 17497; +const int MOVE_WR_PRESS_ADVANTAGE = 17502; + +// Level 6 Maneuvers +const int MOVE_DW_DESERT_TEMPEST = 17305; +const int MOVE_DW_FIERY_ASSAULT = 17309; +const int MOVE_DW_RING_FIRE = 17320; +const int MOVE_DS_AURA_CHAOS = 17328; +const int MOVE_DS_PERFECT_ORDER = 17329; +const int MOVE_DS_AURA_TRIUMPH = 17330; +const int MOVE_DS_AURA_TYRANNY = 17331; +const int MOVE_DS_RALLYING_STRIKE = 17346; +const int MOVE_DM_GREATER_INSIGHTFUL = 17363; +const int MOVE_DM_MOMENT_ALACRITY = 17366; +const int MOVE_IH_IRON_HEART_ENDURANCE = 17383; +const int MOVE_IH_MANTICORE_PARRY = 17388; +const int MOVE_SS_BALLISTA_THROW = 17398; +const int MOVE_SS_SCORPION_PARRY = 17410; +const int MOVE_SH_GHOST_BLADE = 17428; +const int MOVE_SH_SHADOW_NOOSE = 17437; +const int MOVE_SH_STALKER_NIGHT = 17439; +const int MOVE_SD_CRUSHING_VISE = 17449; +const int MOVE_SD_IRON_BONES = 17454; +const int MOVE_SD_IRRESISTIBLE_MOUNTAIN = 17455; +const int MOVE_TC_RABID_BEAR_STRIKE = 17482; +const int MOVE_TC_WOLF_CLIMBS_MOUNTAIN = 17488; +const int MOVE_WR_ORDER_FROM_CHAOS = 17501; +const int MOVE_WR_WAR_LEADERS_CHARGE = 17507; + +// Level 7 Maneuvers +const int MOVE_DW_INFERNO_BLADE = 17316; +const int MOVE_DW_SALAMANDER_CHARGE = 17322; +const int MOVE_DS_CASTIGATING_STRIKE = 17332; +const int MOVE_DS_SHIELD_COUNTER = 17349; +const int MOVE_DM_AVALANCHE_BLADES = 17355; +const int MOVE_DM_QUICKSILVER_MOTION = 17369; +const int MOVE_IH_FINISHING_MOVE = 17382; +const int MOVE_IH_SCYTHING_BLADE = 17391; +const int MOVE_SS_HYDRA_SLAYING_STRIKE = 17407; +const int MOVE_SH_DEATH_DARK = 17424; +const int MOVE_SH_SHADOW_BLINK = 17434; +const int MOVE_SD_ANCIENT_MOUNTAIN = 17443; +const int MOVE_SD_COLOSSUS_STRIKE = 17448; +const int MOVE_TC_HAMSTRING_ATTACK = 17477; +const int MOVE_TC_PREY_ON_THE_WEAK = 17481; +const int MOVE_TC_SWOOPING_DRAGON = 17487; +const int MOVE_WR_CLARION_CALL = 17494; +const int MOVE_WR_SWARMING_ASSAULT = 17504; + +// Level 8 Maneuvers +const int MOVE_DW_RISING_PHOENIX = 17321; +const int MOVE_DW_WYRMS_FLAME = 17326; +const int MOVE_DS_GREATER_DIVINE_SURGE = 17337; +const int MOVE_DS_IMMORTAL_FORTITUDE = 17341; +const int MOVE_DM_DIAMOND_DEFENSE = 17357; +const int MOVE_DM_DIAMOND_NIGHTMARE = 17358; +const int MOVE_DM_STANCE_ALACRITY = 17373; +const int MOVE_IH_ADAMANTINE_HURRICANE = 17377; +const int MOVE_IH_LIGHTNING_THROW = 17387; +const int MOVE_IH_SUPREME_BLADE_PARRY = 17395; +const int MOVE_SS_FOOLS_STRIKE = 17404; +const int MOVE_SS_GHOSTLY_DEFENSE = 17405; +const int MOVE_SH_BALANCE_SKY = 17418; +const int MOVE_SH_ENERVATING_SHADOW = 17426; +const int MOVE_SH_ONE_WITH_SHADOW = 17432; +const int MOVE_SD_ADAMANTINE_BONES = 17442; +const int MOVE_SD_EARTHSTRIKE_QUAKE = 17451; +const int MOVE_SD_STRENGTH_STONE = 17468; +const int MOVE_TC_GIRALLON_WINDMILL = 17476; +const int MOVE_TC_RAGING_MONGOOSE = 17484; +const int MOVE_TC_WOLF_PACK_TACTICS = 17490; +const int MOVE_WR_SWARM_TACTICS = 17503; +const int MOVE_WR_WHITE_RAVEN_HAMMER = 17509; + +// Level 9 Maneuvers +const int MOVE_DW_INFERNO_BLAST = 17317; +const int MOVE_DS_RIGHTEOUS_VITALITY = 17350; +const int MOVE_DM_TIME_STANDS_STILL = 17375; +const int MOVE_IH_PERFECT_CLARITY = 17394; +const int MOVE_SS_TORNADO_THROW = 17416; +const int MOVE_SH_FIVE_SHADOW_CREEPING = 17427; +const int MOVE_SD_MOUNTAIN_TOMBSTONE = 17458; +const int MOVE_TC_FERAL_DEATH_BLOW = 17473; +const int MOVE_WR_WAR_MASTERS_CHARGE = 17508; + +// AoE Constants +const int AOE_PER_IRON_GUARD_GLARE = 158; +const int AOE_PER_STANCE_OF_CLARITY = 159; +const int AOE_PER_BOLSTERING_VOICE = 160; +const int AOE_PER_LEADING_CHARGE = 161; +const int AOE_PER_ROOT_MOUNTAIN = 164; +const int AOE_PER_TACTICS_WOLF = 165; +const int AOE_PER_DESERT_TEMPEST = 166; +const int AOE_PER_SALAMANDER_CHARGE = 157; +const int AOE_PER_THICKET_BLADES = 251; +const int AOE_PER_AURA_TYRANNY = 252; +const int AOE_PER_SWARM_TACTICS = 253; +const int AOE_PER_MOUNTAIN_FORTRESS = 149; + +// Misc Constants +const int SPELL_AVENGING_STRIKE = -1; +const int FEAT_MARTIAL_STUDY_1 = 24112; +const int FEAT_MARTIAL_STUDY_2 = 24113; +const int FEAT_MARTIAL_STUDY_3 = 24114; +const int FEAT_MARTIAL_STANCE_1 = 24115; +const int FEAT_MARTIAL_STANCE_2 = 24116; +const int FEAT_MARTIAL_STANCE_3 = 24117; +const int FEAT_MARTIAL_STANCE_4 = 24118; +const int FEAT_MARTIAL_STANCE_5 = 24119; +const int FEAT_MARTIAL_STANCE_6 = 24120; +const int FEAT_MARTIAL_STANCE_7 = 24121; +const int FEAT_MARTIAL_STANCE_8 = 24122; +const int FEAT_MARTIAL_STANCE_9 = 24123; +const int FEAT_MARTIAL_STANCE_10 = 24124; + +// Class Specific Constants +const int MOVE_MOUNTAIN_FORTRESS = 2066; +const int MOVE_BLOODCLAW_SHIFT = 2100; + +// Jade Phoenix +const int JPM_SPELL_SELECT_CONVO = 19287; +const int JPM_SPELL_SELECT_QUICK1 = 19288; +const int JPM_SPELL_SELECT_QUICK2 = 19289; +const int JPM_SPELL_SELECT_QUICK3 = 19290; +const int JPM_SPELL_SELECT_QUICK4 = 19291; +const int JPM_SPELL_FIREBIRD_AUGMENTED = 19302; +const int JPM_SPELL_ARCANE_WRATH = 19292; +const int JPM_SPELL_RITE_WAKING_SELF = 19294; +const int FEAT_JPM_EMERALD_IMMOLATION = 24083; + +const int MOVE_MYSTIC_PHOENIX = 19297; +const int MOVE_MYSTIC_PHOENIX_AUG = 19298; +const int MOVE_FIREBIRD_STANCE = 19301; +const int MOVE_FIREBIRD_STANCE_AUG = 19302; + +// Eternal Blade +const int FEAT_ETBL_ETERNAL_TRAINING = 24127; +const int ETBL_MANEUVER_SELECT_CONVO = 19308; +const int ETBL_MANEUVER_SELECT_QUICK1 = 19309; +const int ETBL_MANEUVER_SELECT_QUICK2 = 19310; +const int ETBL_MANEUVER_SELECT_QUICK3 = 19311; +const int ETBL_MANEUVER_SELECT_QUICK4 = 19312; +const int ETBL_RACIAL_TYPE = 19315; +const int ETBL_BLADE_GUIDE = 19303; + +// Shadow Sun Ninja +const int MOVE_CHILD_SL_STANCE = 19327; +const int SSN_FLAMESS_ATTACK = 19322; +const int SSN_VOIDSS_ATTACK = 19326; +const int SSN_BALANCELD_ATTACK = 19329; diff --git a/src/include/tob_movehook.nss b/src/include/tob_movehook.nss new file mode 100644 index 0000000..3ac1493 --- /dev/null +++ b/src/include/tob_movehook.nss @@ -0,0 +1,127 @@ +//:://///////////////////////////////////////////// +//:: Tome of Battle Maneuver Hook File. +//:: tob_movehook.nss +//::////////////////////////////////////////////// +/* + This file acts as a hub for all code that + is hooked into the maneuver scripts for Tome of Battle +*/ +//::////////////////////////////////////////////// +//:: Created By: Stratovarius +//:: Created On: 19-3-2007 +//::////////////////////////////////////////////// + +#include "prc_inc_spells" +#include "inc_utility" +#include "x2_inc_spellhook" + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int PreManeuverCastCode(); + +int NullPsionicsField(object oInitiator, object oTarget) +{ + // Null Psionics Field/Anti-Magic Field + if(GetHasSpellEffect(SPELL_ANTIMAGIC_FIELD, oInitiator) + || GetHasSpellEffect(POWER_NULL_PSIONICS_FIELD, oInitiator)) + { + return FALSE; + } + return TRUE; +} + +void AttackManeuverTarget(object oInitiator, object oTarget) +{ + // Don't do anything while outside of combat + if (!GetIsInCombat(oInitiator)) return; + + // If you hit a valid enemy + if (GetIsObjectValid(oTarget) && GetIsEnemy(oTarget)) + AssignCommand(oInitiator, ActionAttack(oTarget)); + else //Otherwise find someone + { + location lTarget = GetLocation(oInitiator); + + // Use the function to get the closest creature as a target + oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oTarget)) + { + if(GetIsEnemy(oTarget)) // Only enemies + { + AssignCommand(oInitiator, ActionAttack(oTarget)); + break; + } + //Select the next target within the spell shape. + oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + } +} + +//------------------------------------------------------------------------------ +// if FALSE is returned by this function, the spell will not be cast +// the order in which the functions are called here DOES MATTER, changing it +// WILL break the crafting subsystems +//------------------------------------------------------------------------------ +int PreManeuverCastCode() +{ + object oInitiator = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + int nMoveId = PRCGetSpellId(); + int nContinue; + + DeleteLocalInt(oInitiator, "SpellConc"); + nContinue = !ExecuteScriptAndReturnInt("premovecode", oInitiator); + + //--------------------------------------------------------------------------- + // Shutdown maneuvers if the PC blocks delayed damage + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = !GetLocalInt(oInitiator, "CrusaderBreak"); + + //--------------------------------------------------------------------------- + // Run NullPsionicsField Check + //--------------------------------------------------------------------------- + if (nContinue && GetIsManeuverSupernatural(nMoveId)) + nContinue = NullPsionicsField(oInitiator, oTarget); + //--------------------------------------------------------------------------- + // Run Dark Discorporation Check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = !GetLocalInt(oInitiator, "DarkDiscorporation"); + + //--------------------------------------------------------------------------- + // Swordsage Insightful Strike, grants wisdom to damage on maneuvers + // Test and local to avoid spaghetti monster + //--------------------------------------------------------------------------- + if (GetLevelByClass(CLASS_TYPE_SWORDSAGE, oInitiator) >= 4) + { + if(GetHasInsightfulStrike(oInitiator)) SetLocalInt(oInitiator, "InsightfulStrike", TRUE); + DelayCommand(2.0, DeleteLocalInt(oInitiator, "InsightfulStrike")); + } + //--------------------------------------------------------------------------- + // Blade Meditation, +1 damage with the preferred weapons of chosen discipline + // Test and local to avoid spaghetti monster + //--------------------------------------------------------------------------- + if (BladeMeditationDamage(oInitiator, nMoveId)) + { + SetLocalInt(oInitiator, "BladeMeditationDamage", TRUE); + DelayCommand(2.0, DeleteLocalInt(oInitiator, "BladeMeditationDamage")); + } + //--------------------------------------------------------------------------- + // Instant Clarity, gain psionic focus when successfully initiating a strike + // Test and local to avoid spaghetti monster + //--------------------------------------------------------------------------- + if (GetManeuverType(nMoveId) == MANEUVER_TYPE_STRIKE && nContinue && GetHasFeat(FEAT_INSTANT_CLARITY, oInitiator)) + { + SetLocalInt(oInitiator, "InstantClaritySwitch", 2); + ExecuteScript("tob_ft_istntclty", oInitiator); + } + + float fDistance = MetersToFeet(GetDistanceBetweenLocations(GetLocation(oInitiator), GetLocation(oTarget))); + float fDelay = FeetToMeters(fDistance)/10; + DelayCommand(fDelay+0.25f, AttackManeuverTarget(oInitiator, oTarget)); + + return nContinue; +} + diff --git a/src/include/true_inc_metautr.nss b/src/include/true_inc_metautr.nss new file mode 100644 index 0000000..d35f622 --- /dev/null +++ b/src/include/true_inc_metautr.nss @@ -0,0 +1,216 @@ +//:://///////////////////////////////////////////// +//:: Truenaming include: Metautterances +//:: true_inc_metautr +//:://///////////////////////////////////////////// +/** @file + Defines functions for handling metautterances + + @author Stratovarius + @date Created - 2006.7.17 + @thanks to Ornedan for his work on Psionics upon which this is based. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +/// No metautterances +const int METAUTTERANCE_NONE = 0x0; +/// Quicken utterance +const int METAUTTERANCE_QUICKEN = 0x2; +/// Empower utterance +const int METAUTTERANCE_EMPOWER = 0x4; +/// Extend utterance +const int METAUTTERANCE_EXTEND = 0x8; + +/// Internal constant. Value is equal to the lowest metautterance constant. Used when looping over metautterance flag variables +const int METAUTTERANCE_MIN = 0x2; +/// Internal constant. Value is equal to the highest metautterance constant. Used when looping over metautterance flag variables +const int METAUTTERANCE_MAX = 0x8; + +/// Empower Utterance variable name +const string METAUTTERANCE_EMPOWER_VAR = "PRC_TrueMeta_Empower"; +/// Extend Utterance variable name +const string METAUTTERANCE_EXTEND_VAR = "PRC_TrueMeta_Extend"; +/// Quicken Utterance variable name +const string METAUTTERANCE_QUICKEN_VAR = "PRC_TrueMeta_Quicken"; + + +/// The name of a marker variable that tells that the Utterance being truespoken had Quicken Utterance used on it +const string PRC_UTTERANCE_IS_QUICKENED = "PRC_UtteranceIsQuickened"; + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +/** + * A structure that contains common data used during utterance. + */ +struct utterance{ + /* Generic stuff */ + /// The creature Truespeaking the Utterance + object oTrueSpeaker; + /// Whether the utterance is successful or not + int bCanUtter; + /// The creature's truespeaker level in regards to this utterance + int nTruespeakerLevel; + /// The utterance's spell ID + int nSpellId; + /// The DC for speaking the utterance + int nUtterDC; + // Used to mark friendly utterances + int bIgnoreSR; + + /* Metautterances */ + /// Whether Empower utterance was used with this utterance + int bEmpower; + /// Whether Extend utterance was used with this utterance + int bExtend; + /// Whether Quicken utterance was used with this utterance + int bQuicken; + + /* Speak Unto the Masses */ + // Check if the target is a friend of not + int bFriend; + // Saving Throw DC + int nSaveDC; + // Saving Throw + int nSaveThrow; + // Saving Throw Type + int nSaveType; + // Spell Pen + int nPen; + // Duration Effects + effect eLink; + // Impact Effects + effect eLink2; + // Any Item Property + itemproperty ipIProp1; + // Any Item Property + itemproperty ipIProp2; + // Any Item Property + itemproperty ipIProp3; + // Duration + float fDur; +}; + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines the metautterances used in this utterance of a utterance + * and the cost added by their use. + * + * @param utter The utterance data related to this particular utterance + * @param nMetaUtterFlags An integer containing a set of bitflags that determine + * which metautterance utterances may be used with the Utterance being truespoken + * + * @return The utterance data, modified to account for the metautterances + */ +struct utterance EvaluateMetautterances(struct utterance utter, int nMetaUtterFlags); + +/** + * Calculates a utterance's damage based on the given dice and metautterances. + * + * @param nDieSize Size of the dice to use + * @param nNumberOfDice Amount of dice to roll + * @param manif The utterance data related to this particular utterance + * @param nBonus A bonus amount of damage to add into the total once + * @param nBonusPerDie A bonus amount of damage to add into the total for each die rolled + * @param bDoesHPDamage Whether the Utterance deals hit point damage, or some other form of point damage + * @param bIsRayOrRangedTouch Whether the utterance's use involves a ranged touch attack roll or not + * @return The amount of damage the Utterance should deal + */ +int MetautterancesDamage(struct utterance utter, int nDieSize, int nNumberOfDice, int nBonus = 0, + int nBonusPerDie = 0, int bDoesHPDamage = FALSE, int bIsRayOrRangedTouch = FALSE); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_utility" +//#include "true_inc_utter" + + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct utterance EvaluateMetautterances(struct utterance utter, int nMetaUtterFlags) +{ + // Total PP cost of metautterances used + int nUtterDC = 0; + // A debug variable to make a Utterance ignore normal use constraints + int bIgnoreConstr = (DEBUG) ? GetLocalInt(utter.oTrueSpeaker, TRUE_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + + /* Calculate the added DC from metautterances and set the use markers for the utterances used */ + + // Quicken Utterance - special handling + if(GetLocalInt(utter.oTrueSpeaker, PRC_UTTERANCE_IS_QUICKENED)) + { + // Add the DC Boost and mark the utterance as quickened here + nUtterDC += 20; + utter.bQuicken = TRUE; + + // Delete the marker var + DeleteLocalInt(utter.oTrueSpeaker, PRC_UTTERANCE_IS_QUICKENED); + } + + if((nMetaUtterFlags & METAUTTERANCE_EMPOWER) && GetLocalInt(utter.oTrueSpeaker, METAUTTERANCE_EMPOWER_VAR)) + { + // Add the DC Boost and mark the utterance as empowered here + nUtterDC += 10; + utter.bEmpower = TRUE; + } + if((nMetaUtterFlags & METAUTTERANCE_EXTEND) && GetLocalInt(utter.oTrueSpeaker, METAUTTERANCE_EXTEND_VAR)) + { + // Add the DC Boost and mark the utterance as extended here + nUtterDC += 5; + utter.bExtend = TRUE; + } + + // Add in the DC boost of the metautterances + utter.nUtterDC += nUtterDC; + + return utter; +} + +int MetautterancesDamage(struct utterance utter, int nDieSize, int nNumberOfDice, int nBonus = 0, + int nBonusPerDie = 0, int bDoesHPDamage = FALSE, int bIsRayOrRangedTouch = FALSE) +{ + int nBaseDamage = 0, + nBonusDamage = nBonus + (nNumberOfDice * nBonusPerDie); + + // Calculate the base damage + int i; + for (i = 0; i < nNumberOfDice; i++) + nBaseDamage += Random(nDieSize) + 1; + + + // Apply general modifying effects + if(bDoesHPDamage) + { + if(bIsRayOrRangedTouch) + { + // Anything that affects Ray Utterances goes here + } + } + + // Apply metautterances + // Empower + if(utter.bEmpower) + nBaseDamage += nBaseDamage / 2; + + return nBaseDamage + nBonusDamage; +} + +// Test main +//void main(){} diff --git a/src/include/true_inc_truespk.nss b/src/include/true_inc_truespk.nss new file mode 100644 index 0000000..7523024 --- /dev/null +++ b/src/include/true_inc_truespk.nss @@ -0,0 +1,524 @@ +//:://///////////////////////////////////////////// +//:: Truenaming include: Truespeaking +//:: true_inc_truespk +//:://///////////////////////////////////////////// +/** @file + Defines functions for handling truespeaking. + + @author Stratovarius + @date Created - 2006.18.07 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Returns the base DC of an Utterance. + * This is where the various formulas can be chosen by switch + * Accounts for Speak Unto the Masses if used + * + * @param oTarget Target of the Utterance + * @param oTrueSpeaker Caster of the Utterance + * @param nLexicon Type of the Utterance: Evolving Mind, Crafted Tool, or Perfected Map + * @return The base DC via formula to hit the target + * This does not include MetaUtterances, increased DC to ignore SR, or the Laws. + */ +int GetBaseUtteranceDC(object oTarget, object oTrueSpeaker, int nLexicon); + +/** + * Returns the Law of Resistance DC increase + * + * @param oTrueSpeaker Caster of the Utterance + * @param nSpellId SpellId of the Utterance + * @return The DC boost for this particular power from the Law of Resistance + */ +int GetLawOfResistanceDCIncrease(object oTrueSpeaker, int nSpellId); + +/** + * Stores the Law Of Resistance DC increase + * + * @param oTrueSpeaker Caster of the Utterance + * @param nSpellId SpellId of the Utterance + */ +void DoLawOfResistanceDCIncrease(object oTrueSpeaker, int nSpellId); + +/** + * Deletes all of the Local Ints stored by the laws. + * Called OnRest and OnEnter + * + * @param oTrueSpeaker Caster of the Utterance + */ +void ClearLawLocalVars(object oTrueSpeaker); + +/** + * Returns the Personal Truename DC increase + * Right now this accounts for targeting self + * This also adds in the Acolyte of the Ego Alter Personal Truename + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Target of the Utterance + * @return The DC boost for using a personal truename + */ +int AddPersonalTruenameDC(object oTrueSpeaker, object oTarget); + +/** + * Returns the DC increase if the TrueSpeaker ignores SR. + * + * @param oTrueSpeaker Caster of the Utterance + * @return The DC boost for using this ability + */ +int AddIgnoreSpellResistDC(object oTrueSpeaker); + +/** + * Returns the DC increase from specific utterances + * + * @param oTrueSpeaker Caster of the Utterance + * @return The DC boost for using this ability + */ +int AddUtteranceSpecificDC(object oTrueSpeaker); + +/** + * Returns the DC used for the Recitation feats + * This is a simplified version of the GetBaseUtteranceDC function + * + * @param oTrueSpeaker Caster/Target of the Recitation + * @return The DC to speak + */ +int GetRecitationDC(object oTrueSpeaker); + +/* + * Returns TRUE if it is a Syllable (Bereft class ability). + * @param nSpellId Utterance to check + * + * @return TRUE or FALSE + */ +int GetIsSyllable(int nSpellId); + +/* + * Returns TRUE if the Truespeaker passes a Truespeak check to affect the target + * @param nPersonal Personal Truename DC or not + * + * @return TRUE or FALSE + */ +int DoSpellTruenameCheck(object oTrueSpeaker, object oTarget, int nPersonal = FALSE); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_inc_spells" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +int GetCraftedToolCR(object oItem) +{ + int nID=0; + if (!GetIdentified(oItem)) + { + nID=1; + SetIdentified(oItem,TRUE); + } + int nGold = GetGoldPieceValue(oItem); + // If none of the statements trigger, the base is 0 (i.e, non-magical) + int nLore = 0; + + if (nGold>1001) nLore= 1; + if (nGold>2501) nLore= 2; + if (nGold>3751) nLore= 3; + if (nGold>4801) nLore= 4; + if (nGold>6501) nLore= 5; + if (nGold>9501) nLore= 6; + if (nGold>13001) nLore= 7; + if (nGold>17001) nLore= 8; + if (nGold>20001) nLore= 9; + if (nGold>30001) nLore= 10; + if (nGold>40001) nLore= 11; + if (nGold>50001) nLore= 12; + if (nGold>60001) nLore= 13; + if (nGold>80001) nLore= 14; + if (nGold>100001) nLore= 15; + if (nGold>150001) nLore= 16; + if (nGold>200001) nLore= 17; + if (nGold>250001) nLore= 18; + if (nGold>300001) nLore= 19; + if (nGold>350001) nLore= 20; + if (nGold>400001) nLore= 21; + if (nGold>500001) + { + nGold = nGold - 500000; + nGold = nGold / 100000; + nLore = nGold + 22; + } + if (nID) SetIdentified(oItem,FALSE); + return nLore; +} + +/** + * Takes the REVERSE SpellId of an Utterance and returns the NORMAL + * This is used for the Law of Resistance and the Law of Sequence so its always stored on the one SpellId + * + * @param nSpellId SpellId of the Utterance + * @return string SpellId of the NORMAL Utterance + */ +string GetNormalUtterSpellId(int nSpellId) +{ + //All utterances have their 'normal' id in master column in spells.2da + string nReturn = Get2DACache("spells", "Master", nSpellId); + if (1 > StringToInt(nReturn)) nReturn = IntToString(nSpellId); // SpellId invalid for the Master column + + return nReturn; + +/* // Level 1 Utterances + if (nSpellId == UTTER_DEFENSIVE_EDGE_R || nSpellId == UTTER_DEFENSIVE_EDGE) return UTTER_DEFENSIVE_EDGE; + else if (nSpellId == UTTER_INERTIAL_SURGE_R || nSpellId == UTTER_INERTIAL_SURGE) return UTTER_INERTIAL_SURGE; + else if (nSpellId == UTTER_KNIGHTS_PUISSANCE_R || nSpellId == UTTER_KNIGHTS_PUISSANCE) return UTTER_KNIGHTS_PUISSANCE; + else if (nSpellId == UTTER_UNIVERSAL_APTITUDE_R || nSpellId == UTTER_UNIVERSAL_APTITUDE) return UTTER_UNIVERSAL_APTITUDE; + else if (nSpellId == UTTER_WORD_NURTURING_MINOR_R || nSpellId == UTTER_WORD_NURTURING_MINOR) return UTTER_WORD_NURTURING_MINOR; + else if (nSpellId == UTTER_FORTIFY_ARMOUR_SNEAK || nSpellId == UTTER_FORTIFY_ARMOUR_CRIT) return UTTER_FORTIFY_ARMOUR_SNEAK; + else if (nSpellId == UTTER_FOG_VOID_CLOUD || nSpellId == UTTER_FOG_VOID_SOLID) return UTTER_FOG_VOID_CLOUD; + + // Level 2 Utterances + else if (nSpellId == UTTER_ARCHERS_EYE_R || nSpellId == UTTER_ARCHERS_EYE) return UTTER_ARCHERS_EYE; + else if (nSpellId == UTTER_HIDDEN_TRUTH_R || nSpellId == UTTER_HIDDEN_TRUTH) return UTTER_HIDDEN_TRUTH; + else if (nSpellId == UTTER_PERCEIVE_UNSEEN_R || nSpellId == UTTER_PERCEIVE_UNSEEN) return UTTER_PERCEIVE_UNSEEN; + else if (nSpellId == UTTER_SILENT_CASTER_R || nSpellId == UTTER_SILENT_CASTER) return UTTER_SILENT_CASTER; + else if (nSpellId == UTTER_SPEED_ZEPHYR_R || nSpellId == UTTER_SPEED_ZEPHYR) return UTTER_SPEED_ZEPHYR; + else if (nSpellId == UTTER_STRIKE_MIGHT_R || nSpellId == UTTER_STRIKE_MIGHT) return UTTER_STRIKE_MIGHT; + else if (nSpellId == UTTER_TEMPORAL_TWIST_R || nSpellId == UTTER_TEMPORAL_TWIST) return UTTER_TEMPORAL_TWIST; + else if (nSpellId == UTTER_WORD_NURTURING_LESSER_R || nSpellId == UTTER_WORD_NURTURING_LESSER) return UTTER_WORD_NURTURING_LESSER; + else if (nSpellId == UTTER_AGITATE_ITEM_HOT || nSpellId == UTTER_AGITATE_ITEM_COLD) return UTTER_AGITATE_ITEM_HOT; + else if (nSpellId == UTTER_ENERGY_VORTEX_ACID || nSpellId == UTTER_ENERGY_VORTEX_COLD || nSpellId == UTTER_ENERGY_VORTEX_ELEC || nSpellId == UTTER_ENERGY_VORTEX_FIRE) return UTTER_ENERGY_VORTEX_ACID; + + // Level 3 Utterances + else if (nSpellId == UTTER_ACCELERATED_ATTACK_R || nSpellId == UTTER_ACCELERATED_ATTACK) return UTTER_ACCELERATED_ATTACK; + else if (nSpellId == UTTER_ENERGY_NEGATION_R || nSpellId == UTTER_ENERGY_NEGATION || nSpellId == UTTER_ENERGY_NEGATION_CHOICE) return UTTER_ENERGY_NEGATION; + else if (nSpellId == UTTER_INCARNATION_ANGELS_R || nSpellId == UTTER_INCARNATION_ANGELS) return UTTER_INCARNATION_ANGELS; + else if (nSpellId == UTTER_SPEED_ZEPHYR_GREATER_R || nSpellId == UTTER_SPEED_ZEPHYR_GREATER) return UTTER_SPEED_ZEPHYR_GREATER; + else if (nSpellId == UTTER_TEMPORAL_SPIRAL_R || nSpellId == UTTER_TEMPORAL_SPIRAL) return UTTER_TEMPORAL_SPIRAL; + else if (nSpellId == UTTER_VISION_SHARPENED_R || nSpellId == UTTER_VISION_SHARPENED) return UTTER_VISION_SHARPENED; + else if (nSpellId == UTTER_WORD_NURTURING_MODERATE_R || nSpellId == UTTER_WORD_NURTURING_MODERATE) return UTTER_WORD_NURTURING_MODERATE; + + // Level 4 Utterances + else if (nSpellId == UTTER_BREATH_CLEANSING_R || nSpellId == UTTER_BREATH_CLEANSING) return UTTER_BREATH_CLEANSING; + else if (nSpellId == UTTER_CASTER_LENS_R || nSpellId == UTTER_CASTER_LENS) return UTTER_CASTER_LENS; + else if (nSpellId == UTTER_CONFOUNDING_RESISTANCE_R || nSpellId == UTTER_CONFOUNDING_RESISTANCE) return UTTER_CONFOUNDING_RESISTANCE; + else if (nSpellId == UTTER_MORALE_BOOST_R || nSpellId == UTTER_MORALE_BOOST) return UTTER_MORALE_BOOST; + else if (nSpellId == UTTER_MAGICAL_CONTRACTION_R || nSpellId == UTTER_MAGICAL_CONTRACTION) return UTTER_MAGICAL_CONTRACTION; + else if (nSpellId == UTTER_SPELL_REBIRTH_R || nSpellId == UTTER_SPELL_REBIRTH) return UTTER_SPELL_REBIRTH; + else if (nSpellId == UTTER_WORD_NURTURING_POTENT_R || nSpellId == UTTER_WORD_NURTURING_POTENT) return UTTER_WORD_NURTURING_POTENT; + + // Level 5 Utterances + else if (nSpellId == UTTER_ELDRITCH_ATTRACTION_R || nSpellId == UTTER_ELDRITCH_ATTRACTION) return UTTER_ELDRITCH_ATTRACTION; + else if (nSpellId == UTTER_ENERGY_NEGATION_GREATER_R || nSpellId == UTTER_ENERGY_NEGATION_GREATER || nSpellId == UTTER_ENERGY_NEGATION_GREATER_CHOICE) return UTTER_ENERGY_NEGATION_GREATER; + else if (nSpellId == UTTER_ESSENCE_LIFESPARK_R || nSpellId == UTTER_ESSENCE_LIFESPARK) return UTTER_ESSENCE_LIFESPARK; + else if (nSpellId == UTTER_PRETERNATURAL_CLARITY_ATTACK || nSpellId == UTTER_PRETERNATURAL_CLARITY_SKILL || nSpellId == UTTER_PRETERNATURAL_CLARITY_SAVE || nSpellId == UTTER_PRETERNATURAL_CLARITY_R) return UTTER_PRETERNATURAL_CLARITY_ATTACK; + else if (nSpellId == UTTER_SENSORY_FOCUS_R || nSpellId == UTTER_SENSORY_FOCUS) return UTTER_SENSORY_FOCUS; + else if (nSpellId == UTTER_WARD_PEACE_R || nSpellId == UTTER_WARD_PEACE) return UTTER_WARD_PEACE; + else if (nSpellId == UTTER_WORD_NURTURING_CRITICAL_R || nSpellId == UTTER_WORD_NURTURING_CRITICAL) return UTTER_WORD_NURTURING_CRITICAL; + else if (nSpellId == UTTER_METAMAGIC_CATALYST_EMP || nSpellId == UTTER_METAMAGIC_CATALYST_EXT || nSpellId == UTTER_METAMAGIC_CATALYST_MAX) return UTTER_METAMAGIC_CATALYST_EMP; + + // Level 6 Utterances + else if (nSpellId == UTTER_BREATH_RECOVERY_R || nSpellId == UTTER_BREATH_RECOVERY) return UTTER_BREATH_RECOVERY; + else if (nSpellId == UTTER_ETHER_REFORGED_R || nSpellId == UTTER_ETHER_REFORGED) return UTTER_ETHER_REFORGED; + else if (nSpellId == UTTER_KNIGHTS_PUISSANCE_GREATER_R || nSpellId == UTTER_KNIGHTS_PUISSANCE_GREATER) return UTTER_KNIGHTS_PUISSANCE_GREATER; + else if (nSpellId == UTTER_MYSTIC_RAMPART_R || nSpellId == UTTER_MYSTIC_RAMPART) return UTTER_MYSTIC_RAMPART; + else if (nSpellId == UTTER_SINGULAR_MIND_R || nSpellId == UTTER_SINGULAR_MIND) return UTTER_SINGULAR_MIND; + else if (nSpellId == UTTER_WORD_NURTURING_GREATER_R || nSpellId == UTTER_WORD_NURTURING_GREATER) return UTTER_WORD_NURTURING_GREATER; + + // Class abilities + else if (nSpellId == SYLLABLE_AFFLICATION_SIGHT || nSpellId == SYLLABLE_AFFLICATION_SOUND || nSpellId == SYLLABLE_AFFLICATION_TOUCH) return SYLLABLE_AFFLICATION_SIGHT; + else if (nSpellId == BRIMSTONE_FIRE_3D6 || nSpellId == BRIMSTONE_FIRE_5D6 || nSpellId == BRIMSTONE_FIRE_8D6) return BRIMSTONE_FIRE_3D6; + else if (nSpellId == BRIMSTONE_HEAVEN_LESSER || nSpellId == BRIMSTONE_HEAVEN_NORMAL || nSpellId == BRIMSTONE_HEAVEN_GREATER) return BRIMSTONE_HEAVEN_LESSER; + + // This is the return for those with no reverse. + return nSpellId;*/ +} + +int GetSwitchAdjustedDC(int nCR, int nTargets, object oTrueSpeaker) +{ + int nClass = GetLevelByClass(CLASS_TYPE_TRUENAMER, oTrueSpeaker); + int nDC = 15 + (2 * nCR) + (2 * nTargets); + // Default is 0, off + int nMulti = GetPRCSwitch(PRC_TRUENAME_CR_MULTIPLIER); + int nBonus = GetPRCSwitch(PRC_TRUENAME_LEVEL_BONUS); + int nConst = GetPRCSwitch(PRC_TRUENAME_DC_CONSTANT); + // nMulti is stored as an int, divide by 100 to get the float + if(nMulti) nDC = FloatToInt(15 + ((IntToFloat(nMulti)/100) * nCR) + (2 * nTargets)); + if(nBonus) nDC -= nClass/nBonus; + // Remove the existing constant and add the new one + if(nConst) nDC = (nDC - 15) + nConst; + + return nDC; +} + +int GetFeatAdjustedDC(object oTrueSpeaker) +{ + int nDC = 0; + // Check for both, not either or + if(GetHasFeat(FEAT_SKILL_FOCUS_TRUESPEAK, oTrueSpeaker)) nDC += 3; + if(GetHasFeat(FEAT_EPIC_SKILL_FOCUS_TRUESPEAK, oTrueSpeaker)) nDC += 10; + if(GetHasFeat(FEAT_TEMPLATE_SAINT_HOLY_POWER, oTrueSpeaker)) + { + if (GetAlignmentGoodEvil(oTrueSpeaker) == ALIGNMENT_GOOD) + { + nDC += 2; + } + else + { + nDC += 0; + } + } + return nDC; +} + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetBaseUtteranceDC(object oTarget, object oTrueSpeaker, int nLexicon) +{ + int nDC; + + // We're targetting a creature + if (nLexicon == LEXICON_EVOLVING_MIND) + { + // Check for Speak Unto the Masses. Syllables use the Evolving Mind formula, but can't Speak Unto Masses + if (GetLocalInt(oTrueSpeaker, TRUE_SPEAK_UNTO_MASSES) && !GetIsSyllable(PRCGetSpellId())) + { + if(DEBUG) DoDebug("GetBaseUtteranceDC: Entered"); + // Speak to the Masses affects all creatures of the same race in the AoE + // Grants a +2 DC for each of them + int nRacial = MyPRCGetRacialType(oTarget); + // The creature with the same race as the target and the highest CR is used as the base + // So we loop through and count all the targets, as well as figure out the highest CR + int nMaxCR = FloatToInt(GetChallengeRating(oTarget)); + int nCurCR, nTargets; + if(DEBUG) DoDebug("GetBaseUtteranceDC: Variables"); + + // Loop over targets + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget)) + { + if(DEBUG) DoDebug("GetBaseUtteranceDC: While"); + // Skip the original target, it doesn't count as a target + if (oAreaTarget != oTarget) + { + if(DEBUG) DoDebug("GetBaseUtteranceDC: Continue"); + + // Targeting limitations + if(MyPRCGetRacialType(oAreaTarget) == nRacial) + { + if(DEBUG) DoDebug("GetBaseUtteranceDC: race check"); + // CR Check + nCurCR = FloatToInt(GetChallengeRating(oAreaTarget)); + // Update if you find something bigger + if (nCurCR > nMaxCR) nMaxCR = nCurCR; + // Increment Targets + nTargets++; + }// end if - Targeting check + } + + // Get next target + if(DEBUG) DoDebug("GetBaseUtteranceDC: Next Target"); + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE); + }// end while - Target loop + + // Runs the function just above this that adjusts based on switches + nDC = GetSwitchAdjustedDC(nMaxCR, nTargets, oTrueSpeaker); + } // end if - Speak unto the Masses check + else // Single Target Utterance. The normal result + { + // CR does not take into account floats, so this is converted. + int nCR = FloatToInt(GetChallengeRating(oTarget)); + // For PCs, use their HitDice + if (GetIsPC(oTarget)) nCR = GetHitDice(oTarget); + // Runs the function just above this that adjusts based on switches + nDC = GetSwitchAdjustedDC(nCR, 0, oTrueSpeaker); + } + } + // Targetting an Item here + else if (nLexicon == LEXICON_CRAFTED_TOOL) + { + // The formula isn't finished, because there isn't caster level on NWN items. + int nCasterLvl = GetCraftedToolCR(oTarget); + nDC = 15 + (2 * nCasterLvl); + } + // Targetting the land + else if (nLexicon == LEXICON_PERFECTED_MAP) + { + // Default is 0, off + int nMulti = GetPRCSwitch(PRC_PERFECTED_MAP_MULTIPLIER); + int nConst = GetPRCSwitch(PRC_PERFECTED_MAP_CONSTANT); + + // Using Errata formula to prevent abuses + nDC = 25 + (GetUtteranceLevel(oTrueSpeaker) * 2); + + // nMulti is stored as an int + if(nMulti) nDC = 25 + (GetUtteranceLevel(oTrueSpeaker) * nMulti); + // Remove the existing constant and add the new one + if(nConst) nDC = (nDC - 25) + nConst; + } + // Check to see if the PC has either of the Skill Focus feats. + // If so, subtract (They are a bonus to the PC) from the DC roll + nDC -= GetFeatAdjustedDC(oTrueSpeaker); + + return nDC; +} + +int GetLawOfResistanceDCIncrease(object oTrueSpeaker, int nSpellId) +{ + // This makes sure everything is stored using the Normal, and not the reverse + // Law of resistance is stored for each utterance by SpellId + int nDC = GetLocalInt(oTrueSpeaker, LAW_OF_RESIST_VARNAME + GetNormalUtterSpellId(nSpellId)); + // Its stored by the number of succesful attempts, so we double it to get the DC boost + nDC = nDC * 2; + + if (GetPRCSwitch(PRC_LAW_OF_RESISTANCE)) nDC = 0; + return nDC; +} + +void DoLawOfResistanceDCIncrease(object oTrueSpeaker, int nSpellId) +{ + // This makes sure everything is stored using the Normal, and not the reverse + string sSpellId = GetNormalUtterSpellId(nSpellId); + // Law of resistance is stored for each utterance by SpellId + int nNum = GetLocalInt(oTrueSpeaker, LAW_OF_RESIST_VARNAME + sSpellId); + // Store the number of times per day its been cast succesfully + SetLocalInt(oTrueSpeaker, LAW_OF_RESIST_VARNAME + sSpellId, (nNum + 1)); +} + +void ClearLawLocalVars(object oTrueSpeaker) +{ + // As long as the PC isn't a truenamer, don't run this. + if (GetLevelByClass(CLASS_TYPE_TRUENAMER, oTrueSpeaker)) + { + // Law of resistance is stored for each utterance by SpellId + // So we loop em all and blow em away + // Because there are only about 60, this should not TMI + // i is the SpellId + // Replace numbers when done + int i; + for(i = 3526; i < 3639; i++) + { + DeleteLocalInt(oTrueSpeaker, LAW_OF_RESIST_VARNAME + IntToString(i)); + DeleteLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + IntToString(i)); + } + } + // As long as the PC isn't a brimstone speaker, don't run this. + if (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oTrueSpeaker)) + { + // Law of resistance is stored for class features by SpellId + // So we loop em all and blow em away + // i is the SpellId + // Replace numbers when done + int i; + for(i = 3426; i < 3431; i++) + { + DeleteLocalInt(oTrueSpeaker, LAW_OF_RESIST_VARNAME + IntToString(i)); + DeleteLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + IntToString(i)); + } + } + // As long as the PC isn't a bereft, don't run this. + if (GetLevelByClass(CLASS_TYPE_BEREFT, oTrueSpeaker)) + { + // Law of resistance is stored for each utterance by SpellId + // So we loop em all and blow em away + // Because there are only about 60, this should not TMI + // i is the SpellId + // Replace numbers when done + int i; + for(i = 3418; i < 3425; i++) + { + DeleteLocalInt(oTrueSpeaker, LAW_OF_RESIST_VARNAME + IntToString(i)); + DeleteLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + IntToString(i)); + } + } +} + +int AddPersonalTruenameDC(object oTrueSpeaker, object oTarget) +{ + // Targetting yourself increases the DC by 2 + // But you get a +4 Bonus to speak your own truename + // Total Adjustment: -2 + int nDC = oTrueSpeaker == oTarget ? -2 : 0; + + ///Increase the DC by 4 + if (GetLevelByClass(CLASS_TYPE_ACOLYTE_EGO, oTrueSpeaker) > 6 && oTrueSpeaker != oTarget) nDC += 4; + ///Increase the DC by another 4 + if (GetLevelByClass(CLASS_TYPE_ACOLYTE_EGO, oTrueSpeaker) > 8 && oTrueSpeaker != oTarget) nDC += 4; + + return nDC; +} + +int AddIgnoreSpellResistDC(object oTrueSpeaker) +{ + int nDC = GetLocalInt(oTrueSpeaker, TRUE_IGNORE_SR) ? 5 : 0; + + return nDC; +} + +int AddUtteranceSpecificDC(object oTrueSpeaker) +{ + int nDC = 0; + int nSpellID = PRCGetSpellId(); + // When using this utterance you add +10 to the DC to make yourself immune to crits + if(nSpellID == UTTER_FORTIFY_ARMOUR_CRIT) nDC += 10; + // When using this utterance you add +10 to the DC to maximize a potion or scroll + if(nSpellID == UTTER_METAMAGIC_CATALYST_MAX) nDC += 10; + // When using this utterance you add +10 to the DC to create a solid fog spell + if(nSpellID == UTTER_FOG_VOID_SOLID) nDC += 10; + + return nDC; +} + +int GetRecitationDC(object oTrueSpeaker) +{ + int nCR = GetHitDice(oTrueSpeaker); + // Formula for the DC. The -2 is from speak your own Truename. See AddPersonalTruenameDC + int nDC = 15 + (2 * nCR) - 2; + + return nDC; +} + +int GetIsSyllable(int nSpellId) +{ + switch(nSpellId) + { + case SYLLABLE_DETACHMENT: + case SYLLABLE_AFFLICATION_SIGHT: + case SYLLABLE_AFFLICATION_SOUND: + case SYLLABLE_AFFLICATION_TOUCH: + case SYLLABLE_EXILE: + case SYLLABLE_DISSOLUTION: + case SYLLABLE_ENERVATION: + return TRUE; + } + + return FALSE; +} + +int DoSpellTruenameCheck(object oTrueSpeaker, object oTarget, int nPersonal = FALSE) +{ + // CR does not take into account floats, so this is converted. + int nCR = FloatToInt(GetChallengeRating(oTarget)); + // For PCs, use their HitDice + if (GetIsPC(oTarget)) nCR = GetHitDice(oTarget); + // Runs the function just above this that adjusts based on switches + int nDC = GetSwitchAdjustedDC(nCR, 0, oTrueSpeaker); + // DC change for targeting self and using a Personal Truename + if (nPersonal) + nDC += AddPersonalTruenameDC(oTrueSpeaker, oTarget); + + if(GetIsSkillSuccessful(oTrueSpeaker, SKILL_TRUESPEAK, nDC)) return TRUE; + + return FALSE; +} diff --git a/src/include/true_inc_trufunc.nss b/src/include/true_inc_trufunc.nss new file mode 100644 index 0000000..355783a --- /dev/null +++ b/src/include/true_inc_trufunc.nss @@ -0,0 +1,839 @@ +//:://///////////////////////////////////////////// +//:: Truenaming include: Misceallenous +//:: true_inc_trufunc +//:://///////////////////////////////////////////// +/** @file + Defines various functions and other stuff that + do something related to the truenaming implementation. + + Also acts as inclusion nexus for the general + truenaming includes. In other words, don't include + them directly in your scripts, instead include this. + + @author Stratovarius + @date Created - 2006.7.18 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +//:: Updated for .35 by Jaysyn 2023/03/11 + +//:: Test Void +//void main (){} + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines from what class's Utterance list the currently being truespoken + * Utterance is truespoken from. + * + * @param oTrueSpeaker A creature uttering a Utterance at this moment + * @return CLASS_TYPE_* constant of the class + */ +int GetTruespeakingClass(object oTrueSpeaker = OBJECT_SELF); + +/** + * Determines the given creature's truespeaker level. If a class is specified, + * then returns the truespeaker level for that class. Otherwise, returns + * the truespeaker level for the currently active utterance. + * + * @param oTrueSpeaker The creature whose truespeaker level to determine + * @param nSpecificClass The class to determine the creature's truespeaker + * level in. + * @param nUseHD If this is set, it returns the Character Level of the calling creature. + * DEFAULT: CLASS_TYPE_INVALID, which means the creature's + * truespeaker level in regards to an ongoing utterance + * is determined instead. + * @return The truespeaker level + */ +int GetTruespeakerLevel(object oTrueSpeaker, int nSpecificClass = CLASS_TYPE_INVALID, int nUseHD = FALSE); + +/** + * Determines whether a given creature uses truenaming. + * Requires either levels in a truenaming-related class or + * natural truenaming ability based on race. + * + * @param oCreature Creature to test + * @return TRUE if the creature can use truenames, FALSE otherwise. + */ +int GetIsTruenamingUser(object oCreature); + +/** + * Determines the given creature's highest undmodified truespeaker level among it's + * uttering classes. + * + * @param oCreature Creature whose highest truespeaker level to determine + * @return The highest unmodified truespeaker level the creature can have + */ +int GetHighestTrueSpeakerLevel(object oCreature); + +/** + * Determines whether a given class is a truenaming-related class or not. + * + * @param nClass CLASS_TYPE_* of the class to test + * @return TRUE if the class is a truenaming-related class, FALSE otherwise + */ +int GetIsTruenamingClass(int nClass); + +/** + * Gets the level of the Utterance being currently truespoken. + * WARNING: Return value is not defined when a Utterance is not being truespoken. + * + * @param oTrueSpeaker The creature currently uttering a utterance + * @return The level of the Utterance being truespoken + */ +int GetUtteranceLevel(object oTrueSpeaker); + +/** + * Determines a creature's ability score in the uttering ability of a given + * class. + * + * @param oTrueSpeaker Creature whose ability score to get + * @param nClass CLASS_TYPE_* constant of a uttering class + */ +int GetTruenameAbilityScoreOfClass(object oTrueSpeaker, int nClass); + +/** + * Determines the uttering ability of a class. + * + * @param nClass CLASS_TYPE_* constant of the class to determine the uttering stat of + * @return ABILITY_* of the uttering stat. ABILITY_CHARISMA for non-TrueSpeaker + * classes. + */ +int GetTruenameAbilityOfClass(int nClass); + +/** + * Calculates the DC of the Utterance being currently truespoken. + * Base value is 10 + Utterance level + ability modifier in uttering stat + * + * WARNING: Return value is not defined when a Utterance is not being truespoken. + * + */ +int GetTrueSpeakerDC(object oTrueSpeaker = OBJECT_SELF); + +/** + * Determines the truespeaker's level in regards to truespeaker checks to overcome + * spell resistance. + * + * WARNING: Return value is not defined when a Utterance is not being truespoken. + * + * @param oTrueSpeaker A creature uttering a Utterance at the moment + * @return The creature's truespeaker level, adjusted to account for + * modifiers that affect spell resistance checks. + */ +int GetTrueSpeakPenetration(object oTrueSpeaker = OBJECT_SELF); + +/** + * Marks an utterance as active for the Law of Sequence. + * Called from the Utterance + * + * @param oTrueSpeaker Caster of the Utterance + * @param nSpellId SpellId of the Utterance + * @param fDur Duration of the Utterance + */ +void DoLawOfSequence(object oTrueSpeaker, int nSpellId, float fDur); + +/** + * Checks to see whether the law of sequence is active + * Utterance fails if it is. + * + * @param oTrueSpeaker Caster of the Utterance + * @param nSpellId SpellId of the Utterance + * + * @return True if the Utterance is active, False if it is not. + */ +int CheckLawOfSequence(object oTrueSpeaker, int nSpellId); + +/** + * Returns the name of the Utterance + * + * @param nSpellId SpellId of the Utterance + */ +string GetUtteranceName(int nSpellId); + +/** + * Returns the name of the Lexicon + * + * @param nLexicon LEXICON_* to name + */ +string GetLexiconName(int nLexicon); + +/** + * Returns the Lexicon the Utterance is in + * @param nSpellId Utterance to check + * + * @return LEXICON_* + */ +int GetLexiconByUtterance(int nSpellId); + +/** + * Affects all of the creatures with Speak Unto the Masses + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Original Target of Utterance + * @param utter The utterance structure returned by EvaluateUtterance + */ +void DoSpeakUntoTheMasses(object oTrueSpeaker, object oTarget, struct utterance utter); + +/** + * Affects all of the creatures with Speak Unto the Masses + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Original Target of Utterance + * @param utter The utterance structure returned by EvaluateUtterance + */ +void DoWordOfNurturingReverse(object oTrueSpeaker, object oTarget, struct utterance utter); + +/** + * Affects all of the creatures with Speak Unto the Masses + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Original Target of Utterance + * @param utter The utterance structure returned by EvaluateUtterance + * @param nBeats Number of rounds to fire this utterance + * @param nDamageType DAMAGE_TYPE_* + */ +void DoEnergyNegation(object oTrueSpeaker, object oTarget, struct utterance utter, int nBeats, int nDamageType); + +/** + * Checks to see if the chosen target of the Crafted Tool utterance is valid. + * If it is not valid, it will search through all slots, starting with right hand weapon + * to try and find a valid target. + * + * @param oTrueSpeaker Caster of the Utterance + * @param oTarget Target of the utterance + * + * @return Item in slot, or, if there are no valid objects on the creature, OBJECT_INVALID. + * If the target is an item, it returns the item. + */ +object CraftedToolTarget(object oTrueSpeaker, object oTarget); + +/** + * Enforces the cross class cap on the Truespeech skill + * + * @param oTrueSpeaker The PC whose feats to check. + * @return TRUE if needed to relevel, FALSE otherwise. + */ +int CheckTrueSpeechSkill(object oTrueSpeaker); + +/** + * Applies modifications to a utterance's damage that depend on some property + * of the target. + * Currently accounts for: + * - Mental Resistance + * - Greater Utterance Specialization + * - Intellect Fortress + * + * @param oTarget A creature being dealt damage by a utterance + * @param oTrueSpeaker The creature uttering the damaging utterance + * @param nDamage The amount of damage the creature would be dealt + * + * @param bIsHitPointDamage Is the damage HP damage or something else? + * @param bIsEnergyDamage Is the damage caused by energy or something else? Only relevant if the damage is HP damage. + * + * @return The amount of damage, modified by oTarget's abilities + */ +/*int GetTargetSpecificChangesToDamage(object oTarget, object oTrueSpeaker, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE); + +*/ + +/** + * Returns how many Cadence feats an Acolyte of the Ego has + * + * @param oTrueSpeaker The PC whose feats to check. + * @return The count of feats + */ +int GetCadenceCount(object oTrueSpeaker); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "prc_alterations" +#include "true_inc_utter" +#include "true_inc_truknwn" + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int GetTruespeakingClass(object oTrueSpeaker = OBJECT_SELF) +{ + return GetLocalInt(oTrueSpeaker, PRC_TRUESPEAKING_CLASS) - 1; +} + +int GetTruespeakerLevel(object oTrueSpeaker, int nSpecificClass = CLASS_TYPE_INVALID, int nUseHD = FALSE) +{ + int nLevel; + int nAdjust = GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_ADJUSTMENT); + // Bereft's speak syllables and use their character level. + if (GetIsSyllable(PRCGetSpellId())) nUseHD = TRUE; + + // If this is set, return the user's HD + if (nUseHD) return GetHitDice(oTrueSpeaker); + + // The function user needs to know the character's truespeaker level in a specific class + // instead of whatever the character last truespoken a Utterance as + if(nSpecificClass != CLASS_TYPE_INVALID) + { + if(GetIsTruenamingClass(nSpecificClass)) + { + int nClassLevel = GetLevelByClass(nSpecificClass, oTrueSpeaker); + if (nClassLevel > 0) + { + nLevel = nClassLevel; + } + } + // A character's truespeaker level gained from non-uttering classes is always a nice, round zero + else + return 0; + } + + // Item Spells + if(GetItemPossessor(GetSpellCastItem()) == oTrueSpeaker) + { + if(DEBUG) SendMessageToPC(oTrueSpeaker, "Item casting at level " + IntToString(GetCasterLevel(oTrueSpeaker))); + + return GetCasterLevel(oTrueSpeaker) + nAdjust; + } + + // For when you want to assign the caster level. + else if(GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE) != 0) + { + if(DEBUG) SendMessageToPC(oTrueSpeaker, "Forced-level uttering at level " + IntToString(GetCasterLevel(oTrueSpeaker))); + + DelayCommand(1.0, DeleteLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE)); + nLevel = GetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE); + } + else if(GetTruespeakingClass(oTrueSpeaker) != CLASS_TYPE_INVALID) + { + //Gets the level of the uttering class + int nUtteringClass = GetTruespeakingClass(oTrueSpeaker); + nLevel = GetLevelByClass(nUtteringClass, oTrueSpeaker); + } + + // If everything else fails, use the character's first class position + if(nLevel == 0) + { + if(DEBUG) DoDebug("Failed to get truespeaker level for creature " + DebugObject2Str(oTrueSpeaker) + ", using first class slot"); + else WriteTimestampedLogEntry("Failed to get truespeaker level for creature " + DebugObject2Str(oTrueSpeaker) + ", using first class slot"); + + nLevel = GetLevelByPosition(1, oTrueSpeaker); + } + + nLevel += nAdjust; + + // This spam is technically no longer necessary once the truespeaker level getting mechanism has been confirmed to work +// if(DEBUG) FloatingTextStringOnCreature("TrueSpeaker Level: " + IntToString(nLevel), oTrueSpeaker, FALSE); + + return nLevel; +} + +int GetIsTruenamingUser(object oCreature) +{ + return !!(GetLevelByClass(CLASS_TYPE_TRUENAMER, oCreature) || + GetLevelByClass(CLASS_TYPE_BEREFT, oCreature) + ); +} + +int GetHighestTrueSpeakerLevel(object oCreature) +{ + int n = 0; + int nHighest; + int nTemp; + + while(n <= 8) + { + if(GetClassByPosition(n, oCreature) != CLASS_TYPE_INVALID) + { + nTemp = GetTruespeakerLevel(oCreature, GetClassByPosition(n, oCreature)); + + if(nTemp > nHighest) + nHighest = nTemp; + } + n++; + + } + + return nHighest; +} + +/* int GetHighestTrueSpeakerLevel(object oCreature) +{ + return PRCMax(PRCMax(GetClassByPosition(1, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(1, oCreature)) : 0, + GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(2, oCreature)) : 0 + ), + GetClassByPosition(3, oCreature) != CLASS_TYPE_INVALID ? GetTruespeakerLevel(oCreature, GetClassByPosition(3, oCreature)) : 0 + ); +} */ + +int GetIsTruenamingClass(int nClass) +{ + return (nClass == CLASS_TYPE_TRUENAMER || + nClass == CLASS_TYPE_BEREFT + ); +} + +int GetUtteranceLevel(object oTrueSpeaker) +{ + return GetLocalInt(oTrueSpeaker, PRC_UTTERANCE_LEVEL); +} + +int GetTruenameAbilityScoreOfClass(object oTrueSpeaker, int nClass) +{ + return GetAbilityScore(oTrueSpeaker, GetTruenameAbilityOfClass(nClass)); +} + +int GetTruenameAbilityOfClass(int nClass){ + switch(nClass) + { + case CLASS_TYPE_TRUENAMER: + return ABILITY_CHARISMA; + default: + return ABILITY_CHARISMA; + } + + // Technically, never gets here but the compiler does not realise that + return -1; +} + +int GetTrueSpeakerDC(object oTrueSpeaker = OBJECT_SELF) +{ + // Things we need for DC Checks + int nSpellId = PRCGetSpellId(); + object oTarget = PRCGetSpellTargetObject(); + int nRace = MyPRCGetRacialType(oTarget); + // DC is 10 + 1/2 Truenamer level + Ability (Charisma) + int nClass = GetTruespeakingClass(oTrueSpeaker); + int nDC = 10; + nDC += GetLevelByClass(nClass, oTrueSpeaker) / 2; + nDC += GetAbilityModifier(GetTruenameAbilityOfClass(nClass), oTrueSpeaker); + int nFeat = -1; + + // Focused Lexicon. Bonus vs chosen racial type //:: [PRC .35] Needs update for new racialtypes + switch(nRace) + { + case RACIAL_TYPE_ABERRATION: nFeat = FEAT_FOCUSED_LEXICON_ABERRATION; break; + case RACIAL_TYPE_ANIMAL: nFeat = FEAT_FOCUSED_LEXICON_ANIMAL; break; + case RACIAL_TYPE_BEAST: nFeat = FEAT_FOCUSED_LEXICON_BEAST; break; + case RACIAL_TYPE_CONSTRUCT: nFeat = FEAT_FOCUSED_LEXICON_CONSTRUCT; break; + case RACIAL_TYPE_DRAGON: nFeat = FEAT_FOCUSED_LEXICON_DRAGON; break; + case RACIAL_TYPE_DWARF: nFeat = FEAT_FOCUSED_LEXICON_DWARF; break; + case RACIAL_TYPE_ELEMENTAL: nFeat = FEAT_FOCUSED_LEXICON_ELEMENTAL; break; + case RACIAL_TYPE_ELF: nFeat = FEAT_FOCUSED_LEXICON_ELF; break; + case RACIAL_TYPE_FEY: nFeat = FEAT_FOCUSED_LEXICON_FEY; break; + case RACIAL_TYPE_GIANT: nFeat = FEAT_FOCUSED_LEXICON_GIANT; break; + case RACIAL_TYPE_GNOME: nFeat = FEAT_FOCUSED_LEXICON_GNOME; break; + case RACIAL_TYPE_HALFELF: nFeat = FEAT_FOCUSED_LEXICON_HALFELF; break; + case RACIAL_TYPE_HALFLING: nFeat = FEAT_FOCUSED_LEXICON_HALFLING; break; + case RACIAL_TYPE_HALFORC: nFeat = FEAT_FOCUSED_LEXICON_HALFORC; break; + case RACIAL_TYPE_HUMAN: nFeat = FEAT_FOCUSED_LEXICON_HUMAN; break; + case RACIAL_TYPE_HUMANOID_GOBLINOID: nFeat = FEAT_FOCUSED_LEXICON_GOBLINOID; break; + case RACIAL_TYPE_HUMANOID_MONSTROUS: nFeat = FEAT_FOCUSED_LEXICON_MONSTROUS; break; + case RACIAL_TYPE_HUMANOID_ORC: nFeat = FEAT_FOCUSED_LEXICON_ORC; break; + case RACIAL_TYPE_HUMANOID_REPTILIAN: nFeat = FEAT_FOCUSED_LEXICON_REPTILIAN; break; + case RACIAL_TYPE_MAGICAL_BEAST: nFeat = FEAT_FOCUSED_LEXICON_MAGICALBEAST; break; + case RACIAL_TYPE_OOZE: nFeat = FEAT_FOCUSED_LEXICON_OOZE; break; + case RACIAL_TYPE_OUTSIDER: nFeat = FEAT_FOCUSED_LEXICON_OUTSIDER; break; + case RACIAL_TYPE_SHAPECHANGER: nFeat = FEAT_FOCUSED_LEXICON_SHAPECHANGER; break; + case RACIAL_TYPE_UNDEAD: nFeat = FEAT_FOCUSED_LEXICON_UNDEAD; break; + case RACIAL_TYPE_VERMIN: nFeat = FEAT_FOCUSED_LEXICON_VERMIN; break; + } + if(nFeat > -1 && GetHasFeat(nFeat, oTrueSpeaker)) + { + nDC += 1; + nFeat = -1; + } + + // Utterance Focus. DC Bonus for a chosen utterance + switch(nSpellId) + { + case UTTER_BREATH_CLEANSING_R: nFeat = FEAT_UTTERANCE_FOCUS_BREATH_CLEANSING; break; + case UTTER_BREATH_RECOVERY_R: nFeat = FEAT_UTTERANCE_FOCUS_BREATH_RECOVERY; break; + case UTTER_ELDRITCH_ATTRACTION: nFeat = FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION; break; + case UTTER_ELDRITCH_ATTRACTION_R: nFeat = FEAT_UTTERANCE_FOCUS_ELDRITCH_ATTRACTION; break; + case UTTER_MORALE_BOOST_R: nFeat = FEAT_UTTERANCE_FOCUS_MORALE_BOOST; break; + case UTTER_PRETERNATURAL_CLARITY_R: nFeat = FEAT_UTTERANCE_FOCUS_PRETERNATURAL_CLARITY; break; + case UTTER_SENSORY_FOCUS_R: nFeat = FEAT_UTTERANCE_FOCUS_SENSORY_FOCUS; break; + case UTTER_SILENT_CASTER_R: nFeat = FEAT_UTTERANCE_FOCUS_SILENT_CASTER; break; + case UTTER_SINGULAR_MIND_R: nFeat = FEAT_UTTERANCE_FOCUS_SINGULAR_MIND; break; + case UTTER_TEMPORAL_SPIRAL_R: nFeat = FEAT_UTTERANCE_FOCUS_TEMPORAL_SPIRAL; break; + case UTTER_TEMPORAL_TWIST_R: nFeat = FEAT_UTTERANCE_FOCUS_TEMPORAL_TWIST; break; + case UTTER_WARD_PEACE_R: nFeat = FEAT_UTTERANCE_FOCUS_WARD_PEACE; break; + case UTTER_SHOCKWAVE: nFeat = FEAT_UTTERANCE_FOCUS_SHOCKWAVE; break; + } + if(nFeat > -1 && GetHasFeat(nFeat, oTrueSpeaker)) + nDC += 1; + + return nDC; +} + +int GetTrueSpeakPenetration(object oTrueSpeaker = OBJECT_SELF) +{ + int nPen = GetTruespeakerLevel(oTrueSpeaker); + + // According to Page 232 of Tome of Magic, Spell Pen as a feat counts, so here it is. + if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oTrueSpeaker)) nPen += 6; + else if(GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oTrueSpeaker)) nPen += 4; + else if(GetHasFeat(FEAT_SPELL_PENETRATION, oTrueSpeaker)) nPen += 2; + + // Blow away SR totally, just add 9000 + // Does not work on Syllables, only utterances + if (GetLocalInt(oTrueSpeaker, TRUE_IGNORE_SR) && !GetIsSyllable(PRCGetSpellId())) nPen += 9000; + + if(DEBUG) DoDebug("GetTrueSpeakPenetration(" + GetName(oTrueSpeaker) + "): " + IntToString(nPen)); + + return nPen; +} + +void DoLawOfSequence(object oTrueSpeaker, int nSpellId, float fDur) +{ + // This makes sure everything is stored using the Normal, and not the reverse + string sSpellId = GetNormalUtterSpellId(nSpellId); + SetLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + sSpellId, TRUE); + DelayCommand(fDur, DeleteLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + sSpellId)); +} + +int CheckLawOfSequence(object oTrueSpeaker, int nSpellId) +{ + // Turns this off + if (GetPRCSwitch(PRC_LAW_OF_SEQUENCE)) return FALSE; + // This makes sure everything is stored using the Normal, and not the reverse + return GetLocalInt(oTrueSpeaker, LAW_OF_SEQUENCE_VARNAME + GetNormalUtterSpellId(nSpellId)); +} + +string GetUtteranceName(int nSpellId) +{ + return GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))); +} + +string GetLexiconName(int nLexicon) +{ + int nStrRef; + switch(nLexicon) + { + case LEXICON_EVOLVING_MIND: nStrRef = 16828478; break; + case LEXICON_CRAFTED_TOOL: nStrRef = 16828479; break; + case LEXICON_PERFECTED_MAP: nStrRef = 16828480; break; + } + + return GetStringByStrRef(nStrRef); +} + +int GetLexiconByUtterance(int nSpellId) +{ + int i, nUtter; + for(i = 0; i < GetPRCSwitch(FILE_END_CLASS_POWER) ; i++) + { + nUtter = StringToInt(Get2DACache("cls_true_utter", "SpellID", i)); + if(nUtter == nSpellId) + { + return StringToInt(Get2DACache("cls_true_utter", "Lexicon", i)); + } + } + // This should never happen + return -1; +} + +void DoSpeakUntoTheMasses(object oTrueSpeaker, object oTarget, struct utterance utter) +{ + // Check for Speak Unto the Masses, exit function if not set + if (!GetLocalInt(oTrueSpeaker, TRUE_SPEAK_UNTO_MASSES)) return; + + // Speak to the Masses affects all creatures of the same race in the AoE + int nRacial = MyPRCGetRacialType(oTarget); + object oSkin; + + // Loop over targets + object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oAreaTarget)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: While entered"); + // Skip the original target/truespeaker, its already been hit + if (oAreaTarget != oTarget && oAreaTarget != oTrueSpeaker) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Target check"); + // Targeting limitations + if(MyPRCGetRacialType(oAreaTarget) == nRacial) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Racial Check"); + // Only affect friends or ignore it + if (GetIsFriend(oAreaTarget, oTrueSpeaker) || !utter.bFriend) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Friend Check"); + // Do SR, or ignore if its a friendly utterance. + if (!PRCDoResistSpell(utter.oTrueSpeaker, oAreaTarget, utter.nPen) || utter.bIgnoreSR) + { + if(DEBUG) DoDebug("Speak Unto the Masses: SR Check"); + // Saving throw, ignore it if there is no DC to check + if(!PRCMySavingThrow(utter.nSaveThrow, oAreaTarget, utter.nSaveDC, utter.nSaveType, OBJECT_SELF) || + utter.nSaveDC == 0) + { + if(DEBUG) DoDebug("Speak Unto the Masses: Saving Throw"); + // Itemproperty, if there is one + oSkin = GetPCSkin(oAreaTarget); + if (GetIsItemPropertyValid(utter.ipIProp1)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: IProp1"); + IPSafeAddItemProperty(oSkin, utter.ipIProp1, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + // Itemproperty, if there is one + if (GetIsItemPropertyValid(utter.ipIProp2)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: IProp2"); + IPSafeAddItemProperty(oSkin, utter.ipIProp2, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + // Itemproperty, if there is one + if (GetIsItemPropertyValid(utter.ipIProp3)) + { + if(DEBUG) DoDebug("Speak Unto the Masses: IProp3"); + IPSafeAddItemProperty(oSkin, utter.ipIProp3, utter.fDur, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + // Duration Effects + SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, utter.eLink, oAreaTarget, utter.fDur, TRUE, utter.nSpellId, utter.nTruespeakerLevel); + if(DEBUG) DoDebug("Speak Unto the Masses: Duration"); + // Impact Effects + SPApplyEffectToObject(DURATION_TYPE_INSTANT, utter.eLink2, oAreaTarget); + if(DEBUG) DoDebug("Speak Unto the Masses: Instant"); + // Utterance Specific code down here + DoWordOfNurturingReverse(oTrueSpeaker, oAreaTarget, utter); + if(DEBUG) DoDebug("Speak Unto the Masses: Word of Nurturing Reverse"); + if (utter.nSpellId == UTTER_ENERGY_NEGATION_R) + DoEnergyNegation(oTrueSpeaker, oTarget, utter, FloatToInt(utter.fDur / 6.0), GetLocalInt(oTrueSpeaker, "TrueEnergyNegation")); + } // end if - Saving Throw + } // end if - Spell Resistance + } // end if - Friend Check + }// end if - Targeting check + } + + // Get next target + oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), GetLocation(oTarget), TRUE, OBJECT_TYPE_CREATURE); + }// end while - Target loop +} + +void DoWordOfNurturingReverse(object oTrueSpeaker, object oTarget, struct utterance utter) +{ + // Returns TRUE upon concentration failure + if (GetBreakConcentrationCheck(oTrueSpeaker)) return; + + int nDamage; + // First, find out what utterance we're using + if (utter.nSpellId == UTTER_WORD_NURTURING_MINOR_R) nDamage = d6(); + else if (utter.nSpellId == UTTER_WORD_NURTURING_LESSER_R) nDamage = d6(2); + else if (utter.nSpellId == UTTER_WORD_NURTURING_MODERATE_R) nDamage = d6(4); + else if (utter.nSpellId == UTTER_WORD_NURTURING_POTENT_R) nDamage = d6(6); + else if (utter.nSpellId == UTTER_WORD_NURTURING_CRITICAL_R) nDamage = d6(8); + else if (utter.nSpellId == UTTER_WORD_NURTURING_GREATER_R) nDamage = d6(10); + // Empower it + if(utter.bEmpower) nDamage += (nDamage/2); + // If we're using this, target has already failed SR and Saves + effect eImp = EffectLinkEffects(EffectVisualEffect(VFX_IMP_MAGLAW), EffectDamage(nDamage)); + SPApplyEffectToObject(DURATION_TYPE_INSTANT, eImp, oTarget); +} + +void DoEnergyNegation(object oTrueSpeaker, object oTarget, struct utterance utter, int nBeats, int nDamageType) +{ + int nDamage = d6(2); + // Empower it + if(utter.bEmpower) nDamage += (nDamage/2); + // Impact VFX + utter.eLink2 = EffectLinkEffects(EffectVisualEffect(VFX_IMP_MAGVIO), EffectDamage(nDamage, nDamageType)); + // Impact Effects + SPApplyEffectToObject(DURATION_TYPE_INSTANT, utter.eLink2, oTarget); + + nBeats -= 1; + if (nBeats > 0) + DelayCommand(6.0, DoEnergyNegation(oTrueSpeaker, oTarget, utter, nBeats, nDamageType)); +} + +object CraftedToolTarget(object oTrueSpeaker, object oTarget) +{ + // Check to see if its a weapon or item of some sort + // Return it if its a valid base item type + if (GetBaseItemType(oTarget) != BASE_ITEM_INVALID) return oTarget; + + object oItem = OBJECT_INVALID; + + // These are utterances that only target weapons + if (PRCGetSpellId() == UTTER_KEEN_WEAPON || PRCGetSpellId() == UTTER_SUPPRESS_WEAPON || PRCGetSpellId() == UTTER_TRANSMUTE_WEAPON) + { + // By the time we're here, it should only be creatures, not items as targets + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + // Only do this for Keen + if (PRCGetSpellId() == UTTER_KEEN_WEAPON) + { + // Put the bonus on the ammo rather than the bow if its ranged + if( GetBaseItemType(oItem) == BASE_ITEM_LONGBOW || GetBaseItemType(oItem) == BASE_ITEM_SHORTBOW ) + { + oItem = GetItemInSlot(INVENTORY_SLOT_ARROWS, oTarget); + } + else if(GetBaseItemType(oItem) == BASE_ITEM_LIGHTCROSSBOW || GetBaseItemType(oItem) == BASE_ITEM_HEAVYCROSSBOW) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BOLTS, oTarget); + } + else if(GetBaseItemType(oItem) == BASE_ITEM_SLING) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BULLETS, oTarget); + } + } + // If its a valid weapon, return it + if (GetBaseItemType(oItem) != BASE_ITEM_INVALID) return oItem; + // Check the spare hand, and make sure its not a shield + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + // If its a valid weapon and not a shield, return it + if (GetBaseItemType(oItem) != BASE_ITEM_INVALID && + GetBaseItemType(oItem) != BASE_ITEM_LARGESHIELD && + GetBaseItemType(oItem) != BASE_ITEM_SMALLSHIELD && + GetBaseItemType(oItem) != BASE_ITEM_TOWERSHIELD) return oItem; + }// These ones target only armour + else if (PRCGetSpellId() == UTTER_FORTIFY_ARMOUR_SNEAK || PRCGetSpellId() == UTTER_FORTIFY_ARMOUR_CRIT) + { + return GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + }// This one targets scrolls and potions + else if (PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_EMP || PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_EXT || + PRCGetSpellId() == UTTER_METAMAGIC_CATALYST_MAX) + { + oItem = GetFirstItemInInventory(oTarget); + while(GetIsObjectValid(oItem)) + { + if (GetBaseItemType(oItem) == BASE_ITEM_SCROLL || GetBaseItemType(oItem) == BASE_ITEM_POTIONS) + { + return oItem; + } + oItem = GetNextItemInInventory(oTarget); + } + } + else // For the rest of the utterances, any item is a valid target. + { + // Get the PC's chosen inventory slot + int nSlot = GetLocalInt(oTrueSpeaker, "TrueCraftedToolTargetSlot"); + oItem = GetItemInSlot(nSlot, oTarget); + // If the chosen item isn't valid, we go into the choice progession + // Yes, its a long chain + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_HEAD, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTRING, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_NECK, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_CLOAK, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_ARMS, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BOOTS, oTarget); + if (!GetIsObjectValid(oItem)) + { + oItem = GetItemInSlot(INVENTORY_SLOT_BELT, oTarget); + } + } + } + } + } + } + } + } + } + } + } + } + return oItem; +} + +int CheckTrueSpeechSkill(object oTrueSpeaker) +{ + // The max for a class skill is 3 + 1 per level. We just divide this in half for Cross Class + int nMax = GetHitDice(oTrueSpeaker) + 3; + nMax /= 2; + // We want base ranks only + int nRanks = GetSkillRank(SKILL_TRUESPEAK, oTrueSpeaker, TRUE); + + // The Truenamer class has Truespeech as a class skill, so no relevel + if (GetLevelByClass(CLASS_TYPE_TRUENAMER, oTrueSpeaker) > 0) return FALSE; + // Same for this class + else if (GetLevelByClass(CLASS_TYPE_BEREFT, oTrueSpeaker) > 0) return FALSE; + // And this one + else if (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oTrueSpeaker) > 0) return FALSE; + // If they have the feat, no relevel + else if(GetHasFeat(FEAT_TRUENAME_TRAINING, oTrueSpeaker)) return FALSE; + // Now we check the values. If they have too many ranks, relevel. + else if (nRanks > nMax) + { + // Relevel + FloatingTextStringOnCreature("You cannot have more than " + IntToString(nMax) + " in TrueSpeech.", oTrueSpeaker, FALSE); + return TRUE; + } + + // No relevel normally + return FALSE; +} + +/* +int GetTargetSpecificChangesToDamage(object oTarget, object oTrueSpeaker, int nDamage, + int bIsHitPointDamage = TRUE, int bIsEnergyDamage = FALSE) +{ + // Greater Utterance Specialization - +2 damage on all HP-damaging utterances when target is within 30ft + if(bIsHitPointDamage && + GetHasFeat(FEAT_GREATER_Utterance_SPECIALIZATION, oTrueSpeaker) && + GetDistanceBetween(oTarget, oTrueSpeaker) <= FeetToMeters(30.0f) + ) + nDamage += 2; + // Intellect Fortress - Halve damage dealt by utterances that allow PR. Goes before DR (-like) reductions + if(GetLocalInt(oTarget, "PRC_Utterance_IntellectFortress_Active") && + Get2DACache("spells", "ItemImmunity", PRCGetSpellId()) == "1" + ) + nDamage /= 2; + // Mental Resistance - 3 damage less for all non-energy damage and ability damage + if(GetHasFeat(FEAT_MENTAL_RESISTANCE, oTarget) && !bIsEnergyDamage) + nDamage -= 3; + + // Reasonable return values only + if(nDamage < 0) nDamage = 0; + + return nDamage; +} +*/ +// Test main +//void main(){} + +int GetCadenceCount(object oTrueSpeaker) +{ + int nClass = GetLevelByClass(CLASS_TYPE_ACOLYTE_EGO, oTrueSpeaker); + if (GetLocalInt(oTrueSpeaker, "ResonantVoice") == TRUE) + { + nClass += 3; //Adds 3 to class level + } + + int nCount = nClass/2; //Get a cadence feat at 2, 4, 6, 8, 10 levels. + + if (nCount > 6) nCount = 6; //Can't go above 6 with Resonant Voice active + + // Return total + return nCount; +} \ No newline at end of file diff --git a/src/include/true_inc_truknwn.nss b/src/include/true_inc_truknwn.nss new file mode 100644 index 0000000..22fbea0 --- /dev/null +++ b/src/include/true_inc_truknwn.nss @@ -0,0 +1,462 @@ +//:://///////////////////////////////////////////// +//:: Truenaming include: utterances known +//:: true_inc_truknwn +//:://///////////////////////////////////////////// +/** @file + Defines functions for adding & removing + utterances known. + + Data stored: + + - For each Utterance list + -- Total number of utterances known + -- A modifier value to maximum utterances known on this list to account for feats and classes that add utterances + -- An array related to utterances the knowledge of which is not dependent on character level + --- Each array entry specifies the spells.2da row of the known utterance's class-specific entry + -- For each character level on which utterances have been gained from this list + --- An array of utterances gained on this level + ---- Each array entry specifies the spells.2da row of the known utterance's class-specific entry + + @author Stratovarius + @date Created - 2006.07.18 +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + +// Included here to provide the values for the constants below +#include "prc_class_const" +#include "true_utter_const" + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const int UTTERANCE_LIST_TRUENAMER = CLASS_TYPE_TRUENAMER; + +/// Special Utterance list. utterances gained via Expanded Knowledge, Psychic Chirurgery and similar sources +const int UTTERANCE_LIST_MISC = CLASS_TYPE_INVALID;//-1; + +const string _UTTERANCE_LIST_NAME_BASE = "PRC_UtteranceList_"; +const string _UTTERANCE_LIST_TOTAL_KNOWN = "_TotalKnown"; +const string _UTTERANCE_LIST_MODIFIER = "_KnownModifier"; +const string _UTTERANCE_LIST_MISC_ARRAY = "_UtterancesKnownMiscArray"; +const string _UTTERANCE_LIST_LEVEL_ARRAY = "_UtterancesKnownLevelArray_"; +const string _UTTERANCE_LIST_GENERAL_ARRAY = "_UtterancesKnownGeneralArray"; + + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Gives the creature the control feats for the given Utterance and marks the utterance + * in a utterances known array. + * If the utterance's data is already stored in one of the utterances known arrays for + * the list or adding the utterance's data to the array fails, the function aborts. + * + * @param oCreature The creature to gain the utterance + * @param nList The list the Utterance comes from. One of UTTERANCE_LIST_* + * @param n2daRow The 2da row in the lists's 2da file that specifies the utterance. + * @param bLevelDependent If this is TRUE, the Utterance is tied to a certain level and can + * be lost via level loss. If FALSE, the Utterance is not dependent + * of a level and cannot be lost via level loss. + * @param nLevelToTieTo If bLevelDependent is TRUE, this specifies the level the utterance + * is gained on. Otherwise, it's ignored. + * The default value (-1) means that the current level of oCreature + * will be used. + * @param nLexicon Type of the Utterance: Evolving Mind, Crafted Tool, or Perfected Map + * + * @return TRUE if the Utterance was successfully stored and control feats added. + * FALSE otherwise. + */ +int AddUtteranceKnown(object oCreature, int nList, int n2daRow, int nLexicon, int bLevelDependent = FALSE, int nLevelToTieTo = -1); + +/** + * Removes all utterances gained from each list on the given level. + * + * @param oCreature The creature whose utterances to remove + * @param nLevel The level to clear + */ +void RemoveUtterancesKnownOnLevel(object oCreature, int nLevel); + +/** + * Gets the value of the utterances known modifier, which is a value that is added + * to the 2da-specified maximum utterances known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to get + * @param nList The list the maximum utterances known from which the modifier + * modifies. One of UTTERANCE_LIST_* + * @param nLexicon Type of the Utterance: Evolving Mind, Crafted Tool, or Perfected Map + */ +int GetKnownUtterancesModifier(object oCreature, int nList, int nLexicon); + +/** + * Sets the value of the utterances known modifier, which is a value that is added + * to the 2da-specified maximum utterances known to determine the actual maximum. + * + * @param oCreature The creature whose modifier to set + * @param nList The list the maximum utterances known from which the modifier + * modifies. One of UTTERANCE_LIST_* + * @param nLexicon Type of the Utterance: Evolving Mind, Crafted Tool, or Perfected Map + */ +void SetKnownUtterancesModifier(object oCreature, int nList, int nNewValue, int nLexicon); + +/** + * Gets the number of utterances a character character possesses from a + * specific list and lexicon + * + * @param oCreature The creature whose utterances to check + * @param nList The list to check. One of UTTERANCE_LIST_* + * @param nLexicon Type of the Utterance: Evolving Mind, Crafted Tool, or Perfected Map + * @return The number of utterances known oCreature has from nList + */ +int GetUtteranceCount(object oCreature, int nList, int nLexicon); + +/** + * Gets the maximum number of utterances a character may posses from a given list + * at this time. Calculated based on class levels, feats and a misceallenous + * modifier. There are three Types of utterances, so it checks each seperately. + * + * @param oCreature Character to determine maximum utterances for + * @param nList UTTERANCE_LIST_* of the list to determine maximum utterances for + * @param nLexicon Type of the Utterance: Evolving Mind, Crafted Tool, or Perfected Map + * @return Maximum number of utterances that oCreature may know from the given list. + */ +int GetMaxUtteranceCount(object oCreature, int nList, int nLexicon); + +/** + * Determines whether a character has a given utterance, gained via some Utterance list. + * + * @param nUtter utterance_* of the Utterance to test + * @param oCreature Character to test for the possession of the utterance + * @return TRUE if the character has the utterance, FALSE otherwise + */ +int GetHasUtterance(int nUtter, object oCreature = OBJECT_SELF); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "inc_item_props" +#include "inc_pers_array" +#include "prc_inc_nwscript" +#include "inc_lookups" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +void _TruenameRecurseRemoveArray(object oCreature, string sArrayName, string sUtterFile, int nArraySize, int nCurIndex) +{ + if(DEBUG) DoDebug("_TruenameRecurseRemoveArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "sArrayName = '" + sArrayName + "'\n" + + "sUtterFile = '" + sUtterFile + "'\n" + + "nArraySize = " + IntToString(nArraySize) + "\n" + + "nCurIndex = " + IntToString(nCurIndex) + "\n" + ); + + // Determine whether we've already parsed the whole array or not + if(nCurIndex >= nArraySize) + { + if(DEBUG) DoDebug("_TruenameRecurseRemoveArray(): Running itemproperty removal loop."); + // Loop over itemproperties on the skin and remove each match + object oSkin = GetPCSkin(oCreature); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT && + GetLocalInt(oCreature, "PRC_UtterFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest))) + ) + { + if(DEBUG) DoDebug("_TruenameRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + } + + ipTest = GetNextItemProperty(oSkin); + } + } + // Still parsing the array + else + { + // Set the marker + string sName = "PRC_UtterFeatRemovalMarker_" + Get2DACache(sUtterFile, "IPFeatID", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArrayName, nCurIndex)) + ); + if(DEBUG) DoDebug("_TruenameRecurseRemoveArray(): Recursing through array, marker set:\n" + sName); + + SetLocalInt(oCreature, sName, TRUE); + // Recurse to next array index + _TruenameRecurseRemoveArray(oCreature, sArrayName, sUtterFile, nArraySize, nCurIndex + 1); + // After returning, delete the local + DeleteLocalInt(oCreature, sName); + } +} + +void _RemoveUtteranceArray(object oCreature, int nList, int nLevel, int nLexicon) +{ + if(DEBUG) DoDebug("_RemoveUtteranceArray():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + + "nLexicon = " + IntToString(nLexicon) + "\n" + ); + + string sBase = _UTTERANCE_LIST_NAME_BASE + IntToString(nList) + IntToString(nLexicon); + string sArray = sBase + _UTTERANCE_LIST_LEVEL_ARRAY + IntToString(nLevel); + int nSize = persistant_array_get_size(oCreature, sArray); + + // Reduce the total by the array size + SetPersistantLocalInt(oCreature, sBase + _UTTERANCE_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _UTTERANCE_LIST_TOTAL_KNOWN) - nSize + ); + + // Remove each Utterance in the array + _TruenameRecurseRemoveArray(oCreature, sArray, GetAMSDefinitionFileName(nList), nSize, 0); + + // Remove the array itself + persistant_array_delete(oCreature, sArray); +} + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +int AddUtteranceKnown(object oCreature, int nList, int n2daRow, int nLexicon, int bLevelDependent = FALSE, int nLevelToTieTo = -1) +{ + string sBase = _UTTERANCE_LIST_NAME_BASE + IntToString(nList) + IntToString(nLexicon); + string sArray = sBase; + string sUtterFile = GetAMSDefinitionFileName(nList); + string sTestArray; + int i, j, nSize, bReturn; + + // Get the spells.2da row corresponding to the cls_psipw_*.2da row + int nSpells2daRow = StringToInt(Get2DACache(sUtterFile, "SpellID", n2daRow)); + + // Determine the array name. + if(bLevelDependent) + { + // If no level is specified, default to the creature's current level + if(nLevelToTieTo == -1) + nLevelToTieTo = GetHitDice(oCreature); + + sArray += _UTTERANCE_LIST_LEVEL_ARRAY + IntToString(nLevelToTieTo); + } + else + { + sArray += _UTTERANCE_LIST_GENERAL_ARRAY; + } + + // Make sure the Utterance isn't already in an array. If it is, abort and return FALSE + // Loop over each level array and check that it isn't there. + for(i = 1; i <= GetHitDice(oCreature); i++) + { + sTestArray = sBase + _UTTERANCE_LIST_LEVEL_ARRAY + IntToString(i); + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + } + // Check the non-level-dependent array + sTestArray = sBase + _UTTERANCE_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sTestArray)) + { + nSize = persistant_array_get_size(oCreature, sTestArray); + for(j = 0; j < nSize; j++) + if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow) + return FALSE; + } + + // All checks are made, now start adding the new utterance + // Create the array if it doesn't exist yet + if(!persistant_array_exists(oCreature, sArray)) + persistant_array_create(oCreature, sArray); + + // Store the Utterance in the array + if(persistant_array_set_int(oCreature, sArray, persistant_array_get_size(oCreature, sArray), nSpells2daRow) != SDL_SUCCESS) + { + if(DEBUG) DoDebug("true_inc_truknwn: AddUtteranceKnown(): ERROR: Unable to add Utterance to known array\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nList = " + IntToString(nList) + "\n" + + "nLexicon = " + IntToString(nLexicon) + "\n" + + "n2daRow = " + IntToString(n2daRow) + "\n" + + "bLevelDependent = " + DebugBool2String(bLevelDependent) + "\n" + + "nLevelToTieTo = " + IntToString(nLevelToTieTo) + "\n" + + "nSpells2daRow = " + IntToString(nSpells2daRow) + "\n" + ); + return FALSE; + } + + // Increment utterances known total + SetPersistantLocalInt(oCreature, sBase + _UTTERANCE_LIST_TOTAL_KNOWN, + GetPersistantLocalInt(oCreature, sBase + _UTTERANCE_LIST_TOTAL_KNOWN) + 1 + ); + + // Give the utterance's control feats + object oSkin = GetPCSkin(oCreature); + string sUtterFeatIP = Get2DACache(sUtterFile, "IPFeatID", n2daRow); + itemproperty ipFeat = PRCItemPropertyBonusFeat(StringToInt(sUtterFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + // Second Utterance feat, if any + sUtterFeatIP = Get2DACache(sUtterFile, "IPFeatID2", n2daRow); + if(sUtterFeatIP != "") + { + ipFeat = PRCItemPropertyBonusFeat(StringToInt(sUtterFeatIP)); + IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); + } + + return TRUE; +} + +void RemoveUtterancesKnownOnLevel(object oCreature, int nLevel) +{ + if(DEBUG) DoDebug("true_inc_truknwn: RemoveUtterancesKnownOnLevel():\n" + + "oCreature = " + DebugObject2Str(oCreature) + "\n" + + "nLevel = " + IntToString(nLevel) + "\n" + ); + + string sPostFix = _UTTERANCE_LIST_LEVEL_ARRAY + IntToString(nLevel); + // For each Utterance list and lexicon, determine if an array exists for this level. + int nLexicon; + for(nLexicon = LEXICON_MIN_VALUE; nLexicon <= LEXICON_MAX_VALUE; nLexicon++) + { + if(persistant_array_exists(oCreature, _UTTERANCE_LIST_NAME_BASE + IntToString(UTTERANCE_LIST_TRUENAMER) + IntToString(nLexicon) + sPostFix)) + _RemoveUtteranceArray(oCreature, UTTERANCE_LIST_TRUENAMER, nLevel, nLexicon); + + if(persistant_array_exists(oCreature, _UTTERANCE_LIST_NAME_BASE + IntToString(UTTERANCE_LIST_MISC) + IntToString(nLexicon) + sPostFix)) + _RemoveUtteranceArray(oCreature, UTTERANCE_LIST_MISC, nLevel, nLexicon); + } +} + +int GetKnownUtterancesModifier(object oCreature, int nList, int nLexicon) +{ + return GetPersistantLocalInt(oCreature, _UTTERANCE_LIST_NAME_BASE + IntToString(nList) + IntToString(nLexicon) + _UTTERANCE_LIST_MODIFIER); +} + +void SetKnownUtterancesModifier(object oCreature, int nList, int nNewValue, int nLexicon) +{ + SetPersistantLocalInt(oCreature, _UTTERANCE_LIST_NAME_BASE + IntToString(nList) + IntToString(nLexicon) + _UTTERANCE_LIST_MODIFIER, nNewValue); +} + +int GetUtteranceCount(object oCreature, int nList, int nLexicon) +{ + return GetPersistantLocalInt(oCreature, _UTTERANCE_LIST_NAME_BASE + IntToString(nList) + IntToString(nLexicon) + _UTTERANCE_LIST_TOTAL_KNOWN); +} + +int GetMaxUtteranceCount(object oCreature, int nList, int nLexicon) +{ + int nMaxUtterances = 0; + if(DEBUG) DoDebug("GetMaxUtteranceCount(" + IntToString(nList) + ", " + IntToString(nLexicon) + ", " + GetName(oCreature) + ")"); + switch(nList) + { + case UTTERANCE_LIST_TRUENAMER:{ + // Determine base utterances known + int nLevel = GetLevelByClass(CLASS_TYPE_TRUENAMER, oCreature); + if(nLevel == 0) + break; + if (LEXICON_EVOLVING_MIND == nLexicon) + nMaxUtterances = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_TRUENAMER), "EvolvingMind", nLevel - 1)); + else if (LEXICON_CRAFTED_TOOL == nLexicon) + nMaxUtterances = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_TRUENAMER), "CraftedTool", nLevel - 1)); + else if (LEXICON_PERFECTED_MAP == nLexicon) + nMaxUtterances = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_TRUENAMER), "PerfectedMap", nLevel - 1)); + + // Calculate feats + if(DEBUG) DoDebug("case UTTERANCE_LIST_TRUENAMER:{" + IntToString(nMaxUtterances)); + // Add in the custom modifier + nMaxUtterances += GetKnownUtterancesModifier(oCreature, nList, nLexicon); + break; + } + case UTTERANCE_LIST_MISC: + if(DEBUG) DoDebug("GetMaxUtteranceCount(): ERROR: Using unfinished Utterance list!"); + break; + + default:{ + string sErr = "GetMaxUtteranceCount(): ERROR: Unknown Utterance list value: " + IntToString(nList) + IntToString(nLexicon); + if(DEBUG) DoDebug(sErr); + else WriteTimestampedLogEntry(sErr); + } + } + + return nMaxUtterances; +} + +int GetHasUtterance(int nUtter, object oCreature = OBJECT_SELF) +{ + if((GetLevelByClass(CLASS_TYPE_TRUENAMER, oCreature) + && GetHasFeat(GetClassFeatFromPower(nUtter, CLASS_TYPE_TRUENAMER), oCreature) + ) + // add new truenaming classes here + ) + return TRUE; + return FALSE; +} + +string DebugListKnownUtterances(object oCreature) +{ + string sReturn = "Utterances known by " + DebugObject2Str(oCreature) + ":\n"; + int i, j, k, numUtterLists = 6; + int nUtterList, nSize; + string sTemp, sArray, sArrayBase, sUtterFile; + // Loop over all Utterance lists + for(i = 1; i <= numUtterLists; i++) + { + // Some padding + sReturn += " "; + // Get the Utterance list for this loop + switch(i) + { + case 1: nUtterList = UTTERANCE_LIST_TRUENAMER; sReturn += "Truenamer"; break; + + // This should always be last + case 2: nUtterList = UTTERANCE_LIST_MISC; sReturn += "Misceallenous"; break; + } + sReturn += " utterances known:\n"; + + // Determine if the character has any utterances from this list + sUtterFile = GetAMSDefinitionFileName(nUtterList); + sArrayBase = _UTTERANCE_LIST_NAME_BASE + IntToString(nUtterList); + + // Loop over levels + for(j = 1; j <= GetHitDice(oCreature); j++) + { + sArray = sArrayBase + _UTTERANCE_LIST_LEVEL_ARRAY + IntToString(j); + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Gained on level " + IntToString(j) + ":\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sUtterFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + // Non-leveldependent utterances + sArray = sArrayBase + _UTTERANCE_LIST_GENERAL_ARRAY; + if(persistant_array_exists(oCreature, sArray)) + { + sReturn += " Non-leveldependent:\n"; + nSize = persistant_array_get_size(oCreature, sArray); + for(k = 0; k < nSize; k++) + sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sUtterFile, "Name", + GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k)) + ) + ) + ) + + "\n"; + } + } + + return sReturn; +} +// Test main +//void main(){} \ No newline at end of file diff --git a/src/include/true_inc_utter.nss b/src/include/true_inc_utter.nss new file mode 100644 index 0000000..3b726dc --- /dev/null +++ b/src/include/true_inc_utter.nss @@ -0,0 +1,671 @@ +//:://///////////////////////////////////////////// +//:: Truenaming include: Uttering +//:: true_inc_Utter +//:://///////////////////////////////////////////// +/** @file + Defines structures and functions for handling + truespeaking an utterance + + @author Stratovarius + @date Created - 2006.7.17 + @thanks to Ornedan for his work on Psionics upon which this is based. +*/ +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// + + +////////////////////////////////////////////////// +/* Constants */ +////////////////////////////////////////////////// + +const string PRC_TRUESPEAKING_CLASS = "PRC_CurrentUtterance_TrueSpeakingClass"; +const string PRC_UTTERANCE_LEVEL = "PRC_CurrentUtterance_Level"; +const string TRUE_DEBUG_IGNORE_CONSTRAINTS = "TRUE_DEBUG_IGNORE_CONSTRAINTS"; + +/** + * The variable in which the utterance token is stored. If no token exists, + * the variable is set to point at the truespeaker itself. That way OBJECT_INVALID + * means the variable is unitialised. + */ +//const string PRC_UTTERANCE_TOKEN_VAR = "PRC_UtteranceToken"; +//const string PRC_UTTERANCE_TOKEN_NAME = "PRC_UTTERTOKEN"; +//const float PRC_UTTERANCE_HB_DELAY = 0.5f; + + +////////////////////////////////////////////////// +/* Structures */ +////////////////////////////////////////////////// + +// struct utterance moved to true_inc_metautr + +////////////////////////////////////////////////// +/* Function prototypes */ +////////////////////////////////////////////////// + +/** + * Determines if the utterance that is currently being attempted to be TrueSpoken + * can in fact be truespoken. Determines metautterances used. + * + * @param oTrueSpeaker A creature attempting to truespeak a utterance at this moment. + * @param oTarget The target of the utterance, if any. For pure Area of Effect. + * utterances, this should be OBJECT_INVALID. Otherwise the main + * target of the utterance as returned by PRCGetSpellTargetObject(). + * @param nMetaUtterFlags The metautterances that may be used to modify this utterance. Any number + * of METAUTTERANCE_* constants ORd together using the | operator. + * For example (METAUTTERANCE_EMPOWER | METAUTTERANCE_EXTEND) + * @param nLexicon Whether it is of the Crafted Tool, Evolving Mind or Perfected Map + * Use one of three constants: TYPE_EVOLVING_MIND, TYPE_CRAFTED_TOOL, TYPE_PERFECTED_MAP + * + * @return A utterance structure that contains the data about whether + * the utterance was successfully truespeaked, what metautterances + * were used and some other commonly used data, like the + * TrueNamer's truespeaker level for this utterance. + */ +struct utterance EvaluateUtterance(object oTrueSpeaker, object oTarget, int nMetaUtterFlags, int nLexicon); + +/** + * Causes OBJECT_SELF to use the given utterance. + * + * @param nUtter The index of the utterance to use in spells.2da or an UTTER_* + * @param nClass The index of the class to use the utterance as in classes.2da or a CLASS_TYPE_* + * @param nLevelOverride An optional override to normal truespeaker level. + * Default: 0, which means the parameter is ignored. + */ +void UseUtterance(int nUtter, int nClass, int nLevelOverride = 0); + +/** + * A debugging function. Takes a utterance structure and + * makes a string describing the contents. + * + * @param utter A set of utterance data + * @return A string describing the contents of utter + */ +string DebugUtterance2Str(struct utterance utter); + +/** + * Stores a utterance structure as a set of local variables. If + * a structure was already stored with the same name on the same object, + * it is overwritten. + * + * @param oObject The object on which to store the structure + * @param sName The name under which to store the structure + * @param utter The utterance structure to store + */ +void SetLocalUtterance(object oObject, string sName, struct utterance utter); + +/** + * Retrieves a previously stored utterance structure. If no structure is stored + * by the given name, the structure returned is empty. + * + * @param oObject The object from which to retrieve the structure + * @param sName The name under which the structure is stored + * @return The structure built from local variables stored on oObject under sName + */ +struct utterance GetLocalUtterance(object oObject, string sName); + +/** + * Deletes a stored utterance structure. + * + * @param oObject The object on which the structure is stored + * @param sName The name under which the structure is stored + */ +void DeleteLocalUtterance(object oObject, string sName); + +/** + * Sets the evaluation functions to ignore constraints on truespeaking. + * Call this just prior to EvaluateUtterance() in a utterance script. + * That evaluation will then ignore lacking utterance ability score, + * utterance Points and Psionic Focuses. + * + * @param oTrueSpeaker A creature attempting to truespeak a utterance at this moment. + */ +void TruenameDebugIgnoreConstraints(object oTrueSpeaker); + +////////////////////////////////////////////////// +/* Includes */ +////////////////////////////////////////////////// + +#include "true_inc_metautr" +#include "true_inc_truespk" +#include "inc_newspellbook" +#include "inc_lookups" + +////////////////////////////////////////////////// +/* Internal functions */ +////////////////////////////////////////////////// + +/** Internal function. + * Handles Spellfire absorption when a utterance is used on a friendly spellfire + * user. + */ +struct utterance _DoTruenameSpellfireFriendlyAbsorption(struct utterance utter, object oTarget) +{ + if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") && + GetIsFriend(oTarget, utter.oTrueSpeaker) + ) + { + if(CheckSpellfire(utter.oTrueSpeaker, oTarget, TRUE)) + { + PRCShowSpellResist(utter.oTrueSpeaker, oTarget, SPELL_RESIST_MANTLE); + utter.bCanUtter = FALSE; + } + } + + return utter; +} + +/** Internal function. + * Deletes utterance-related local variables. + * + * @param oTrueSpeaker The creature currently truespeaking a utterance + */ +void _CleanUtteranceVariables(object oTrueSpeaker) +{ + DeleteLocalInt(oTrueSpeaker, PRC_TRUESPEAKING_CLASS); + DeleteLocalInt(oTrueSpeaker, PRC_UTTERANCE_LEVEL); +} + +/** Internal function. + * Sets utterance-related local variables. + * + * @param oTrueSpeaker The creature currently truespeaking a utterance + * @param nClass Utterance casting class constant + * @param nLevel Utterance level + * @param bQuicken If the utterance was quicened 1, else 0 + */ +void _SetUtteranceVariables(object oTrueSpeaker, int nClass, int nLevel, int bQuicken) +{ + SetLocalInt(oTrueSpeaker, PRC_TRUESPEAKING_CLASS, nClass + 1); + SetLocalInt(oTrueSpeaker, PRC_UTTERANCE_LEVEL, nLevel); + SetLocalInt(oTrueSpeaker, PRC_UTTERANCE_IS_QUICKENED, bQuicken); +} + + + +/** Internal function. + * Determines whether a utterance token exists. If one does, returns it. + * + * @param oTrueSpeaker A creature whose utterance token to get + * @return The utterance token if it exists, OBJECT_INVALID otherwise. + */ +/*object _GetUtteranceToken(object oTrueSpeaker) +{ + object oUtrToken = GetLocalObject(oTrueSpeaker, PRC_UTTERANCE_TOKEN_VAR); + + // If the token object is no longer valid, set the variable to point at truespeaker + if(!GetIsObjectValid(oUtrToken)) + { + oUtrToken = oTrueSpeaker; + SetLocalObject(oTrueSpeaker, PRC_UTTERANCE_TOKEN_VAR, oUtrToken); + } + + + // Check if there is no token + if(oUtrToken == oTrueSpeaker) + oUtrToken = OBJECT_INVALID; + + return oUtrToken; +}*/ + +/** Internal function. + * Destroys the given utterance token and sets the creature's utterance token variable + * to point at itself. + * + * @param oTrueSpeaker The truespeaker whose token to destroy + * @param oUtrToken The token to destroy + */ +/*void _DestroyUtteranceToken(object oTrueSpeaker, object oUtrToken) +{ + DestroyObject(oUtrToken); + SetLocalObject(oTrueSpeaker, PRC_UTTERANCE_TOKEN_VAR, oTrueSpeaker); +}*/ + +/** Internal function. + * Destroys the previous utterance token, if any, and creates a new one. + * + * @param oTrueSpeaker A creature for whom to create a utterance token + * @return The newly created token + */ +/*object _CreateUtteranceToken(object oTrueSpeaker) +{ + object oUtrToken = _GetUtteranceToken(oTrueSpeaker); + object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oTrueSpeaker); + + // Delete any previous tokens + if(GetIsObjectValid(oUtrToken)) + _DestroyUtteranceToken(oTrueSpeaker, oUtrToken); + + // Create new token and store a reference to it + oUtrToken = CreateItemOnObject(PRC_UTTERANCE_TOKEN_NAME, oStore); + SetLocalObject(oTrueSpeaker, PRC_UTTERANCE_TOKEN_VAR, oUtrToken); + + Assert(GetIsObjectValid(oUtrToken), "GetIsObjectValid(oUtrToken)", "ERROR: Unable to create utterance token! Store object: " + DebugObject2Str(oStore), "true_inc_Utter", "_CreateUtteranceToken()"); + + return oUtrToken; +}*/ + +/** Internal function. + * Determines whether the given truespeaker is doing something that would + * interrupt truespeaking a utterance or affected by an effect that would do + * the same. + * + * @param oTrueSpeaker A creature on which _UtteranceHB() is running + * @return TRUE if the creature can continue truespeaking, + * FALSE otherwise + */ +/*int _UtteranceStateCheck(object oTrueSpeaker) +{ + int nAction = GetCurrentAction(oTrueSpeaker); + // If the current action is not among those that could either be used to truespeak the utterance or movement, the utterance fails + if(!(nAction || ACTION_CASTSPELL || nAction == ACTION_INVALID || + nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT || + nAction || ACTION_USEOBJECT || nAction == ACTION_WAIT + ) ) + return FALSE; + + // Affected by something that prevents one from truespeaking + effect eTest = GetFirstEffect(oTrueSpeaker); + int nEType; + while(GetIsEffectValid(eTest)) + { + nEType = GetEffectType(eTest); + if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE || + nEType == EFFECT_TYPE_DAZED || + nEType == EFFECT_TYPE_PARALYZE || + nEType == EFFECT_TYPE_PETRIFY || + nEType == EFFECT_TYPE_SLEEP || + nEType == EFFECT_TYPE_STUNNED + ) + return FALSE; + + // Get next effect + eTest = GetNextEffect(oTrueSpeaker); + } + + return TRUE; +}*/ + +/** Internal function. + * Runs while the given creature is truespeaking. If they move, take other actions + * that would cause them to interrupt truespeaking the utterance or are affected by an + * effect that would cause such interruption, deletes the utterance token. + * Stops if such condition occurs or something else destroys the token. + * + * @param oTrueSpeaker A creature truespeaking a utterance + * @param lTrueSpeaker The location where the truespeaker was when starting the utterance + * @param oUtrToken The utterance token that controls the ongoing utterance + */ +/*void _UtteranceHB(object oTrueSpeaker, location lTrueSpeaker, object oUtrToken) +{ + if(DEBUG) DoDebug("_UtteranceHB() running:\n" + + "oTrueSpeaker = " + DebugObject2Str(oTrueSpeaker) + "\n" + + "lTrueSpeaker = " + DebugLocation2Str(lTrueSpeaker) + "\n" + + "oUtrToken = " + DebugObject2Str(oUtrToken) + "\n" + + "Distance between utterance start location and current location: " + FloatToString(GetDistanceBetweenLocations(lTrueSpeaker, GetLocation(oTrueSpeaker))) + "\n" + ); + if(GetIsObjectValid(oUtrToken)) + { + // Continuance check + if(GetDistanceBetweenLocations(lTrueSpeaker, GetLocation(oTrueSpeaker)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting + !_UtteranceStateCheck(oTrueSpeaker) // Action and effect check + ) + { + if(DEBUG) DoDebug("_UtteranceHB(): truespeaker moved or lost concentration, destroying token"); + _DestroyUtteranceToken(oTrueSpeaker, oUtrToken); + + // Inform truespeaker + FloatingTextStrRefOnCreature(16828469, oTrueSpeaker, FALSE); // "You have lost concentration on the utterance you were attempting to truespeak!" + } + // Schedule next HB + else + DelayCommand(PRC_UTTERANCE_HB_DELAY, _UtteranceHB(oTrueSpeaker, lTrueSpeaker, oUtrToken)); + } +}*/ + +/** Internal function. + * Checks if the truespeaker is in range to use the utterance they are trying to use. + * If not, queues commands to make the truespeaker to run into range. + * + * @param oTrueSpeaker A creature truespeaking a utterance + * @param nUtter SpellID of the utterance being truespeaked + * @param lTarget The target location or the location of the target object + */ +/*void _UtteranceRangeCheck(object oTrueSpeaker, int nUtter, location lTarget) +{ + float fDistance = GetDistanceBetweenLocations(GetLocation(oTrueSpeaker), lTarget); + float fRangeLimit; + string sRange = Get2DACache("spells", "Range", nUtter); + + // Personal range utterances are always in range + if(sRange == "P") + return; + // Ranges according to the CCG spells.2da page + else if(sRange == "T") + fRangeLimit = 2.25f; + else if(sRange == "S") + fRangeLimit = 8.0f; + else if(sRange == "M") + fRangeLimit = 20.0f; + else if(sRange == "L") + fRangeLimit = 40.0f; + + // See if we are out of range + if(fDistance > fRangeLimit) + { + // Create waypoint for the movement + object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget); + + // Move into range, with a bit of fudge-factor + //fRangeLimit /= 2; + //ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f); + + // CleanUp + ActionDoCommand(DestroyObject(oWP)); + + // CleanUp, paranoia + AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f))); + } +}*/ + +/** Internal function. + * Assigns the fakecast command that is used to display the conjuration VFX when using an utterance. + * Separated from UseUtterance() due to a bug with ActionFakeCastSpellAtObject(), which requires + * use of ClearAllActions() to work around. + * The problem is that if the target is an item on the ground, if the actor is out of spell + * range when doing the fakecast, they will run on top of the item instead of to the edge of + * the spell range. This only happens if there was a "real action" in the actor's action queue + * immediately prior to the fakecast. + */ +/*void _AssignUseUtteranceFakeCastCommands(object oTrueSpeaker, object oTarget, location lTarget, int nSpellID) +{ + // Nuke actions to prevent the fakecast action from bugging + ClearAllActions(); + + if(GetIsObjectValid(oTarget)) + ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT); + else + ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT); +}*/ + + +/** Internal function. + * Places the cheatcasting of the real utterance into the truespeaker's action queue. + */ +/*void _UseUtteranceAux(object oTrueSpeaker, object oUtrToken, int nSpellId, + object oTarget, location lTarget, + int nUtter, int nClass, int nLevelOverride, + int bQuickened + ) +{ + if(DEBUG) DoDebug("_UseUtteranceAux() running:\n" + + "oTrueSpeaker = " + DebugObject2Str(oTrueSpeaker) + "\n" + + "oUtrToken = " + DebugObject2Str(oUtrToken) + "\n" + + "nSpellId = " + IntToString(nSpellId) + "\n" + + "oTarget = " + DebugObject2Str(oTarget) + "\n" + + "lTarget = " + DebugLocation2Str(lTarget) + "\n" + + "nUtter = " + IntToString(nUtter) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + + "bQuickened = " + DebugBool2String(bQuickened) + "\n" + ); + + // Make sure nothing has interrupted this utterance + if(GetIsObjectValid(oUtrToken)) + { + if(DEBUG) DoDebug("_UseUtteranceAux(): Token was valid, queueing actual utterance"); + // Set the class to truespeak as + SetLocalInt(oTrueSpeaker, PRC_TRUESPEAKING_CLASS, nClass + 1); + + // Set the utterance's level + SetLocalInt(oTrueSpeaker, PRC_UTTERANCE_LEVEL, StringToInt(lookup_spell_innate(nSpellId))); + + // Set whether the utterance was quickened + SetLocalInt(oTrueSpeaker, PRC_UTTERANCE_IS_QUICKENED, bQuickened); + + // Queue the real utterance + //ActionCastSpell(nUtter, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, TRUE, TRUE, oTarget); + + if(nLevelOverride != 0) + AssignCommand(oTrueSpeaker, ActionDoCommand(SetLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride))); + if(GetIsObjectValid(oTarget)) + AssignCommand(oTrueSpeaker, ActionCastSpellAtObject(nUtter, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + else + AssignCommand(oTrueSpeaker, ActionCastSpellAtLocation(nUtter, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)); + if(nLevelOverride != 0) + AssignCommand(oTrueSpeaker, ActionDoCommand(DeleteLocalInt(oTrueSpeaker, PRC_CASTERLEVEL_OVERRIDE))); + + // Destroy the utterance token for this utterance + _DestroyUtteranceToken(oTrueSpeaker, oUtrToken); + } +}*/ + + +////////////////////////////////////////////////// +/* Function definitions */ +////////////////////////////////////////////////// + +struct utterance EvaluateUtterance(object oTrueSpeaker, object oTarget, int nMetaUtterFlags, int nLexicon) +{ + /* Get some data */ + int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oTrueSpeaker, TRUE_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // truespeaker-related stuff + int nTruespeakerLevel = GetTruespeakerLevel(oTrueSpeaker); + int nUtterLevel = GetUtteranceLevel(oTrueSpeaker); + int nClass = GetTruespeakingClass(oTrueSpeaker); + + /* Initialise the utterance structure */ + struct utterance utter; + utter.oTrueSpeaker = oTrueSpeaker; + utter.bCanUtter = TRUE; // Assume successfull utterance by default + utter.nTruespeakerLevel = nTruespeakerLevel; + utter.nSpellId = PRCGetSpellId(); + utter.nUtterDC = GetBaseUtteranceDC(oTarget, oTrueSpeaker, nLexicon); + + // Account for metautterances. This includes adding the appropriate DC boosts. + utter = EvaluateMetautterances(utter, nMetaUtterFlags); + // Account for the law of resistance + utter.nUtterDC += GetLawOfResistanceDCIncrease(oTrueSpeaker, utter.nSpellId); + // DC change for targeting self and using a Personal Truename + utter.nUtterDC += AddPersonalTruenameDC(oTrueSpeaker, oTarget); + // DC change for ignoring Spell Resistance + utter.nUtterDC += AddIgnoreSpellResistDC(oTrueSpeaker); + // DC change for specific utterances + utter.nUtterDC += AddUtteranceSpecificDC(oTrueSpeaker); + + // Skip paying anything if something has prevented successfull utterance already by this point + if(utter.bCanUtter) + { + /* Roll the dice, and see if we succeed or fail. + */ + if(GetIsSkillSuccessful(oTrueSpeaker, SKILL_TRUESPEAK, utter.nUtterDC) || bIgnoreConstraints) + { + // Increases the DC of the subsequent utterances + DoLawOfResistanceDCIncrease(oTrueSpeaker, utter.nSpellId); + // Spellfire friendly absorption - This may set bCananifest to FALSE + utter = _DoTruenameSpellfireFriendlyAbsorption(utter, oTarget); + //* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL UTTERANCE ABOVE *// + + } + // Failed the DC roll + else + { + // No need for an output here because GetIsSkillSuccessful does it for us. + utter.bCanUtter = FALSE; + } + }//end if + + if(DEBUG) DoDebug("EvaluateUtterance(): Final result:\n" + DebugUtterance2Str(utter)); + + // Initiate utterance-related variable CleanUp + //DelayCommand(0.5f, _CleanUtteranceVariables(oTrueSpeaker)); + + return utter; +} + +/*void UseUtterance(int nUtter, int nClass, int nLevelOverride = 0) +{ + object oTrueSpeaker = OBJECT_SELF; + object oSkin = GetPCSkin(oTrueSpeaker); + object oTarget = PRCGetSpellTargetObject(); + object oUtrToken; + location lTarget = PRCGetSpellTargetLocation(); + int nSpellID = PRCGetSpellId(); + int nUtterDur = StringToInt(Get2DACache("spells", "ConjTime", nUtter)) + StringToInt(Get2DACache("spells", "CastTime", nUtter)); + int bQuicken = FALSE; + + // Normally swift action utterances check + if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nUtter, nClass)) == "SWIFT_ACTION" && // The utterance is swift action to use + TakeSwiftAction(oTrueSpeaker) // And the truespeaker can take a swift action now + ) + { + nUtterDur = 0; + } + // Quicken utterance check + else if(nUtterDur <= 6000 && // If the utterance could be quickened by having truespeaking time of 1 round or less + GetLocalInt(oTrueSpeaker, METAUTTERANCE_QUICKEN_VAR) && // And the truespeaker has Quicken utterance active + TakeSwiftAction(oTrueSpeaker) // And the truespeaker can take a swift action + ) + { + // Set the utterance time to 0 to skip VFX + nUtterDur = 0; + // And set the Quicken utterance used marker to TRUE + bQuicken = TRUE; + } + + if(DEBUG) DoDebug("UseUtterance(): truespeaker is " + DebugObject2Str(oTrueSpeaker) + "\n" + + "nUtter = " + IntToString(nUtter) + "\n" + + "nClass = " + IntToString(nClass) + "\n" + + "nLevelOverride = " + IntToString(nLevelOverride) + "\n" + + "utterance duration = " + IntToString(nUtterDur) + "ms \n" + + "bQuicken = " + DebugBool2String(bQuicken) + "\n" + //+ "Token exists = " + DebugBool2String(GetIsObjectValid(oUtrToken)) + ); + + // Create the utterance token. Deletes any old tokens and cancels corresponding utterances as a side effect + oUtrToken = _CreateUtteranceToken(oTrueSpeaker); + + /// @todo Hook to the truespeaker's OnDamaged event for the concentration checks to avoid losing the utterance + + // Nuke action queue to prevent cheating with creative utterance stacking. + // Probably not necessary anymore - Ornedan + if(DEBUG) SendMessageToPC(oTrueSpeaker, "Clearing all actions in preparation for second stage of the utterance."); + ClearAllActions(); + + // If out of range, move to range + _UtteranceRangeCheck(oTrueSpeaker, nUtter, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget); + + // Start the utterance monitor HB + DelayCommand(nUtterDur / 1000.0f, ActionDoCommand(_UtteranceHB(oTrueSpeaker, GetLocation(oTrueSpeaker), oUtrToken))); + + // Assuming the spell isn't used as a swift action, fakecast for visuals + if(nUtterDur > 0) + { + // Hack. Workaround of a bug with the fakecast actions. See function comment for details + ActionDoCommand(_AssignUseUtteranceFakeCastCommands(oTrueSpeaker, oTarget, lTarget, nSpellID)); + } + + // Action queue the function that will cheatcast the actual utterance + DelayCommand(nUtterDur / 1000.0f, AssignCommand(oTrueSpeaker, ActionDoCommand(_UseUtteranceAux(oTrueSpeaker, oUtrToken, nSpellID, oTarget, lTarget, nUtter, nClass, nLevelOverride, bQuicken)))); +}*/ + +void UseUtterance(int nUtter, int nClass, int nLevelOverride = 0) +{ + object oTrueSpeaker = OBJECT_SELF; + int bQuickened = FALSE; + int nUtterDur = StringToInt(Get2DACache("spells", "ConjTime", nUtter)) + StringToInt(Get2DACache("spells", "CastTime", nUtter)); + + // Check the Law of Sequence. Returns True if the utterance is active + if (CheckLawOfSequence(oTrueSpeaker, nUtter)) + { + FloatingTextStringOnCreature("You already have " + GetUtteranceName(nUtter) + " active. Utterance Failed.", oTrueSpeaker, FALSE); + return; + } + + // Quicken utterance check + if(nUtterDur <= 6000 && // If the utterance could be quickened by having truespeaking time of 1 round or less + GetLocalInt(oTrueSpeaker, METAUTTERANCE_QUICKEN_VAR) && // And the truespeaker has Quicken utterance active + TakeSwiftAction(oTrueSpeaker)) // And the truespeaker can take a swift action + { + //Adding Auto-Quicken III for one round - deleted after casting is finished. + object oSkin = GetPCSkin(oTrueSpeaker); + itemproperty ipAutoQuicken = ItemPropertyBonusFeat(IP_CONST_NSB_AUTO_QUICKEN); + ActionDoCommand(AddItemProperty(DURATION_TYPE_TEMPORARY, ipAutoQuicken, oSkin, nUtterDur/1000.0f)); + bQuickened = TRUE; + } + + // Setup utterance-related variables + ActionDoCommand(_SetUtteranceVariables(oTrueSpeaker, nClass, StringToInt(lookup_spell_innate(nUtter)), bQuickened)); + + // cast the actual utterance + ActionCastSpell(nUtter, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, FALSE); + + // Initiate utterance-related variable CleanUp + ActionDoCommand(_CleanUtteranceVariables(oTrueSpeaker)); +} + +string DebugUtterance2Str(struct utterance utter) +{ + string sRet; + + sRet += "oTrueSpeaker = " + DebugObject2Str(utter.oTrueSpeaker) + "\n"; + sRet += "bCanUtter = " + DebugBool2String(utter.bCanUtter) + "\n"; + sRet += "nTruespeakerLevel = " + IntToString(utter.nTruespeakerLevel) + "\n"; + + sRet += "bEmpower = " + DebugBool2String(utter.bEmpower) + "\n"; + sRet += "bExtend = " + DebugBool2String(utter.bExtend) + "\n"; + sRet += "bQuicken = " + DebugBool2String(utter.bQuicken);// + "\n"; + + return sRet; +} + +void SetLocalUtterance(object oObject, string sName, struct utterance utter) +{ + //SetLocal (oObject, sName + "_", ); + SetLocalObject(oObject, sName + "_oTrueSpeaker", utter.oTrueSpeaker); + + SetLocalInt(oObject, sName + "_bCanUtter", utter.bCanUtter); + SetLocalInt(oObject, sName + "_nTruespeakerLevel", utter.nTruespeakerLevel); + SetLocalInt(oObject, sName + "_nSpellID", utter.nSpellId); + + SetLocalInt(oObject, sName + "_bEmpower", utter.bEmpower); + SetLocalInt(oObject, sName + "_bExtend", utter.bExtend); + SetLocalInt(oObject, sName + "_bQuicken", utter.bQuicken); +} + +struct utterance GetLocalUtterance(object oObject, string sName) +{ + struct utterance utter; + utter.oTrueSpeaker = GetLocalObject(oObject, sName + "_oTrueSpeaker"); + + utter.bCanUtter = GetLocalInt(oObject, sName + "_bCanUtter"); + utter.nTruespeakerLevel = GetLocalInt(oObject, sName + "_nTruespeakerLevel"); + utter.nSpellId = GetLocalInt(oObject, sName + "_nSpellID"); + + utter.bEmpower = GetLocalInt(oObject, sName + "_bEmpower"); + utter.bExtend = GetLocalInt(oObject, sName + "_bExtend"); + utter.bQuicken = GetLocalInt(oObject, sName + "_bQuicken"); + + return utter; +} + +void DeleteLocalUtterance(object oObject, string sName) +{ + DeleteLocalObject(oObject, sName + "_oTrueSpeaker"); + + DeleteLocalInt(oObject, sName + "_bCanUtter"); + DeleteLocalInt(oObject, sName + "_nTruespeakerLevel"); + DeleteLocalInt(oObject, sName + "_nSpellID"); + + DeleteLocalInt(oObject, sName + "_bEmpower"); + DeleteLocalInt(oObject, sName + "_bExtend"); + DeleteLocalInt(oObject, sName + "_bQuicken"); +} + +void TruenameDebugIgnoreConstraints(object oTrueSpeaker) +{ + SetLocalInt(oTrueSpeaker, TRUE_DEBUG_IGNORE_CONSTRAINTS, TRUE); + DelayCommand(0.0f, DeleteLocalInt(oTrueSpeaker, TRUE_DEBUG_IGNORE_CONSTRAINTS)); +} + +// Test main +//void main(){} diff --git a/src/include/true_utter_const.nss b/src/include/true_utter_const.nss new file mode 100644 index 0000000..4fb0b62 --- /dev/null +++ b/src/include/true_utter_const.nss @@ -0,0 +1,178 @@ + + +const string LAW_OF_RESIST_VARNAME = "PRC_LawOfResistance"; +const string LAW_OF_SEQUENCE_VARNAME = "PRC_LawOfSequence"; +const string TRUE_IGNORE_SR = "PRC_True_IgnoreSR"; +const string TRUE_SPEAK_UNTO_MASSES = "PRC_True_SpeakMass"; +const int LEXICON_EVOLVING_MIND = 1; +const int LEXICON_CRAFTED_TOOL = 2; +const int LEXICON_PERFECTED_MAP = 3; +const int LEXICON_MIN_VALUE = 1; +const int LEXICON_MAX_VALUE = 3; + +//Real Utterance SpellId Constants +//Reverse Utterances are denoted with a _R + +// Level 1 Utterances +const int UTTER_DEFENSIVE_EDGE = 3526; +const int UTTER_DEFENSIVE_EDGE_R = 3527; +const int UTTER_INERTIAL_SURGE = 3528; +const int UTTER_INERTIAL_SURGE_R = 3529; +const int UTTER_KNIGHTS_PUISSANCE = 3530; +const int UTTER_KNIGHTS_PUISSANCE_R = 3531; +const int UTTER_UNIVERSAL_APTITUDE = 3532; +const int UTTER_UNIVERSAL_APTITUDE_R = 3533; +const int UTTER_WORD_NURTURING_MINOR = 3534; +const int UTTER_WORD_NURTURING_MINOR_R = 3535; +const int UTTER_FORTIFY_ARMOUR_SNEAK = 3536; +const int UTTER_FORTIFY_ARMOUR_CRIT = 3537; +const int UTTER_KEEN_WEAPON = 3538; +const int UTTER_FOG_VOID_CLOUD = 3539; +const int UTTER_FOG_VOID_SOLID = 3540; +const int UTTER_SHIELD_LANDSCAPE = 3541; +const int UTTER_SHOCKWAVE = 3542; + +// Level 2 Utterances +const int UTTER_ARCHERS_EYE = 3543; +const int UTTER_ARCHERS_EYE_R = 3544; +const int UTTER_HIDDEN_TRUTH = 3545; +const int UTTER_HIDDEN_TRUTH_R = 3546; +const int UTTER_PERCEIVE_UNSEEN = 3547; +const int UTTER_PERCEIVE_UNSEEN_R = 3548; +const int UTTER_SILENT_CASTER = 3549; +const int UTTER_SILENT_CASTER_R = 3550; +const int UTTER_SPEED_ZEPHYR = 3551; +const int UTTER_SPEED_ZEPHYR_R = 3352; +const int UTTER_STRIKE_MIGHT = 3553; +const int UTTER_STRIKE_MIGHT_R = 3554; +const int UTTER_TEMPORAL_TWIST = 3555; +const int UTTER_TEMPORAL_TWIST_R = 3556; +const int UTTER_WORD_NURTURING_LESSER = 3557; +const int UTTER_WORD_NURTURING_LESSER_R = 3558; +const int UTTER_AGITATE_ITEM_HOT = 3559; +const int UTTER_AGITATE_ITEM_COLD = 3560; +const int UTTER_ANALYZE_ITEM = 3561; +const int UTTER_ENERGY_VORTEX_ACID = 3562; +const int UTTER_ENERGY_VORTEX_COLD = 3563; +const int UTTER_ENERGY_VORTEX_ELEC = 3564; +const int UTTER_ENERGY_VORTEX_FIRE = 3565; +const int UTTER_SPEAK_ROCK_MUD = 3566; +const int UTTER_TRANSFORM_LANDSCAPE = 3567; + +// Level 3 Utterances +const int UTTER_ACCELERATED_ATTACK = 3568; +const int UTTER_ACCELERATED_ATTACK_R = 3569; +const int UTTER_ENERGY_NEGATION = 3570; +const int UTTER_ENERGY_NEGATION_R = 3571; +const int UTTER_ENERGY_NEGATION_CHOICE = 3290; +const int UTTER_INCARNATION_ANGELS = 3572; +const int UTTER_INCARNATION_ANGELS_R = 3573; +const int UTTER_SPEED_ZEPHYR_GREATER = 3574; +const int UTTER_SPEED_ZEPHYR_GREATER_R = 3575; +const int UTTER_TEMPORAL_SPIRAL = 3576; +const int UTTER_TEMPORAL_SPIRAL_R = 3577; +const int UTTER_VISION_SHARPENED = 3578; +const int UTTER_VISION_SHARPENED_R = 3579; +const int UTTER_WORD_NURTURING_MODERATE = 3580; +const int UTTER_WORD_NURTURING_MODERATE_R = 3581; +const int UTTER_REMOVE_ITEM = 3582; +const int UTTER_SUPPRESS_WEAPON = 3583; +const int UTTER_LORE_WORLD = 3584; +const int UTTER_MASTER_FOUR_WINDS = 3585; +const int UTTER_THWART_TRAVELER = 3586; + +// Level 4 Utterances +const int UTTER_BREATH_CLEANSING = 3587; +const int UTTER_BREATH_CLEANSING_R = 3588; +const int UTTER_CASTER_LENS = 3589; +const int UTTER_CASTER_LENS_R = 3590; +const int UTTER_CONFOUNDING_RESISTANCE = 3591; +const int UTTER_CONFOUNDING_RESISTANCE_R = 3592; +const int UTTER_MORALE_BOOST = 3593; +const int UTTER_MORALE_BOOST_R = 3594; +const int UTTER_MAGICAL_CONTRACTION = 3595; +const int UTTER_MAGICAL_CONTRACTION_R = 3596; +const int UTTER_SPELL_REBIRTH = 3597; +const int UTTER_SPELL_REBIRTH_R = 3598; +const int UTTER_WORD_NURTURING_POTENT = 3599; +const int UTTER_WORD_NURTURING_POTENT_R = 3600; +const int UTTER_WORD_BOLSTERING = 3601; +const int UTTER_WORD_BOLSTERING_R = 3602; +const int UTTER_WORD_BOLSTERING_CHOICE = 3335; +const int UTTER_SUPPRESS_ITEM = 3603; +const int UTTER_TRANSMUTE_WEAPON = 3604; +const int UTTER_ANGER_SLEEPING_EARTH = 3605; +const int UTTER_CONJUNCTIVE_GATE = 3606; +const int UTTER_DENY_PASSAGE = 3607; + +// Level 5 Utterances +const int UTTER_ELDRITCH_ATTRACTION = 3608; +const int UTTER_ELDRITCH_ATTRACTION_R = 3609; +const int UTTER_ENERGY_NEGATION_GREATER = 3610; +const int UTTER_ENERGY_NEGATION_GREATER_R = 3611; +const int UTTER_ENERGY_NEGATION_GREATER_CHOICE = 3347; +const int UTTER_ESSENCE_LIFESPARK = 3612; +const int UTTER_ESSENCE_LIFESPARK_R = 3613; +const int UTTER_PRETERNATURAL_CLARITY_ATTACK = 3614; +const int UTTER_PRETERNATURAL_CLARITY_SKILL = 3615; +const int UTTER_PRETERNATURAL_CLARITY_SAVE = 3616; +const int UTTER_PRETERNATURAL_CLARITY_R = 3617; +const int UTTER_SENSORY_FOCUS = 3618; +const int UTTER_SENSORY_FOCUS_R = 3619; +const int UTTER_WARD_PEACE = 3620; +const int UTTER_WARD_PEACE_R = 3621; +const int UTTER_WORD_NURTURING_CRITICAL = 3622; +const int UTTER_WORD_NURTURING_CRITICAL_R = 3623; +const int UTTER_METAMAGIC_CATALYST_EMP = 3624; +const int UTTER_METAMAGIC_CATALYST_EXT = 3625; +const int UTTER_METAMAGIC_CATALYST_MAX = 3626; +const int UTTER_SEIZE_ITEM = 3627; + +// Level 6 Utterances +const int UTTER_BREATH_RECOVERY = 3628; +const int UTTER_BREATH_RECOVERY_R = 3629; +const int UTTER_ETHER_REFORGED = 3630; +const int UTTER_ETHER_REFORGED_R = 3631; +const int UTTER_KNIGHTS_PUISSANCE_GREATER = 3632; +const int UTTER_KNIGHTS_PUISSANCE_GREATER_R = 3633; +const int UTTER_MYSTIC_RAMPART = 3634; +const int UTTER_MYSTIC_RAMPART_R = 3635; +const int UTTER_SINGULAR_MIND = 3636; +const int UTTER_SINGULAR_MIND_R = 3637; +const int UTTER_WORD_NURTURING_GREATER = 3638; +const int UTTER_WORD_NURTURING_GREATER_R = 3639; + +// Syllables +const int SYLLABLE_DETACHMENT = 3418; +const int SYLLABLE_AFFLICATION_SIGHT = 3420; +const int SYLLABLE_AFFLICATION_SOUND = 3421; +const int SYLLABLE_AFFLICATION_TOUCH = 3422; +const int SYLLABLE_EXILE = 3423; +const int SYLLABLE_DISSOLUTION = 3424; +const int SYLLABLE_ENERVATION = 3425; + +// Truenamer +const int TRUE_SEE_THE_NAMED = 3415; + +// Brimstone Speaker +const int BRIMSTONE_FIRE_3D6 = 3426; +const int BRIMSTONE_FIRE_5D6 = 3427; +const int BRIMSTONE_FIRE_8D6 = 3428; +const int BRIMSTONE_HEAVEN_LESSER = 3429; +const int BRIMSTONE_HEAVEN_NORMAL = 3430; +const int BRIMSTONE_HEAVEN_GREATER = 3431; + +// Crafted Tool Target Constants +const int CRAFTED_TOOL_QUICKSLOT1 = 3411; +const int CRAFTED_TOOL_QUICKSLOT2 = 3412; +const int CRAFTED_TOOL_QUICKSLOT3 = 3413; +const int CRAFTED_TOOL_QUICKSLOT4 = 3414; + +// AoE Constants for Perfected Map +const int AOE_PER_FOG_VOID_CLOUD = 142; +const int AOE_PER_FOG_VOID_SOLID = 143; +const int AOE_PER_SHIELD_LANDSCAPE = 144; +const int AOE_PER_ENERGY_VORTEX = 145; +const int AOE_PER_SPEAK_ROCK_MUD = 146; +const int AOE_PER_TRANSFORM_LANDSCAPE = 147; +const int AOE_PER_DENY_PASSAGE = 148; \ No newline at end of file diff --git a/src/include/true_utterhook.nss b/src/include/true_utterhook.nss new file mode 100644 index 0000000..36b6b8c --- /dev/null +++ b/src/include/true_utterhook.nss @@ -0,0 +1,157 @@ +//:://///////////////////////////////////////////// +//:: Truenaming Utterance Hook File. +//:: true_utterhook.nss +//::////////////////////////////////////////////// +/* + + This file acts as a hub for all code that + is hooked into the utterance scripts for Truenaming + +*/ +//::////////////////////////////////////////////// +//:: Created By: Stratovarius +//:: Created On: 17-7-2006 +//::////////////////////////////////////////////// + +//#include "prc_x2_craft" +#include "x2_inc_spellhook" +#include "prc_inc_spells" +#include "inc_utility" +#include "prc_inc_itmrstr" + + +// This function holds all functions that are supposed to run before the actual +// spellscript gets run. If this functions returns FALSE, the spell is aborted +// and the spellscript will not run +int TruePreUtterCastCode(); + + +//------------------------------------------------------------------------------ +// if FALSE is returned by this function, the spell will not be cast +// the order in which the functions are called here DOES MATTER, changing it +// WILL break the crafting subsystems +//------------------------------------------------------------------------------ +int TruePreUtterCastCode() +{ + object oTrueSpeaker = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + int nUtterID = PRCGetSpellId(); + int nUtterLevel = GetUtteranceLevel(oTrueSpeaker); + int nTrueSpeakingClass = GetTruespeakingClass(oTrueSpeaker); + int bUtterIsHostile = Get2DACache("spells", "HostileSetting", nUtterID) == "1"; + + int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oTrueSpeaker); + + //--------------------------------------------------------------------------- + // Break any spell require maintaining concentration + //--------------------------------------------------------------------------- + X2BreakConcentrationSpells(); + + //--------------------------------------------------------------------------- + // Check for PRC spell effects + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PRCSpellEffects(oTrueSpeaker, oTarget, nUtterID, nUtterLevel, nTrueSpeakingClass, bUtterIsHostile, -1); + + //--------------------------------------------------------------------------- + // Run Grappling Concentration Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = GrappleConc(oTrueSpeaker, nUtterLevel); + + //--------------------------------------------------------------------------- + // This stuff is only interesting for player characters we assume that use + // magic device always works and NPCs don't use the crafting feats or + // sequencers anyway. Thus, any NON PC spellcaster always exits this script + // with TRUE (unless they are DM possessed or in the Wild Magic Area in + // Chapter 2 of Hordes of the Underdark. + //--------------------------------------------------------------------------- + if(!GetIsPC(oTrueSpeaker) + && !GetPRCSwitch(PRC_NPC_HAS_PC_SPELLCASTING)) + { + if(!GetIsDMPossessed(oTrueSpeaker) && !GetLocalInt(GetArea(oTrueSpeaker), "X2_L_WILD_MAGIC")) + { + return TRUE; + } + } + + //--------------------------------------------------------------------------- + // Run use magic device skill check + //--------------------------------------------------------------------------- + if (nContinue) + { + nContinue = X2UseMagicDeviceCheck(oTrueSpeaker); + } + + //----------------------------------------------------------------------- + // run any user defined spellscript here + //----------------------------------------------------------------------- + if (nContinue) + { + nContinue = X2RunUserDefinedSpellScript(); + } + + //--------------------------------------------------------------------------- + // Check for the new restricted itemproperties + //--------------------------------------------------------------------------- + if(nContinue + && GetIsObjectValid(GetSpellCastItem()) + && !CheckPRCLimitations(GetSpellCastItem(), oTrueSpeaker)) + { + SendMessageToPC(oTrueSpeaker, "You cannot use "+GetName(GetSpellCastItem())); + nContinue = FALSE; + } + + //--------------------------------------------------------------------------- + // The following code is only of interest if an item was targeted + //--------------------------------------------------------------------------- + if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + + //----------------------------------------------------------------------- + // Check if spell was used to trigger item creation feat + //----------------------------------------------------------------------- + if (nContinue) { + nContinue = !ExecuteScriptAndReturnInt("x2_pc_craft", oTrueSpeaker); + } + + //----------------------------------------------------------------------- + // * Execute item OnSpellCast At routing script if activated + //----------------------------------------------------------------------- + if (GetModuleSwitchValue(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS) == TRUE) + { + SetUserDefinedItemEventNumber(X2_ITEM_EVENT_SPELLCAST_AT); + int nRet = ExecuteScriptAndReturnInt(GetUserDefinedItemEventScriptName(oTarget), oTrueSpeaker); + if (nRet == X2_EXECUTE_SCRIPT_END) + { + return FALSE; + } + } + + //----------------------------------------------------------------------- + // Prevent any spell that has no special coding to handle targetting of items + // from being cast on items. We do this because we can not predict how + // all the hundreds spells in NWN will react when cast on items + //----------------------------------------------------------------------- + if (nContinue) { + nContinue = X2CastOnItemWasAllowed(oTarget); + } + } + + //Cleaning spell variables used for holding the charge + if(!GetLocalInt(oTrueSpeaker, "PRC_SPELL_EVENT")) + { + DeleteLocalInt(oTrueSpeaker, "PRC_SPELL_CHARGE_COUNT"); + DeleteLocalInt(oTrueSpeaker, "PRC_SPELL_CHARGE_SPELLID"); + DeleteLocalObject(oTrueSpeaker, "PRC_SPELL_CONC_TARGET"); + DeleteLocalInt(oTrueSpeaker, "PRC_SPELL_METAMAGIC"); + DeleteLocalManifestation(oTrueSpeaker, "PRC_POWER_HOLD_MANIFESTATION"); + DeleteLocalMystery(oTrueSpeaker, "MYST_HOLD_MYST"); + } + else if(GetLocalInt(oTrueSpeaker, "PRC_SPELL_CHARGE_SPELLID") != PRCGetSpellId()) + { //Sanity check, in case something goes wrong with the action queue + DeleteLocalInt(oTrueSpeaker, "PRC_SPELL_EVENT"); + } + + return nContinue; +} \ No newline at end of file diff --git a/src/include/utl_i_sqluuid.nss b/src/include/utl_i_sqluuid.nss new file mode 100644 index 0000000..01b3a98 --- /dev/null +++ b/src/include/utl_i_sqluuid.nss @@ -0,0 +1,668 @@ +//:://///////////////////////////////////////////// +//:: Utility Include: SQLocals Campaign +//:: utl_i_sqluuid.nss +//::////////////////////////////////////////////// +/* + Daz wrote these library functions to act as replacements for the usual local + functions: + * GetLocalInt / SetLocalInt / DeleteLocalInt + * GetLocalFloat / SetLocalFloat / DeleteLocalFloat + * GetLocalString / SetLocalString / DeleteLocalString + * GetLocalObject / SetLocalObject / DeleteLocalObject (NB: remember these are references NOT serialised objects) + * GetLocalLocation / SetLocalLocation / DeleteLocalLocation + * Plus a new function for saving just a vector by itself. + This version stores variables in the campaign DB using the UUID of the object as + the identifier, therefore it suggests only using this for oPlayer since their UUIDs + are persistent after server restarts. + Note for players existing OnClientLeave this is still valid, while the + versions in utl_i_sqlplayer is not. +*/ +//::////////////////////////////////////////////// +//:: Based off of the nwscript_utility_scripts project; see for dates/creator info +//:: https://github.com/Finaldeath/nwscript_utility_scripts +//::////////////////////////////////////////////// + +const string SQLLOCALUUID_DATABASE_NAME = "sqllocalsuuid_db"; + +const string SQLOCALSUUID_TABLE_NAME = "sqlocalsuuid_table"; + +const int SQLOCALSUUID_TYPE_ALL = 0; +const int SQLOCALSUUID_TYPE_INT = 1; +const int SQLOCALSUUID_TYPE_FLOAT = 2; +const int SQLOCALSUUID_TYPE_STRING = 4; +const int SQLOCALSUUID_TYPE_OBJECT = 8; +const int SQLOCALSUUID_TYPE_VECTOR = 16; +const int SQLOCALSUUID_TYPE_LOCATION = 32; + +// Returns an integer stored in the campaign DB for oPlayer, or 0 on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +int SQLocalsUUID_GetInt(object oPlayer, string sVarName); +// Sets an integer stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to set +// * nValue - Value to store +void SQLocalsUUID_SetInt(object oPlayer, string sVarName, int nValue); +// Deletes an integer stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteInt(object oPlayer, string sVarName); + +// Returns a float stored in the campaign DB for oPlayer, or 0.0 on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +float SQLocalsUUID_GetFloat(object oPlayer, string sVarName); +// Sets a float stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to set +// * fValue - Value to store +void SQLocalsUUID_SetFloat(object oPlayer, string sVarName, float fValue); +// Deletes a float stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteFloat(object oPlayer, string sVarName); + +// Returns an string stored in the campaign DB for oPlayer, or "" on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +string SQLocalsUUID_GetString(object oPlayer, string sVarName); +// Sets a string stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to set +// * sValue - Value to store +void SQLocalsUUID_SetString(object oPlayer, string sVarName, string sValue); +// Deletes a string stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteString(object oPlayer, string sVarName); + +// Returns an object identifier stored in the campaign DB for oPlayer +// If this is used on a player it might return a "once valid" OID, so check +// with GetIsObjectValid, do not compare to OBJECT_INVALID. +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +object SQLocalsUUID_GetObject(object oPlayer, string sVarName); +// Sets an object identifier stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to set +// * oValue - Value to store +void SQLocalsUUID_SetObject(object oPlayer, string sVarName, object oValue); +// Deletes an object identifier stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteObject(object oPlayer, string sVarName); + +// Returns a vector stored in the campaign DB for oPlayer, or [0.0, 0.0, 0.0] on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +vector SQLocalsUUID_GetVector(object oPlayer, string sVarName); +// Sets a vector stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to set +// * vValue - Value to store +void SQLocalsUUID_SetVector(object oPlayer, string sVarName, vector vValue); +// Deletes a vector stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteVector(object oPlayer, string sVarName); + +// Returns a location stored in the campaign DB for oPlayer, or the starting location of the module on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +location SQLocalsUUID_GetLocation(object oPlayer, string sVarName); +// Sets a location stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to set +// * lValue - Value to store +void SQLocalsUUID_SetLocation(object oPlayer, string sVarName, location lValue); +// Deletes a location stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteLocation(object oPlayer, string sVarName); + +// Deletes a set of locals stored in the campaign DB for oPlayer matching the given criteria +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * nType - The SQLOCALSUUID_TYPE_* you wish to remove (default: SQLOCALSUUID_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +void SQLocalsUUID_Delete(object oPlayer, int nType = SQLOCALSUUID_TYPE_ALL, string sLike = "", string sEscape = ""); +// Counts a set of locals stored in the campaign DB for oPlayer matching the given criteria +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * nType - The SQLOCALSUUID_TYPE_* you wish to count (default: SQLOCALSUUID_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +int SQLocalsUUID_Count(object oPlayer, int nType = SQLOCALSUUID_TYPE_ALL, string sLike = "", string sEscape = ""); +// Checks a locals stored in the campaign DB for oPlayer is set +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSUUID_TYPE_* you wish to check +int SQLocalsUUID_IsSet(object oPlayer, string sVarName, int nType); +// Returns the last Unix time the given variable was updated +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSUUID_TYPE_* you wish to check +int SQLocalsUUID_GetLastUpdated_UnixEpoch(object oPlayer, string sVarName, int nType); +// Returns the last UTC time the given variable was updated +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSUUID_TYPE_* you wish to check +string SQLocalsUUID_GetLastUpdated_UTC(object oPlayer, string sVarName, int nType); + + +/* INTERNAL */ +void SQLocalsUUID_CreateTable() +{ + sqlquery sql = SqlPrepareQueryCampaign(SQLLOCALUUID_DATABASE_NAME, + "CREATE TABLE IF NOT EXISTS " + SQLOCALSUUID_TABLE_NAME + " (" + + "type INTEGER, " + + "uuid TEXT, " + + "varname TEXT, " + + "value TEXT, " + + "timestamp INTEGER, " + + "PRIMARY KEY(type, uuid, varname));"); + SqlStep(sql); +} + +sqlquery SQLocalsUUID_PrepareSelect(object oPlayer, int nType, string sVarName) +{ + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryCampaign(SQLLOCALUUID_DATABASE_NAME, + "SELECT value FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE type = @type AND uuid = @uuid AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + SqlBindString(sql, "@varname", sVarName); + + return sql; +} + +sqlquery SQLocalsUUID_PrepareInsert(object oPlayer, int nType, string sVarName) +{ + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryCampaign(SQLLOCALUUID_DATABASE_NAME, + "INSERT INTO " + SQLOCALSUUID_TABLE_NAME + " " + + "(type, uuid, varname, value, timestamp) VALUES (@type, @uuid, @varname, @value, strftime('%s','now')) " + + "ON CONFLICT (type, uuid, varname) DO UPDATE SET value = @value, timestamp = strftime('%s','now');"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + SqlBindString(sql, "@varname", sVarName); + + return sql; +} + +sqlquery SQLocalsUUID_PrepareDelete(object oPlayer, int nType, string sVarName) +{ + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryCampaign(SQLLOCALUUID_DATABASE_NAME, + "DELETE FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE type = @type AND uuid = @uuid AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + SqlBindString(sql, "@varname", sVarName); + + return sql; +} + +string SQLocalsUUID_LocationToString(location locLocation) +{ + string sAreaId = ObjectToString(GetAreaFromLocation(locLocation)); + vector vPosition = GetPositionFromLocation(locLocation); + float fFacing = GetFacingFromLocation(locLocation); + + return "#A#" + sAreaId + + "#X#" + FloatToString(vPosition.x, 0, 5) + + "#Y#" + FloatToString(vPosition.y, 0, 5) + + "#Z#" + FloatToString(vPosition.z, 0, 5) + + "#F#" + FloatToString(fFacing, 0, 5) + "#"; +} + +location SQLocalsUUID_StringToLocation(string sLocation) +{ + location locLocation; + + int nLength = GetStringLength(sLocation); + + if(nLength > 0) + { + int nPos, nCount; + + nPos = FindSubString(sLocation, "#A#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + object oArea = StringToObject(GetSubString(sLocation, nPos, nCount)); + + nPos = FindSubString(sLocation, "#X#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fX = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + nPos = FindSubString(sLocation, "#Y#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fY = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + nPos = FindSubString(sLocation, "#Z#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fZ = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + vector vPosition = Vector(fX, fY, fZ); + + nPos = FindSubString(sLocation, "#F#") + 3; + nCount = FindSubString(GetSubString(sLocation, nPos, nLength - nPos), "#"); + float fOrientation = StringToFloat(GetSubString(sLocation, nPos, nCount)); + + if (GetIsObjectValid(oArea)) + locLocation = Location(oArea, vPosition, fOrientation); + else + locLocation = GetStartingLocation(); + } + + return locLocation; +} +/* **** */ + +/* INT */ + +// Returns an integer stored in the campaign DB for oPlayer, or 0 on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +int SQLocalsUUID_GetInt(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return 0; + + sqlquery sql = SQLocalsUUID_PrepareSelect(oPlayer, SQLOCALSUUID_TYPE_INT, sVarName); + + if (SqlStep(sql)) + return SqlGetInt(sql, 0); + else + return 0; +} + +// Sets an integer stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nValue - Value to store +void SQLocalsUUID_SetInt(object oPlayer, string sVarName, int nValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareInsert(oPlayer, SQLOCALSUUID_TYPE_INT, sVarName); + SqlBindInt(sql, "@value", nValue); + SqlStep(sql); +} + +// Deletes an integer stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteInt(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareDelete(oPlayer, SQLOCALSUUID_TYPE_INT, sVarName); + SqlStep(sql); +} +/* **** */ + +/* FLOAT */ + +// Returns a float stored in the campaign DB for oPlayer, or 0.0 on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +float SQLocalsUUID_GetFloat(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return 0.0f; + + sqlquery sql = SQLocalsUUID_PrepareSelect(oPlayer, SQLOCALSUUID_TYPE_FLOAT, sVarName); + + if (SqlStep(sql)) + return SqlGetFloat(sql, 0); + else + return 0.0f; +} + +// Sets a float stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * fValue - Value to store +void SQLocalsUUID_SetFloat(object oPlayer, string sVarName, float fValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareInsert(oPlayer, SQLOCALSUUID_TYPE_FLOAT, sVarName); + SqlBindFloat(sql, "@value", fValue); + SqlStep(sql); +} + +// Deletes a float stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteFloat(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareDelete(oPlayer, SQLOCALSUUID_TYPE_FLOAT, sVarName); + SqlStep(sql); +} +/* **** */ + +/* STRING */ + +// Returns an string stored in the campaign DB for oPlayer, or "" on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +string SQLocalsUUID_GetString(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return ""; + + sqlquery sql = SQLocalsUUID_PrepareSelect(oPlayer, SQLOCALSUUID_TYPE_STRING, sVarName); + + if (SqlStep(sql)) + return SqlGetString(sql, 0); + else + return ""; +} + +// Sets a string stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * sValue - Value to store +void SQLocalsUUID_SetString(object oPlayer, string sVarName, string sValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareInsert(oPlayer, SQLOCALSUUID_TYPE_STRING, sVarName); + SqlBindString(sql, "@value", sValue); + SqlStep(sql); +} + +// Deletes a string stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteString(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareDelete(oPlayer, SQLOCALSUUID_TYPE_STRING, sVarName); + SqlStep(sql); +} +/* **** */ + +/* OBJECT */ + + +// Returns an object identifier stored in the campaign DB for oPlayer +// If this is used on a player it might return a "once valid" OID, so check +// with GetIsObjectValid, do not compare to OBJECT_INVALID. +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +object SQLocalsUUID_GetObject(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return OBJECT_INVALID; + + sqlquery sql = SQLocalsUUID_PrepareSelect(oPlayer, SQLOCALSUUID_TYPE_OBJECT, sVarName); + + if (SqlStep(sql)) + return StringToObject(SqlGetString(sql, 0)); + else + return OBJECT_INVALID; +} + +// Sets an object identifier stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * oValue - Value to store +void SQLocalsUUID_SetObject(object oPlayer, string sVarName, object oValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareInsert(oPlayer, SQLOCALSUUID_TYPE_OBJECT, sVarName); + SqlBindString(sql, "@value", ObjectToString(oValue)); + SqlStep(sql); +} + +// Deletes an object identifier stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteObject(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareDelete(oPlayer, SQLOCALSUUID_TYPE_OBJECT, sVarName); + SqlStep(sql); +} +/* **** */ + +/* VECTOR */ + +// Returns a vector stored in the campaign DB for oPlayer, or [0.0, 0.0, 0.0] on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +vector SQLocalsUUID_GetVector(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return [0.0f, 0.0f, 0.0f]; + + sqlquery sql = SQLocalsUUID_PrepareSelect(oPlayer, SQLOCALSUUID_TYPE_VECTOR, sVarName); + + if (SqlStep(sql)) + return SqlGetVector(sql, 0); + else + return [0.0f, 0.0f, 0.0f]; +} + +// Sets a vector stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * vValue - Value to store +void SQLocalsUUID_SetVector(object oPlayer, string sVarName, vector vValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareInsert(oPlayer, SQLOCALSUUID_TYPE_VECTOR, sVarName); + SqlBindVector(sql, "@value", vValue); + SqlStep(sql); +} + +// Deletes a vector stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteVector(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareDelete(oPlayer, SQLOCALSUUID_TYPE_VECTOR, sVarName); + SqlStep(sql); +} +/* **** */ + +/* LOCATION */ + +// Returns a location stored in the campaign DB for oPlayer, or the starting location of the module on error +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +location SQLocalsUUID_GetLocation(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return GetStartingLocation(); + + sqlquery sql = SQLocalsUUID_PrepareSelect(oPlayer, SQLOCALSUUID_TYPE_LOCATION, sVarName); + + if (SqlStep(sql)) + return SQLocalsUUID_StringToLocation(SqlGetString(sql, 0)); + else + return GetStartingLocation(); +} + +// Sets a location stored in the campaign DB for oPlayer to the given value +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * lValue - Value to store +void SQLocalsUUID_SetLocation(object oPlayer, string sVarName, location lValue) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareInsert(oPlayer, SQLOCALSUUID_TYPE_LOCATION, sVarName); + SqlBindString(sql, "@value", SQLocalsUUID_LocationToString(lValue)); + SqlStep(sql); +} + +// Deletes a location stored in the campaign DB for oPlayer +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to delete +void SQLocalsUUID_DeleteLocation(object oPlayer, string sVarName) +{ + if (!GetIsPC(oPlayer) || sVarName == "") return; + + sqlquery sql = SQLocalsUUID_PrepareDelete(oPlayer, SQLOCALSUUID_TYPE_LOCATION, sVarName); + SqlStep(sql); +} +/* **** */ + +/* UTILITY */ + +// Deletes a set of locals stored in the campaign DB for oPlayer matching the given criteria +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * nType - The SQLOCALSUUID_TYPE_* you wish to remove (default: SQLOCALSUUID_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +void SQLocalsUUID_Delete(object oPlayer, int nType = SQLOCALSUUID_TYPE_ALL, string sLike = "", string sEscape = "") +{ + if (!GetIsPC(oPlayer) || nType < 0) return; + + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryCampaign(SQLLOCALUUID_DATABASE_NAME, + "DELETE FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE uuid = @uuid" + + (nType != SQLOCALSUUID_TYPE_ALL ? "AND type & @type " : " ") + + (sLike != "" ? "AND varname LIKE @like " + (sEscape != "" ? "ESCAPE @escape" : "") : "") + + ";"); + + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + + if (nType != SQLOCALSUUID_TYPE_ALL) + SqlBindInt(sql, "@type", nType); + if (sLike != "") + { + SqlBindString(sql, "@like", sLike); + + if (sEscape != "") + SqlBindString(sql, "@escape", sEscape); + } + + SqlStep(sql); +} + +// Counts a set of locals stored in the campaign DB for oPlayer matching the given criteria +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * nType - The SQLOCALSUUID_TYPE_* you wish to count (default: SQLOCALSUUID_TYPE_ALL) +// * sLike - The string to compare with the SQL "like" comparison +// * sEscape - The escape character to use with the SQL "escape" keyword +int SQLocalsUUID_Count(object oPlayer, int nType = SQLOCALSUUID_TYPE_ALL, string sLike = "", string sEscape = "") +{ + if (!GetIsPC(oPlayer) || nType < 0) return 0; + + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT COUNT(*) FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE uuid = @uuid " + + (nType != SQLOCALSUUID_TYPE_ALL ? "AND type & @type " : " ") + + (sLike != "" ? "AND varname LIKE @like " + (sEscape != "" ? "ESCAPE @escape" : "") : "") + + ";"); + + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + + if (nType != SQLOCALSUUID_TYPE_ALL) + SqlBindInt(sql, "@type", nType); + if (sLike != "") + { + SqlBindString(sql, "@like", sLike); + + if (sEscape != "") + SqlBindString(sql, "@escape", sEscape); + } + + if (SqlStep(sql)) + return SqlGetInt(sql, 0); + else + return 0; +} + +// Checks a locals stored in the campaign DB for oPlayer is set +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSUUID_TYPE_* you wish to check (default: SQLOCALSUUID_TYPE_ALL) +int SQLocalsUUID_IsSet(object oPlayer, string sVarName, int nType) +{ + if (!GetIsPC(oPlayer) || nType < 0) return 0; + + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT * FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE uuid = @uuid " + + (nType != SQLOCALSUUID_TYPE_ALL ? "AND type & @type " : " ") + + "AND varname = @varname;"); + + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + + if (nType != SQLOCALSUUID_TYPE_ALL) + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@varname", sVarName); + + return SqlStep(sql); +} + +// Returns the last Unix time the given variable was updated +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSUUID_TYPE_* you wish to check (default: SQLOCALSUUID_TYPE_ALL) +int SQLocalsUUID_GetLastUpdated_UnixEpoch(object oPlayer, string sVarName, int nType) +{ + if (!GetIsPC(oPlayer) || nType <= 0) return 0; + + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT timestamp FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE type = @type " + + "AND uuid = @uuid" + + "AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + SqlBindString(sql, "@varname", sVarName); + + if (SqlStep(sql)) + return SqlGetInt(sql, 0); + else + return 0; +} + +// Returns the last UTC time the given variable was updated +// * oPlayer - a player object (uses GetObjectUUID to identify) +// * sVarName - name of the variable to retrieve +// * nType - The SQLOCALSUUID_TYPE_* you wish to check (default: SQLOCALSUUID_TYPE_ALL) +string SQLocalsUUID_GetLastUpdated_UTC(object oPlayer, string sVarName, int nType) +{ + if (!GetIsPC(oPlayer) || nType <= 0) return ""; + + SQLocalsUUID_CreateTable(); + + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "SELECT datetime(timestamp, 'unixepoch') FROM " + SQLOCALSUUID_TABLE_NAME + " " + + "WHERE type = @type " + + "AND uuid = @uuid" + + "AND varname = @varname;"); + + SqlBindInt(sql, "@type", nType); + SqlBindString(sql, "@uuid", GetObjectUUID(oPlayer)); + SqlBindString(sql, "@varname", sVarName); + + if (SqlStep(sql)) + return SqlGetString(sql, 0); + else + return ""; +} \ No newline at end of file diff --git a/src/include/x0_i0_transport.nss b/src/include/x0_i0_transport.nss new file mode 100644 index 0000000..bd3c288 --- /dev/null +++ b/src/include/x0_i0_transport.nss @@ -0,0 +1,302 @@ +//::////////////////////////////////////////////////// +//:: X0_I0_TRANSPORT +//:: Copyright (c) 2002 Floodgate Entertainment +//::////////////////////////////////////////////////// +/* + +Functions for making creatures travel/transport to new locations. + + */ +//::////////////////////////////////////////////////// +//:: Created By: Naomi Novik +//:: Created On: 09/12/2002 +//::////////////////////////////////////////////////// + +/********************************************************************** + * CONSTANTS + **********************************************************************/ + + +/********************************************************************** + * FUNCTION PROTOTYPES + **********************************************************************/ + + +// Target goes to specified destination object intelligently. +// If location is in same area, walk (or run) there. +// If location is in different area, walk (or run) to +// most appropriate door, area transition, or waypoint, +// then jump. +// If either of these fail, jump after fDelay seconds. +void TravelToObject(object oDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0); + +// Target goes to specified location intelligently. See +// TravelToObject for description. +void TravelToLocation(location lDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0); + +// Find nearest exit to target (either door or waypoint). +object GetNearestExit(object oTarget=OBJECT_SELF); + +// Find best exit based on desired target area +object GetBestExit(object oTarget=OBJECT_SELF, object oTargetArea=OBJECT_INVALID); + +// Transport a player and his/her associates to a waypoint. +// This does NOT transport the rest of the player's party, +// only their henchman, summoned, dominated, etc. +void TransportToWaypoint(object oPC, object oWaypoint); + +// Transport a player and his/her associates to a location. +// This does NOT transport the rest of the player's party, +// only their henchman, summoned, dominated, etc. +void TransportToLocation(object oPC, location oLoc); + +// Transport an entire party to a waypoint +void TransportAllToWaypoint(object oPC, object oWaypoint); + +// Transport an entire party to a location +void TransportAllToLocation(object oPC, location lLoc); + + + +/********************************************************************** + * FUNCTION PROTOTYPES + **********************************************************************/ + +// Target goes to specified destination object intelligently. +// If location is in same area, walk (or run) there. +// If location is in different area, walk (or run) to +// nearest waypoint or door, then jump. +// If either of these fail, jump after a timeout. +void TravelToObject(object oDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0) +{ + TravelToLocation(GetLocation(oDest), oTarget, bRun, fDelay); +} + +// Target goes to specified location intelligently. See +// TravelToObject for description. +void TravelToLocation(location lDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0) +{ + object oDestArea = GetAreaFromLocation(lDest); + if (oDestArea == GetArea(oTarget)) { + AssignCommand(oTarget, + ActionForceMoveToLocation(lDest, bRun, fDelay)); + } else { + object oBestExit = GetBestExit(oTarget, oDestArea); + AssignCommand(oTarget, + ActionForceMoveToObject(oBestExit, bRun, 1.0, fDelay)); + int nObjType = GetObjectType(oBestExit); + if (nObjType == OBJECT_TYPE_DOOR) { + AssignCommand(oTarget, ActionOpenDoor(oBestExit)); + } + AssignCommand(oTarget, + ActionJumpToLocation(lDest)); + } + AssignCommand(oTarget, DelayCommand(fDelay, ClearAllActions())); + AssignCommand(oTarget, DelayCommand(fDelay, JumpToLocation(lDest))); +} + +// Find nearest exit to target (either door or trigger or +// (failing those) waypoint). +object GetNearestExit(object oTarget=OBJECT_SELF) +{ + object oCurArea = GetArea(oTarget); + + object oNearDoor = GetNearestObject(OBJECT_TYPE_DOOR, oTarget); + if (GetArea(oNearDoor) != oCurArea) + oNearDoor = OBJECT_INVALID; + + // Find nearest area transition trigger + int nTrig = 1; + object oNearTrig = GetNearestObject(OBJECT_TYPE_TRIGGER, oTarget); + while (GetIsObjectValid(oNearTrig) + && GetArea(oNearTrig) == oCurArea + && !GetIsObjectValid(GetTransitionTarget(oNearTrig))) + { + nTrig++; + oNearTrig = GetNearestObject(OBJECT_TYPE_TRIGGER, oTarget, nTrig); + } + if (GetArea(oNearTrig) != oCurArea) + oNearTrig = OBJECT_INVALID; + + float fMaxDist = 10000.0; + float fDoorDist = fMaxDist; + float fTrigDist = fMaxDist; + + if (GetIsObjectValid(oNearDoor)) { + fDoorDist = GetDistanceBetween(oNearDoor, oTarget); + } + if (GetIsObjectValid(oNearTrig)) { + fTrigDist = GetDistanceBetween(oNearTrig, oTarget); + } + + if (fTrigDist < fDoorDist) + return oNearTrig; + + if (fDoorDist < fTrigDist || fDoorDist < fMaxDist) + return oNearDoor; + + // No door/area transition -- use waypoint as a backup exit + return GetNearestObject(OBJECT_TYPE_WAYPOINT, oTarget); +} + +// Private function: find the best exit of the desired type. +object GetBestExitByType(object oTarget=OBJECT_SELF, object oTargetArea=OBJECT_INVALID, int nObjType=OBJECT_TYPE_DOOR) +{ + object oCurrentArea = GetArea(oTarget); + int nDoor = 1; + + object oDoor = GetNearestObject(nObjType, oTarget); + object oNearestDoor = oDoor; + object oDestArea = OBJECT_INVALID; + + object oBestDoor = OBJECT_INVALID; + object oBestDestArea = OBJECT_INVALID; + + while (GetIsObjectValid(oDoor) && GetArea(oDoor) == oCurrentArea) { + oDestArea = GetArea(GetTransitionTarget(oDoor)); + + // If we find a door that leads to the target + // area, use it + if (oDestArea == oTargetArea) { + return oDoor; + } + + // If we find a door that leads to a different area, + // that might be good if we haven't already found one + // that leads to the desired area, or a closer door + // that leads to a different area. + if (oDestArea != oCurrentArea && !GetIsObjectValid(oBestDoor)) { + oBestDoor = oDoor; + } + + // try the next door + nDoor++; + oDoor = GetNearestObject(nObjType, oTarget, nDoor); + } + + // If we found a door that leads to a different area, + // return that one. + if (GetIsObjectValid(oBestDoor)) + return oBestDoor; + + // Otherwise, return the nearest, if it's in this area. + if (GetArea(oNearestDoor) == oCurrentArea) + return oNearestDoor; + + return OBJECT_INVALID; +} + + +// Find best exit based on desired target area +object GetBestExit(object oTarget=OBJECT_SELF, object oTargetArea=OBJECT_INVALID) +{ + if (!GetIsObjectValid(oTargetArea)) + return GetNearestExit(oTarget); + + // Try and find a door + object oBestDoor = GetBestExitByType(oTarget, + oTargetArea, + OBJECT_TYPE_DOOR); + + if (GetIsObjectValid(oBestDoor)) { + if (GetTransitionTarget(oBestDoor) == oTargetArea) { + return oBestDoor; + } + } + + // Try and find a trigger + object oBestTrigger = GetBestExitByType(oTarget, + oTargetArea, + OBJECT_TYPE_TRIGGER); + if (GetIsObjectValid(oBestTrigger)) { + if (GetTransitionTarget(oBestTrigger) == oTargetArea) { + return oBestTrigger; + } + } + + if (GetIsObjectValid(oBestDoor)) + return oBestDoor; + + if (GetIsObjectValid(oBestTrigger)) + return oBestTrigger; + + return GetNearestExit(oTarget); + +} + + +// Transport a player and his/her associates to a waypoint. +// This does NOT transport the rest of the player's party, +// only their henchman, summoned, dominated, etc. +void TransportToWaypoint(object oPC, object oWaypoint) +{ + if (!GetIsObjectValid(oWaypoint)) { + return; + } + TransportToLocation(oPC, GetLocation(oWaypoint)); +} + +// Transport a player and his/her associates to a location. +// This does NOT transport the rest of the player's party, +// only their henchman, summoned, dominated, etc. +void TransportToLocation(object oPC, location oLoc) +{ + // Jump the PC + AssignCommand(oPC, ClearAllActions()); + AssignCommand(oPC, JumpToLocation(oLoc)); + + // Not a PC, so has no associates + if (!GetIsPC(oPC)) + return; + + // Get all the possible associates of this PC + object oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC); + object oDomin = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + object oFamil = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + object oAnimalComp = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + + // Jump any associates + if (GetIsObjectValid(oHench)) { + AssignCommand(oHench, ClearAllActions()); + AssignCommand(oHench, JumpToLocation(oLoc)); + } + if (GetIsObjectValid(oDomin)) { + AssignCommand(oDomin, ClearAllActions()); + AssignCommand(oDomin, JumpToLocation(oLoc)); + } + if (GetIsObjectValid(oFamil)) { + AssignCommand(oFamil, ClearAllActions()); + AssignCommand(oFamil, JumpToLocation(oLoc)); + } + if (GetIsObjectValid(oSummon)) { + AssignCommand(oSummon, ClearAllActions()); + AssignCommand(oSummon, JumpToLocation(oLoc)); + } + if (GetIsObjectValid(oAnimalComp)) { + AssignCommand(oAnimalComp, ClearAllActions()); + AssignCommand(oAnimalComp, JumpToLocation(oLoc)); + } +} + + +// Transport an entire party with all associates to a waypoint. +void TransportAllToWaypoint(object oPC, object oWaypoint) +{ + if (!GetIsObjectValid(oWaypoint)) { + return; + } + TransportAllToLocation(oPC, GetLocation(oWaypoint)); +} + +// Transport an entire party with all associates to a location. +void TransportAllToLocation(object oPC, location lLoc) +{ + object oPartyMem = GetFirstFactionMember(oPC, TRUE); + while (GetIsObjectValid(oPartyMem)) { + TransportToLocation(oPartyMem, lLoc); + oPartyMem = GetNextFactionMember(oPC, TRUE); + } + TransportToLocation(oPC, lLoc); +} + diff --git a/src/include/x2_inc_cutscenep.nss b/src/include/x2_inc_cutscenep.nss new file mode 100644 index 0000000..02be07d --- /dev/null +++ b/src/include/x2_inc_cutscenep.nss @@ -0,0 +1,2135 @@ +//Modified by Primogenitor +/* +Error in bioware original due to mismatch +betweeen CutCreateObjectCopy definition and +implementation. Bioware compiler doesnt see +it, but PRC one does. +*/ + + +/////////////////////////////////////////////////////// +//:: Name - x2_inc_cutscene +//:: Copyright (c) 2001 Bioware Corp. +/////////////////////////////////////////////////////// +/* + This is the cutscene include file that contains many + useful functions when doing cutscenes. The uses are + described before each function. + + Each function will be in a wrapper function that will + check to see if the cutscene has been aborted yet or + not. + + There is an added variable called iShift. This can be + ignored in all cases, except when the GetShift function + is being called. It was added so that when a shift is + done, you can insert stuff (the reason for the shift) + into your script without the new stuff being shifted. + + An example of this: You decide you need to add 20 + seconds of material in the middle of your cutscene. + You need to do this at the 1 minute mark, and you + want to shift everything after that point by 20 secs. + The new stuff you add there for that 20 secs will + get shifted as well since you called the shift. But + by adding FALSE at the end of the function call, it + will ignore the shift amount. + + Also, if anything after the shift is something that + you don't want adjusted from the default, use this + there as well. +*/ +/////////////////////////////////////////////////////// +//:: Created By: Brad Prince +//:: Created On: Dec 9, 2002 +/////////////////////////////////////////////////////// + +// Used to shift the entire cutscene a length of time. Used internally here, +// but very handy for cutscenes that are complete, but need to be changed +// after the fact. From the point it is called, it just shifts the cutscene +// accordingly. Can be used (and should be) for non-cutscene delays in a +// script (ie - commands that aren't here or regular DelayCommands in a script.) + +//UPDATE Jan 8/03 :Keith Warner: The integer nCutscene is now passed in to all cutscene functions. +//This is so that if a player skips one cutscene and then very soon after enters another +//cutscene, we need to be able to check which cutscene a delayed function belongs to. +//If it is not the cutscene that the PC is currently in - the delayed actions that effect the +//PC should NOT take place + +// UPDATE MAY 27, 2003: Yaron Jakobs: +// - Using the CutSetActiveCutscene() function at the begining +// of each cutscene, instead of giving a number for each line. For example: +// call CutSetActiveCutscene(1) and then any Cut* functions that follow would assume cutscene +// number 1 is valid and would compare that number to the value that is set on the player as the +// active cutscene (if you use ExecuteScript or SignalEvent in the middle of a cutscene, you should +// run the CutSetActiveCutscene() function again right at the returning line). +// - Any object that uses any of the delayed Cut* functions must have it's cutscene value be set by +// the CutSetActiveCutsceneForObject() function. This prevents any delayed actions to be taken after +// a cutscene has been aborted. You would need to handle any specific actions that should still +// happen in the abort script. +// - Added a fade length variable to the CutFadeOutAndIn() function. +// - Changed FreezeAssociate and UnfreezeAssociate to be a part of the SetCutsceneMode function. +// - Added more CutAction* functions +// - Added CutStoreMusic(), CutRestoreMusic() and CutSetMusic() functions +// - the CutRestoreLocation (and any other CutRestore* function) does not require a cutscene +// number parameter anymore, as this function can run even after a cutscene has been aborted so +// there is no need to make a check with this num. +// - Added CutSetTile*** function to change tile colors +// - Added CutSetAbortDelay(). Use this function at the begining of a cutscene to set the +// delay you want for the execution of all cutscene related functions for players only. +// The corresponding function CutGetAbortDelay() fetches the value in the abort script +// and applies it for all the needed functions (defaults the 0). This delay is counted right +// after pressing the abort button. +// - Added CutSetDestroyCopyDelay(). Functions much the same as CutSetAbortDelay(), although +// this one is responsible to delay the removal of the pc copy (if there is one) (defaults to 0). +// - Added CutCreatePCCopy() and CutDestroyPCCopy(). +// - Objects created by CutCreateObject would have their cutscene number set to the current +// cutscene number, unless the default parameter of SetActive is FALSE (default value of TRUE). +// - Added CutDisableCutscene() function. This function should be called at the end of a cutscene +// and it is also called in the generic abort script for the module. +// - Added CutDisableAbort() function to be used in a begining of a function that should not +// be abortable. +// - Added support for cumulative delay (give only the delay between each line). +// - Added CutGetConvDuration() to get dialog duration from a 2da (multi-lang support) +// - CutSpeakStringByStrRef() now also calls PlaySoundByStrRef internally. +// - Add CutBeginConversation to handle dialog without the initiating object running to the other object + +// UPDATE JUNE 25 - Yaron Jakobs: +// - Added an option to specify fade speed to all fade functions. +// - Destroying a PC copy would jump the player to his original location. + +#include "inc_utility" + +// Use this function at the begining of a cutscene for each object involved in the cutscene. +// Notice that this function would fail if oObject already has another cutscene value (any +// number greater than 0). The function returns 0 on success, -1 otherwise. +int CutSetActiveCutsceneForObject(object oObject, int nCutNum, int bMainPC = FALSE); + +float GetShift(object oObject, int iShift); + + +int CUT_DELAY_TYPE_CUMULATIVE = 0; +int CUT_DELAY_TYPE_CONSTANT = 1; +// Set the current active cutscene. Any following delayed Cut* functions would run only if the +// initiating object has the same cutscene number as the global active one. A value of -1 +// means that no cutscene is active (possible values for bDelayType: CUT_DELAY_TYPE_CUMULATIVE, CUT_DELAY_TYPE_CONSTANT. +// bSetCutsceneObject should be set to FALSE whenever calling this function more than once for the +// same cutscene but in another file. This would keep the original cutscene object that created +// all the cutscene effect so it would be possible to remove them when the cutscene ends. +void CutSetActiveCutscene(int nCutsceneNumber, int nDelayType, int bSetCutsceneObject = TRUE); + +// Returns current global cutscene number. +int GetActiveCutsceneNum(); + +// Used to clear commandable state FALSE in the event there are too many +// Used in this include only. +void RemoveCommandable(object oPC); + +// Plays voice chat nChatID for oObject. +void CutPlayVoiceChat(float fDelay, object oObject , int nChatID, int iShift = TRUE); + +// Used to remove all effects from an object. +// Example: CutRemoveEffects(10.5, GetObjectByTag("guard1")) would remove all effects from guard1 after a 10.5 second delay. +// Notice that only effects created for the cutscene would be removed. +void CutRemoveEffects(float fDelay, object oObject, int iShift = TRUE); + +// Jumps all associates of oPC to lLoc +void CutJumpAssociateToLocation(float fDelay, object oPC, location lLoc, int iShift = TRUE); + +// Used for a conversation file when you need to have an NPC speak +// via a conversation instead of bubble text. The fDelay is used +// when timing is important. +// +///Example: CutActionStartConversation(5.0, oNPC, oPC, "my_conv"); would start the +// conversation file "my_conv" of the NPC, after a 5 sec delay, and +// the conversation subject would be the PC. +void CutActionStartConversation(float fDelay, object oNPC, object oPC, string szConversationFile, int iShift = TRUE); + +// Use this instead of CutActionStartConversation() to start a dialog +// without one object running to the other. +void CutBeginConversation(float fDelay, object oTalker, object oTalkTo, string sConvFile, int iShift = TRUE); + +// oAttacker would attack oAttackee. +void CutActionAttack(float fDelay, object oAttacker, object oAttackee, int bPassive = FALSE, int iShift = TRUE); + +void CutActionCloseDoor(float fDelay, object oCloser, object oDoor, int iShift = TRUE); + +void CutActionEquipItem(float fDelay, object oObject, object oItem, int InvSlot, int iShift = TRUE); + +void CutActionUnequipItem(float fDelay, object oObject, object oItem, int iShift = TRUE); + +void CutActionForceFollowObject(float fDelay, object oObject, object oFollow, float fFollowDistance = 0.0, int iShift = TRUE); + +void CutActionLockObject(float fDelay, object oObject, object oTarget, int iShift = TRUE); + +void CutActionUnLockObject(float fDelay, object oObject, object oTarget, int iShift = TRUE); + +void CutActionMoveAwayFromLocation(float fDelay, object oObject, location lLoc, int bRun = FALSE, float fRange = 40.0, int iShift = TRUE); + +void CutActionMoveAwayFromObject(float fDelay, object oObject, object oTarget, int bRun = FALSE, float fRange = 40.0, int iShift = TRUE); + +void CutActionOpenDoor(float fDelay, object oObject, object oDoor, int iShift = TRUE); + +void CutActionSit(float fDelay, object oObject, object oChair, int iShift = TRUE); + +void CutSpeakString(float fDelay, object oSpeaker, string szString, int iShift = TRUE); + +void CutSpeakStringByStrRef(float fDelay, object oSpeaker, int nStrRef, int iShift = TRUE); + +void CutPlayAnimation(float fDelay, object oObject, int nAnimation, float fLength, int iShift = TRUE); + +void CutJumpToLocation(float fDelay, object oPC, location lLoc, int iShift = TRUE); + +void CutJumpToObject(float fDelay, object oPC, object oObject, int iShift = TRUE); + +void CutActionMoveToObject(float fDelay, object oPC, object oTarget, int iRun, int iShift = TRUE); + +void CutActionMoveToLocation(float fDelay, object oPC, location lLoc, int iRun, int iShift = TRUE); + +void CutActionForceMoveToObject(float fDelay, object oPC, object oTarget, int iRun = FALSE, float fRange = 1.0, float fTimeout = 30.0, int iShift = TRUE); + +void CutActionForceMoveToLocation(float fDelay, object oPC, location lLoc, int iRun = FALSE, float fTimeout = 30.0, int iShift = TRUE); + +// Would create an object after the delay. Please avoid using this function as it is currently impossible +// to cancel action of objects created by it after aborting the cutscene. +void CutCreateObject(float fDelay, object oPC, int iType, string sName, location lLoc, int iEffect, int nSetActive = TRUE, int iShift = TRUE); + +void CutSetFacingPoint(float fDelay, object oPC, string szTag, int iShift = TRUE); + +// Combines fade-out and fade-in into one function. +void CutFadeOutAndIn(float fDelay, object oObject, float fFadeLen = 4.3, float nFadeSpeed = FADE_SPEED_FASTEST, int iShift = TRUE); + +void CutFadeToBlack(float fDelay, object oObject, float nFadeSpeed = FADE_SPEED_FASTEST, int iShift = TRUE); + +void CutFadeFromBlack(float fDelay, object oObject, float nFadeSpeed = FADE_SPEED_FASTEST, int iShift = TRUE); + +void CutSetCamera(float fDelay, object oObject, int iCameraType, float fFacing, float fZoom, float fPitch, int nSpeed, int iShift = TRUE); + +void CutClearAllActions(float fDelay, object oObject, int nClearCombatState, int iShift = TRUE); + +void CutApplyEffectAtLocation(float fDelay, object oObject, int iDur, int iEffect, location lLoc, float fDur = 0.0, int iShift = TRUE); + +// Applies visual effect iEffect to oObject. +// If you want to apply non-visual effect use CutApplyEffectToObject2 +void CutApplyEffectToObject(float fDelay, int iDur, int iEffect, object oObject, float fDur = 0.0, int iShift = TRUE); + +// Applies eEffect to Object. +void CutApplyEffectToObject2(float fDelay, int iDur, effect eEffect, object oObject, float fDur = 0.0, int iShift = TRUE); + +void CutKnockdown(float fDelay, object oObject, float fDur, int iShift = TRUE); + +void CutDeath(float fDelay, object oObject, int iSpec, int iShift = TRUE); + +void CutActionCastFakeSpellAtObject(float fDelay, int iSpell, object oObject, object oTarget, int iPath = PROJECTILE_PATH_TYPE_DEFAULT, int iShift = TRUE); + +void CutActionCastFakeSpellAtLocation(float fDelay, int iSpell, object oObject, location lLoc, int iPath = PROJECTILE_PATH_TYPE_DEFAULT, int iShift = TRUE); + +void CutActionCastSpellAtObject(float fDelay, int iSpell, object oObject, object oTarget, int nMetaMagic=METAMAGIC_ANY, int bCheat=FALSE, int nDomainLevel=0, int iPath=PROJECTILE_PATH_TYPE_DEFAULT, int bInstantSpell=FALSE, int iShift = TRUE); + +void CutActionCastSpellAtLocation(float fDelay, int iSpell, object oObject, location lLoc, int METAMAGIC = METAMAGIC_ANY, int nCheat = FALSE, int nDomainLevel = 0, int iPath = PROJECTILE_PATH_TYPE_DEFAULT, int iShift = TRUE); + +// Stores the current location of oPC to be restored later with CutRestoreLocation. +void CutSetLocation(float fDelay, object oPC, int iShift = TRUE); + +// Restores the location that had been stored by CutSetLocation. +void CutRestoreLocation(float fDelay, object oPC, int iShift = TRUE); + +const int VANISH = 2; +const int CUT_CAMERA_HEIGHT_VERY_LOW = 2; +const int CUT_CAMERA_HEIGHT_LOW = 3; +const int CUT_CAMERA_HEIGHT_MEDIUM = 4; +const int CUT_CAMERA_HEIGHT_HIGH = 5; +const int CUT_CAMERA_HEIGHT_VERY_HIGH = 6; + +// Used to turn cutscene mode on or off. +// iValue: TRUE to start cutscene, and FALSE to exit cutscene mode. +// bInv: TRUE - to make the player invisible. +// FASLE - would leave the player visible. +// CUT_CAMERA_HEIGHT_VERY_LOW - would turn the playe invisible and put the camera at a very low position. +// CUT_CAMERA_HEIGHT_LOW - would turn the playe invisible and put the camera at a low position. +// CUT_CAMERA_HEIGHT_MEDIUM - would turn the playe invisible and put the camera at a low position. +// CUT_CAMERA_HEIGHT_HIGH would turn the playe invisible and put the camera at a high position. +// CUT_CAMERA_HEIGHT_VERY_HIGH would turn the playe invisible and put the camera at a very high position. +// bKeepAssociates: would destroy all associate when set to FALSE. +// bFreezeAssociates would freeze all associates when set to TRUE, and turn them invisible when set to VANISH = 2. +void CutSetCutsceneMode(float fDelay, object oPC, int iValue, int bInv, int bKeepAssociates = TRUE, int bFreezeAssociates = TRUE, int iShift = TRUE); + +void CutSetPlotFlag(float fDelay, object oObject, int iValue, int iShift = TRUE); + +void CutDestroyObject(float fDelay, object oObject, int iShift = TRUE); + +// Stores camera facing for oPC to be restored by CutRestoreCameraFacing. +void CutStoreCameraFacing(float fDelay, object oPC, int iShift = TRUE); + +// Restored camera facing that has been stored by CutStoreCameraFacing. +void CutRestoreCameraFacing(float fDelay, object oPC, int iShift = TRUE); + +void CutBlackScreen(float fDelay, object oPC, int iShift = TRUE); + +void CutStopFade(float fDelay, object oPC, int iShift = TRUE); + +void CutPlaySound(float fDelay, object oPC, string szSound, int iShift = TRUE); + +// Sets the track nTrack as the music for the current area of oPC and plays it. +void CutSetMusic(float fDelay, object oPC, int nTrack, int iShift = TRUE); + +// Stores the current music track for the area where oPC is. Use CutRestoreMusic to reset the music for the area +void CutStoreMusic(float fDelay, object oPC, int iShift = TRUE); + +// Restores the music track for the area where oPC is which was stores by CutStoreMusic. +void CutRestoreMusic(float fDelay, object oPC, int iShift = TRUE); + +// Sets the track nTrack as the music for the current area of oPC and plays it. +void CutSetAmbient(float fDelay, object oPC, int nTrack, int iShift = TRUE); + +// Sets the main tile color for the tile at lLoc. +void CutSetTileMainColor(float fDelay, object oPC, location lLoc, int nMainColor1, int nMainColor2, int iShift = TRUE); + +// Sets the source tile color for the tile at lLoc. +void CutSetTileSourceColor(float fDelay, object oPC, location lLoc, int nSourceColor1, int nSourceColor2, int iShift = TRUE); + +void CutSetWeather(float fDelay, object oPC, int nWeather, int iShift = TRUE); + +// Sets the delay between pressing the ESC key to actually doing the cutscene cleanup. +// This function should be used at the beginning of a cutscene only if such delay is required. +// Otherwise, there would be no cleanup delay. +void CutSetAbortDelay(int nCutscene, float fDelay); + +// Retrieves the current abort delay for nCutscene after aborting. +float CutGetAbortDelay(int nCutscene); + +// Sets the delay between pressing the ESC key to actually destroying the PC copy. +// This function should be used at the beginning of a cutscene only if such delay is required. +// Otherwise, there would be no delay. +void CutSetDestroyCopyDelay(int nCutscene, float fDelay); + +// Retrieves the current destroy PC delay after aborting a cutscene. +float CutGetDestroyCopyDelay(int nCutscene); + +// Creates a copy of oPC at lLoc with tag sTag. Older copies of oPC would be destroyed if any. +object CutCreatePCCopy(object oPC, location lLoc, string sTag); + +// Creates a copy of oPC at lLoc with tag sTag. Older copies of oPC would be destroyed if any. +void CutCreateObjectCopy(float fDelay, object oObject, location lLoc, string sTag = "", int iShift = TRUE); + +// Destroys the copy of oPC and restores the PC's original location if bRestorePCLocation is TRUE. +void CutDestroyPCCopy(float fDelay, object oPC, int bRestorePCLocation = TRUE, int iShift = TRUE); + +const int RESTORE_TYPE_NONE = 0; +const int RESTORE_TYPE_NORMAL = 1; +const int RESTORE_TYPE_COPY = 2; +// Disables a cutscene. All generic disable functions would be called after a delay of fCleanupDelay, +// and any PC copy object would be destroyed after a delay of fDestPCCopyDelay). +// If using the CUT_DELAY_TYPE_CUMULATIVE delay type, then each delay should be in relation to +// the previous delay (independent delays). nRestoreType should have one of the following values: +// - RESTORE_TYPE_NONE: Do not jump the player. +// - RESTORE_TYPE_NORMAL: Jump the player to the last place that he used CutSetLocation() at. +// - RESTORE_TYPE_COPY: Jupm the player to the place where the copy was created. +void CutDisableCutscene(int nCutscene, float fCleanupDelay, float fDestPCCopyDelay, int nRestoreType = RESTORE_TYPE_NORMAL); + +// Disables the possibility to disable nCutscene. Pressing ESC would do nothing after calling this function. +void CutDisableAbort(int nCutscene); + +// returns TRUE if it is not possible to abort nCutscene, FALSE otherwise. +int CutGetIsAbortDisabled(int nCutscene); + +// Get the duration of dialog sConvName from a 2da. A value of 0.0 is returned on error. +// The value in the 2da should be set when first knowing the english length of the dialog. +float CutGetConvDuration(string sConvName); + +// This adjusts Faction Reputation, how the entire faction that +// oSourceFactionMember is in, feels about oTarget. +void CutAdjustReputation(float fDelay, object oTarget, object oSource, int nAdjustment, int iShift = TRUE); + +// Sets the current movement rate factor of +// the cutscene camera-man (oPC) +// fMovementRateFactor: Factor ranging between 0.1 and 2.0 +void CutSetCameraSpeed(float fDelay, object oPC, float fMovementRateFactor, int iShift = TRUE); + +void UnFreezeAssociate(object oPlayers); + +float CutGetConvDuration(string sConvName) +{ + // first, get the row of sConvName + int nRow = 0; + string sName = Get2DACache("des_cutconvdur", "Dialog", nRow); + while(sName != "") + { + if(sName == sConvName) // found the dialog we need, current nRow has the right value + return StringToFloat(Get2DACache("des_cutconvdur", "Duration", nRow)); + nRow++; + sName = Get2DACache("des_cutconvdur", "Dialog", nRow); + } + return 0.0; // error value +} + +void CutSetActiveCutscene(int nCutsceneNumber, int nDelayType, int bSetCutsceneObject = TRUE) +{ + // Storing the delay type. + SetLocalInt(GetModule(), "X2_DelayType" + IntToString(nCutsceneNumber), nDelayType); + // Setting the active object, which is the object that applies all the effects for this cutscene. + // This object is used in the cutscene abort script and at the end of the cutscene to remove all + // the effects that this object had created. + if(bSetCutsceneObject == TRUE) + SetLocalObject(GetModule(), "X2_Cut" + IntToString(nCutsceneNumber) + "ActiveObject", OBJECT_SELF); + SetLocalInt(GetModule(), "X2_ActiveCutsceneNumber", nCutsceneNumber); +} + +int GetActiveCutsceneNum() +{ + return GetLocalInt(GetModule(), "X2_ActiveCutsceneNumber"); +} + +// Calculates the "real" delay to execute a cut* action (can be a comulative delay or a constant one) +float CutCalculateCurrentDelay(float fDelayModifier, int nCutsceneNumber) +{ + if(GetLocalInt(GetModule(), "X2_DelayType" + IntToString(nCutsceneNumber)) == CUT_DELAY_TYPE_CONSTANT) + return fDelayModifier; // support for old system - leaving the delay the same + // new system - each delay is the difference from the previous one. + string sDelayVariable = "X2_fCutscene" + IntToString(nCutsceneNumber) + "Delay"; + float fCurrentDelay = GetLocalFloat(GetModule(), sDelayVariable); + fCurrentDelay = fCurrentDelay + fDelayModifier; + SetLocalFloat(GetModule(), sDelayVariable, fCurrentDelay); + return fCurrentDelay; +} + +float GetShift(object oObject, int iShift) +{ + float fShift; + if(iShift != FALSE) + fShift = GetLocalFloat(GetArea(oObject), "cut_shift"); + else + fShift = 0.0; + return fShift; +} + +// flagging a cutscene as non-abortable. This function should be used at the begining of +// a cutscene (probably for short cutscenes). +void CutDisableAbort(int nCutscene) +{ + SetLocalInt(GetModule(), "X2_CutAbortDisabled" + IntToString(nCutscene), 1); +} + +int CutGetIsAbortDisabled(int nCutscene) +{ + return GetLocalInt(GetModule(), "X2_CutAbortDisabled" + IntToString(nCutscene)); +} + +int CutSetActiveCutsceneForObject(object oObject, int nCutNum, int bMainPC = FALSE) +{ + /* + // familiar check (unpossess and bring the pc here) + if(GetIsPossessedFamiliar(oObject)) + { + PrintString("BOOM: object is familiar"); + object oPC = GetMaster(oObject); + AssignCommand(oPC, JumpToObject(oObject)); + UnpossessFamiliar(oObject); + oObject = oPC; + }*/ + + // if trying to set a new cutscene number for a player and the old value is not zero + // than the oObject is already in another cutscene and returning with failure. + if(GetIsPC(oObject) && nCutNum != 0 && GetLocalInt(oObject, "nCutsceneNumber") != 0) + return -1; + + SetLocalInt(oObject, "nCutsceneNumber", nCutNum); + if(bMainPC == TRUE) + SetLocalInt(oObject, "nCutMainPC", 1); + else + SetLocalInt(oObject, "nCutMainPC", 0); + + + if(nCutNum == 0 || GetIsPC(oObject)) + return 0; // Not storing an object if disabling active cutscene (value of 0) or this is a pc + + // Storing the object, so that the generic abort could find it and reset it. + // First, getting the global index for this cutscene (virtual array). + int nCurrentIndex = GetLocalInt(GetModule(), "X2_CutsceneObjectsIndex" + IntToString(nCutNum)); + // Next, storing the object. + SetLocalObject(GetModule(), + "X2_Cutscene" + IntToString(nCutNum) + "Object" + IntToString(nCurrentIndex), oObject); + // Finally, updating the index. + nCurrentIndex++; + SetLocalInt(GetModule(), "X2_CutsceneObjectsIndex" + IntToString(nCutNum), nCurrentIndex); + return 0; +} + +int CutGetActiveCutsceneForObject(object oObject) +{ + return GetLocalInt(oObject, "nCutsceneNumber"); +} + +// This function is used internally by the generic abort script. +// It finds all active objects for this cutscene and resets them so they won't execute any +// more actions. +void CutResetActiveObjectsForCutscene(int nCutscene) +{ + // Getting the size of the virtual array that stores the cutscene objects. + // This would return an index for the "next object to be stored", so there would be no + // object in that index + int nMaxIndex = GetLocalInt(GetModule(), "X2_CutsceneObjectsIndex" + IntToString(nCutscene)); + int i; + object oCurrentObject; + // Iterating through all the cutscene objects + for(i = 0; i < nMaxIndex; i++) + { + oCurrentObject = GetLocalObject(GetModule(), + "X2_Cutscene" + IntToString(nCutscene) + "Object" + IntToString(i)); + CutSetActiveCutsceneForObject(oCurrentObject, 0); + } + // Initializing the index + SetLocalInt(GetModule(), "X2_CutsceneObjectsIndex" + IntToString(nCutscene), 0); +} + +void CallRestorePCCopyLocation(int nCutscene, object oPC) +{ + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + location lLoc = GetLocalLocation(oPC, "X2_PCLocation"); + DeleteLocalLocation(oPC, "X2_PCLocation"); + AssignCommand(oPC, JumpToLocation(lLoc)); + } +} + +void CallRemoveEffects(int nCutscene, object oObject) +{ + if(CutGetActiveCutsceneForObject(oObject) == nCutscene) + { // first, get the object that created all the effects + object oCreator = GetLocalObject(GetModule(), "X2_Cut" + IntToString(nCutscene) + "ActiveObject"); + + effect eEff = GetFirstEffect(oObject); + while(GetIsEffectValid(eEff)) + { + if( GetEffectCreator(eEff) == oCreator) + RemoveEffect(oObject, eEff); + eEff = GetNextEffect(oObject); + } + } +} + +void CutRemoveEffects(float fDelay, object oObject, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallRemoveEffects(nCutscene, oObject))); +} + +void CallRemoveAssociatesEffects(int nCutscene, object oPC) +{ + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + object oAnimal = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + object oDominated = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + object oSummoned = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + CutSetActiveCutsceneForObject(oAnimal, nCutscene); + CutSetActiveCutsceneForObject(oDominated, nCutscene); + CutSetActiveCutsceneForObject(oFamiliar, nCutscene); + CutSetActiveCutsceneForObject(oSummoned, nCutscene); + if(oAnimal != OBJECT_INVALID) + CallRemoveEffects(nCutscene, oAnimal); + if(oDominated != OBJECT_INVALID) + CallRemoveEffects(nCutscene, oDominated); + if(oFamiliar != OBJECT_INVALID) + CallRemoveEffects(nCutscene, oFamiliar); + if(oSummoned != OBJECT_INVALID) + CallRemoveEffects(nCutscene, oSummoned); + + int i = 1; + object oHenchman = GetHenchman(oPC, i); + while(oHenchman != OBJECT_INVALID) + { + CutSetActiveCutsceneForObject(oHenchman, nCutscene); + CallRemoveEffects(nCutscene, oHenchman); + i++; + oHenchman = GetHenchman(oPC, i); + } + } +} + +void CallJumpAssociateToLocation(int nCutscene, object oPC, location lLoc) +{ + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + UnFreezeAssociate(oPC); + object oAnimal = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + object oDominated = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + object oSummoned = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + if(oAnimal != OBJECT_INVALID) + AssignCommand(oAnimal, ActionJumpToLocation(lLoc)); + if(oDominated != OBJECT_INVALID) + AssignCommand(oDominated, ActionJumpToLocation(lLoc)); + if(oFamiliar != OBJECT_INVALID) + AssignCommand(oFamiliar, ActionJumpToLocation(lLoc)); + if(oSummoned != OBJECT_INVALID) + AssignCommand(oSummoned, ActionJumpToLocation(lLoc)); + + int i = 1; + object oHenchman = GetHenchman(oPC, i); + while(oHenchman != OBJECT_INVALID) + { + AssignCommand(oHenchman, ActionJumpToLocation(lLoc)); + i++; + oHenchman = GetHenchman(oPC, i); + } + } +} + +void CutJumpAssociateToLocation(float fDelay, object oPC, location lLoc, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallJumpAssociateToLocation(nCutscene, oPC, lLoc))); +} + + +void CallDestroyPCCopy(int nCutscene, object oPC, int bRestorePCLocation) +{ + object oCopy = GetLocalObject(oPC, "X2_PCCopy" + IntToString(nCutscene)); + if(oCopy == OBJECT_INVALID) + return; + if(bRestorePCLocation == TRUE) + { + CallRestorePCCopyLocation(nCutscene, oPC); + } + + SetPlotFlag(oCopy, FALSE); + DestroyObject(oCopy); +} + +// Destroys the copy of oPC. This function would work even if there is no valid cutscene for oPC. +// If bRestorePCLocation is TRUE then the PC would be jumped to original location. +void CutDestroyPCCopy(float fDelay, object oPC, int bRestorePCLocation = TRUE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallDestroyPCCopy(nCutscene, oPC, bRestorePCLocation))); +} + +// Creates a copy of the pc at lLoc (and destroys an old one, if exists. +object CutCreatePCCopy(object oPC, location lLoc, string sTag) +{ + int nCutscene = GetActiveCutsceneNum(); + // first, destroy an old copy, if exists + CallDestroyPCCopy(nCutscene, oPC, FALSE); + // next, create the new copy. + object oNewPC = CopyObject(oPC, lLoc, OBJECT_INVALID, sTag); + //SetPlotFlag(oNewPC, TRUE); + CutSetActiveCutsceneForObject(oNewPC, nCutscene); + SetLocalObject(oPC, "X2_PCCopy" + IntToString(nCutscene), oNewPC); + SetLocalLocation(oPC, "X2_PCLocation", GetLocation(oPC)); // Keeping location of PC so it can be restored when the copy is destroyed + ChangeToStandardFaction(oNewPC, STANDARD_FACTION_COMMONER); + SetActionMode(oNewPC, ACTION_MODE_STEALTH, FALSE); + SetActionMode(oNewPC, ACTION_MODE_DETECT, FALSE); + return oNewPC; +} + + +void CallCreateObjectCopy(int nCutscene, object oObject, location lLoc, string sTag) +{ + if(CutGetActiveCutsceneForObject(oObject) == nCutscene) + { + CopyObject(oObject, lLoc, OBJECT_INVALID, sTag); + } +} + +void CutCreateObjectCopy(float fDelay, object oObject, location lLoc, string sTag = "", int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallCreateObjectCopy(nCutscene, oObject, lLoc, sTag))); +} + +// returns TRUE whether the pc is the main pc for his current cutscene, FALSE otherwise. +int CutGetIsMainPC(object oPC) +{ + if(GetLocalInt(oPC, "nCutsceneNumber") == 0) + return FALSE; // a player is not a part of any cutscene + return GetLocalInt(oPC, "nCutMainPC"); +} + +// set delay for removal of pc copy in the generic abort script (should be used +// at the begining of a cutscene) +void CutSetDestroyCopyDelay(int nCutscene, float fDelay) +{ + SetLocalFloat(GetModule(), "X2_CutDestroyCopyDelay" + IntToString(nCutscene), fDelay); +} + +float CutGetDestroyCopyDelay(int nCutscene) +{ + return GetLocalFloat(GetModule(), "X2_CutDestroyCopyDelay" + IntToString(nCutscene)); +} + +void CutSetAbortDelay(int nCutscene, float fDelay) +{ + SetLocalFloat(GetModule(), "X2_CutAbortDelay" + IntToString(nCutscene), fDelay); +} + +// get the delay for cutscene-disable funcions in the generic abort script (used only there) +float CutGetAbortDelay(int nCutscene) +{ + return GetLocalFloat(GetModule(), "X2_CutAbortDelay" + IntToString(nCutscene)); +} + +void RemoveAssociateEffects(object oCreature) +{ + int nCutscene = GetActiveCutsceneNum(); + effect eEff1 = GetFirstEffect(oCreature); + object oCreator = GetLocalObject(GetModule(), "X2_Cut" + IntToString(nCutscene) + "ActiveObject"); + + while(GetIsEffectValid(eEff1)) + { + if (GetEffectCreator(eEff1) == oCreator) + { + RemoveEffect(oCreature, eEff1); + } + eEff1 = GetNextEffect(oCreature); + } +} + +object FreezeAssociate(object oPlayers, int bVanish) +{ + effect eAssociate = EffectCutsceneParalyze(); + effect eInv = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + //Cutscene Paralize any associates. + int i = 1; + object oHench = GetHenchman(oPlayers, i); + while(oHench != OBJECT_INVALID) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAssociate, oHench); + if(bVanish) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInv, oHench); + i++; + oHench = GetHenchman(oPlayers, i); + } + + object oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPlayers); + if (oCompanion != OBJECT_INVALID) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAssociate, oCompanion); + if(bVanish) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInv, oCompanion); + } + + object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPlayers); + if (oFamiliar != OBJECT_INVALID) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAssociate, oFamiliar); + if(bVanish) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInv, oFamiliar); + } + + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPlayers); + if (oSummon != OBJECT_INVALID) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAssociate, oSummon); + if(bVanish) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInv, oSummon); + } + + object oDominated = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPlayers); + if (oDominated != OBJECT_INVALID) + { + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAssociate, oDominated); + if(bVanish) + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInv, oDominated); + + } + return oDominated; +} +void UnFreezeAssociate(object oPlayers) +{ + //Cutscene Paralize any associates. + int i = 1; + object oHench = GetHenchman(oPlayers, i); + while(oHench != OBJECT_INVALID) + { + RemoveAssociateEffects(oHench); + i++; + oHench = GetHenchman(oPlayers, i); + } + + object oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPlayers); + if (oCompanion != OBJECT_INVALID) + RemoveAssociateEffects(oCompanion); + object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPlayers); + if (oFamiliar != OBJECT_INVALID) + RemoveAssociateEffects(oFamiliar); + object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPlayers); + if (oSummon != OBJECT_INVALID) + RemoveAssociateEffects(oSummon); + object oDominated = GetLocalObject(oPlayers, "oDominated"); + if (oDominated != OBJECT_INVALID) + RemoveAssociateEffects(oDominated); +} + +void RemoveCommandable(object oPC) +{ + while(GetCommandable(oPC) == FALSE) + SetCommandable(TRUE, oPC); +} + +// Helper function +void Talk(string sConvFile, object oTalkTo) +{ + BeginConversation(sConvFile, oTalkTo); +} + +void CallBeginConversation(int nCutscene, object oTalker, object oTalkTo, string sConvFile) +{ + if(nCutscene == GetLocalInt(oTalker, "nCutsceneNumber")) + { + AssignCommand(oTalker, Talk(sConvFile, oTalkTo)); + } +} + +void CutBeginConversation(float fDelay, object oTalker, object oTalkTo, string sConvFile, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oTalker, iShift), CallBeginConversation(nCutscene, oTalker, oTalkTo, sConvFile))); +} + +void CallActionStartConversation(int nCutscene, object oNPC, object oPC, string szConversationFile) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + //CutRemoveEffects(0.0, oPC); + //SetCommandable(TRUE, oPC); + AssignCommand(oNPC, ActionStartConversation(oPC, szConversationFile)); + //SetCommandable(FALSE, oPC); + //ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneDominated(), oPC); + } + +} + +void CutActionStartConversation(float fDelay, object oNPC, object oPC, string szConversationFile, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallActionStartConversation(nCutscene, oNPC, oPC, szConversationFile))); +} + +// Used for bubble text type speaking. +/* Example: CutSpeakString(0.0, GetObjectByTag("drow_priest"), "I like + green eggs and ham."); would have the object drow_priest speak the + line "I like green eggs and ham." after no delay. +*/ +void CallSpeakString(int nCutscene, object oSpeaker, string szString) +{ + if(nCutscene == GetLocalInt(oSpeaker, "nCutsceneNumber")) + { + AssignCommand(oSpeaker, SpeakString(szString)); + } +} + +void CallSpeakStringByStrRef(int nCutscene, object oSpeaker, int nStrRef) +{ + if(nCutscene == GetLocalInt(oSpeaker, "nCutsceneNumber")) + { + AssignCommand(oSpeaker, SpeakStringByStrRef(nStrRef)); + AssignCommand(oSpeaker, PlaySoundByStrRef(nStrRef, FALSE)); + } +} + +void CutSpeakString(float fDelay, object oSpeaker, string szString, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oSpeaker, iShift), CallSpeakString(nCutscene, oSpeaker, szString))); +} + +void CutSpeakStringByStrRef(float fDelay, object oSpeaker, int nStrRef, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oSpeaker, iShift), CallSpeakStringByStrRef(nCutscene, oSpeaker, nStrRef))); +} + + +// Used if you need a cutscene participant to do an animation. +/* Example: CutPlayAnimation(26.0, oPC, ANIMATION_FIREFORGET_BOW, 3.0); + would have the PC bow for 3 seconds after a 26 second delay. +*/ +void CallPlayAnimation(int nCutscene, object oObject, int nAnimation, float fLength) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, PlayAnimation(nAnimation, 1.0, fLength)); + } +} + +void CutPlayAnimation(float fDelay, object oObject, int nAnimation, float fLength, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallPlayAnimation(nCutscene, oObject, nAnimation, fLength))); +} + +// Used to jump the PC to a location. +/* Example: CutJumpToLocation(20.0, oPC, GetLocation(GetWaypointByTag + ("wp_jump")); would jump the PC to the wp_jump waypoint after a 20 + second delay. +*/ +void CallJumpToLocation(int nCutscene, object oPC, location lLoc) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + AssignCommand(oPC, ActionJumpToLocation(lLoc)); + } +} + +void CutJumpToLocation(float fDelay, object oPC, location lLoc, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallJumpToLocation(nCutscene, oPC, lLoc))); +} + + +// Used to jump the PC or NPC to an object. +/* Example: CutJumpToObject(20.0, oPC, GetObject(GetObjectByTag + ("invis_object")); would jump the PC to the invis_object after a 20 + second delay. +*/ +void CallJumpToObject(int nCutscene, object oPC, object oObject) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + if(GetCommandable(oPC) == FALSE) + { + //SetCommandable(TRUE, oPC); + AssignCommand(oPC, JumpToObject(oObject)); + //AssignCommand(oPC, JumpToObject(oObject)); + //SetCommandable(FALSE, oPC); + } + else + AssignCommand(oPC, JumpToObject(oObject)); + + } +} + +void CutJumpToObject(float fDelay, object oPC, object oObject, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallJumpToObject(nCutscene, oPC, oObject))); +} + +// Used to force move the PC or NPC to an object. +void CallActionForceMoveToObject(int nCutscene, object oPC, object oTarget, int iRun, float fRange, float fTimeout) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + if(GetCommandable(oPC) == FALSE) + { + SetCommandable(TRUE); + AssignCommand(oPC, ActionForceMoveToObject(oTarget, iRun, fRange, fTimeout)); + SetCommandable(FALSE); + } + else + AssignCommand(oPC, ActionForceMoveToObject(oTarget, iRun, fRange, fTimeout)); + } +} + +void CutActionForceMoveToObject(float fDelay, object oPC, object oTarget, int iRun = FALSE, float fRange = 1.0, float fTimeout = 30.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallActionForceMoveToObject(nCutscene, oPC, oTarget, iRun, fRange, fTimeout))); +} + +// Used to move the PC or NPC to an object. +/* Example: CutActionMoveToObject(2.0, oPC, oTable, TRUE); would have the + PC run to oTable after a 2 second delay. TRUE = run, FALSE = walk. +*/ +void CallActionMoveToObject(int nCutscene, object oPC, object oTarget, int iRun) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + if(GetCommandable(oPC) == FALSE) + { + SetCommandable(TRUE); + AssignCommand(oPC, ActionMoveToObject(oTarget, iRun)); + SetCommandable(FALSE); + } + else + AssignCommand(oPC, ActionMoveToObject(oTarget, iRun)); + } +} + +void CutActionMoveToObject(float fDelay, object oPC, object oTarget, int iRun, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallActionMoveToObject(nCutscene, oPC, oTarget, iRun))); +} + +// Used to force move the PC or NPC to a location. +void CallActionForceMoveToLocation(int nCutscene, object oPC, location lLoc, int iRun, float fTimeout) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + if(GetCommandable(oPC) == FALSE) + { + SetCommandable(TRUE); + AssignCommand(oPC, ActionForceMoveToLocation(lLoc, iRun, fTimeout)); + SetCommandable(FALSE); + } + else + AssignCommand(oPC, ActionForceMoveToLocation(lLoc, iRun, fTimeout)); + } +} + +void CutActionForceMoveToLocation(float fDelay, object oPC, location lLoc, int iRun = FALSE, float fTimeout = 30.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallActionForceMoveToLocation(nCutscene, oPC, lLoc, iRun, fTimeout))); +} + + +// Used to move the PC or NPC to a location. +/* Example: CutActionMoveToLocation(2.0, oPC, lTable, FALSE); would have the + PC walk to lTable after a 2 second delay. TRUE = run, FALSE = walk. +*/ +void CallActionMoveToLocation(int nCutscene, object oPC, location lLoc, int iRun) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + if(GetCommandable(oPC) == FALSE) + { + SetCommandable(TRUE); + AssignCommand(oPC, ActionMoveToLocation(lLoc, iRun)); + SetCommandable(FALSE); + } + else + AssignCommand(oPC, ActionMoveToLocation(lLoc, iRun)); + } +} + +void CutActionMoveToLocation(float fDelay, object oPC, location lLoc, int iRun, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallActionMoveToLocation(nCutscene, oPC, lLoc, iRun))); +} + + +// Used to delay object create. Pass the type, the sTemplate, the location, +// and the effect you wish to have appear when the object is created +// (0 for no effect). The PC is also passed to check cutscene abort. +/* EXAMPLE: CutCreateObject(2.3, oPC, OBJECT_TYPE_PLACEABLE, "resref", lLoc, VFX_FNF_HOLY_STRIKE); + would create a placeable with the resref of "resref" at the location + lLoc after 2.3 seconds, with the VFX_FNF_HOLY_STRIKE effect. +*/ +void CallCreateObject(int nCutscene, int iType, object oPC, string sName, location lLoc, int iEffect, int nSetActive) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(iEffect), lLoc); + object oObject = CreateObject(iType, sName, lLoc); + if(nSetActive == TRUE) + { + CutSetActiveCutsceneForObject(oObject, nCutscene); + } + } + +} + +void CutCreateObject(float fDelay, object oPC, int iType, string sName, location lLoc, int iEffect, int nSetActive = TRUE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallCreateObject(nCutscene, iType, oPC, sName, lLoc, iEffect, nSetActive))); +} + + +// Used to set the facing of a creature. +/* EXAMPLE CutSetFacingPoint(22.5, oPC, "creature_tag"); would + have the PC face "creature_tag" object after a 22.5 second delay. +*/ +void CallSetFacingPoint(int nCutscene, object oPC, string szTag) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + AssignCommand(oPC, SetFacingPoint(GetPosition(GetObjectByTag(szTag)))); + } +} + +void CutSetFacingPoint(float fDelay, object oPC, string szTag, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetFacingPoint(nCutscene, oPC, szTag))); +} + +void CallAdjustReputation(int nCutscene, object oTarget, object oSource, int nAdjustment) +{ + if(nCutscene == GetLocalInt(oTarget, "nCutsceneNumber")) + { + AdjustReputation(oTarget, oSource, nAdjustment); + if(GetIsPossessedFamiliar(oTarget)) + { + AdjustReputation(GetMaster(oTarget), oSource, nAdjustment); + } + else if(GetIsPossessedFamiliar(oSource)) + { + AdjustReputation(oSource, GetMaster(oTarget), nAdjustment); + } + } +} + +void CutAdjustReputation(float fDelay, object oTarget, object oSource, int nAdjustment, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oTarget, iShift), CallAdjustReputation(nCutscene, oTarget, oSource, nAdjustment))); +} + + +// Used to fade out and back in, with a black screen section of +// fFadeLen seconds in between. +/* EXAMPLE: CallFadeOutAndIn(35.0, 2.0, oPC); would Fade the screen + out and back in on the PC, after a delay of 35 seconds. +*/ +void CallFadeOutAndIn(int nCutscene, object oObject, float fFadeLen, float fFadeSpeed) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + FadeToBlack(oObject, fFadeSpeed); + //DelayCommand(2.3, BlackScreen(oObject)); + DelayCommand(fFadeLen, FadeFromBlack(oObject, fFadeSpeed)); + } +} + +void CutFadeOutAndIn(float fDelay, object oObject, float fFadeLen = 4.3, float fFadeSpeed = FADE_SPEED_FASTEST, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallFadeOutAndIn(nCutscene, oObject, fFadeLen, fFadeSpeed))); +} + +// Used to fade out. +/* EXAMPLE: CallFadeToBlack(35.0, oPC); would Fade the screen + out on the PC, after a delay of 35 seconds. +*/ +void CallFadeToBlack(int nCutscene, object oObject, float fFadeSpeed) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + FadeToBlack(oObject, fFadeSpeed); + } +} + +void CutFadeToBlack(float fDelay, object oObject, float fFadeSpeed = FADE_SPEED_FASTEST, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallFadeToBlack(nCutscene, oObject, fFadeSpeed))); +} + +// Used to fade in. +/* EXAMPLE: CallFadeFromBlack(35.0, oPC); would Fade the screen + in on the PC, after a delay of 35 seconds. +*/ +void CallFadeFromBlack(int nCutscene, object oObject, float fFadeSpeed) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + FadeFromBlack(oObject, fFadeSpeed); + } +} + +void CutFadeFromBlack(float fDelay, object oObject, float fFadeSpeed = FADE_SPEED_FASTEST, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallFadeFromBlack(nCutscene, oObject, fFadeSpeed))); +} + +// Used to set the camera for cutscene dramatics. +/* EXAMPLE: To set a camera with the following settings on the PC, + after a 30.0 second delay: + CAMERA_MODE_TOP_DOWN + Facing = 170.0 + Zoom = 5.0 + Pitch = 50.0 + CAMERA_TRANSITION_TYPE_MEDIUM + + CutSetCamera(30.0, oPC, CAMERA_MODE_TOP_DOWN, 170.0, 5.0, 50.0, + CAMERA_TRANSITION_TYPE_MEDIUM); +*/ +void CallSetCamera(int nCutscene, object oObject, int iCameraType, float fFacing, float fZoom, float fPitch, int nSpeed) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + SetCameraMode(oObject, iCameraType); + DelayCommand(0.1, AssignCommand(oObject, SetCameraFacing(fFacing, fZoom, fPitch, nSpeed))); + } +} + +void CutSetCamera(float fDelay, object oObject, int iCameraType, float fFacing, float fZoom, float fPitch, int nSpeed, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallSetCamera(nCutscene, oObject, iCameraType, fFacing, fZoom, fPitch, nSpeed))); +} + +// Used to clear the actions of the subject. +/* EXAMPLE: CutClearAllActions(3.2, GetObjectByTag("guard")); + would clear the actions of "guard" after a 3.2 second delay. +*/ +void CallClearAllActions(int nCutscene, object oObject, int nClearCombatState = FALSE) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ClearAllActions(nClearCombatState)); + } +} + +void CutClearAllActions(float fDelay, object oObject, int nClearCombatState = FALSE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallClearAllActions(nCutscene, oObject, nClearCombatState))); +} + +// Used to apply a visual effect at a location. The PC is passed to +// determine if the cutscene has been aborted or not. +/* EXAMPLE: CutApplyEffectAtLocation(98.3, oPC, DURATION_TYPE_INSTANT, + VXF_FNF_HOLY_STRIKE, lLoc, 0.0); + would have a holy strike visual appear at lLoc after a 98.3 second delay. + The duration is instant, lasting the default time. +*/ +void CallApplyEffectAtLocation(int nCutscene, object oObject, int iDur, int iEffect, location lLoc, float fDur) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + effect eEffect = EffectVisualEffect(iEffect); + ApplyEffectAtLocation(iDur, eEffect, lLoc, fDur); + } + +} + +void CutApplyEffectAtLocation(float fDelay, object oObject, int iDur, int iEffect, location lLoc, float fDur = 0.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallApplyEffectAtLocation(nCutscene, oObject, iDur, iEffect, lLoc, fDur))); +} + +// Used to apply a visual effect to an object. +/* EXAMPLE: CutApplyEffectToObject(98.3, oPC, DURATION_TYPE_TEMPORARY, + VXF_DUR_PETRIFY, oPC, 4.0); + would have a PETRIFY visual appear to the PC after a 98.3 second delay. + The duration is temporary, lasting 4 seconds. +*/ +void CallApplyEffectToObject(int nCutscene, int iDur, int iEffect, object oObject, float fDur) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + effect eEffect = EffectVisualEffect(iEffect); + ApplyEffectToObject(iDur, eEffect, oObject, fDur); + } +} + +void CutApplyEffectToObject(float fDelay, int iDur, int iEffect, object oObject, float fDur = 0.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallApplyEffectToObject(nCutscene, iDur, iEffect, oObject, fDur))); +} + +//For all other effects (NOT VISUAL EFFECTS) +//Used to apply a NON visual effect to an object. +void CallApplyEffectToObject2(int nCutscene, int iDur, effect eEffect, object oObject, float fDur) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + ApplyEffectToObject(iDur, eEffect, oObject, fDur); + } +} + +void CutApplyEffectToObject2(float fDelay, int iDur, effect eEffect, object oObject, float fDur = 0.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallApplyEffectToObject2(nCutscene, iDur, eEffect, oObject, fDur))); +} + +// Used to apply a knockdown effect to an object. +/* EXAMPLE: CutKnockdown(98.3, oPC, 4.0); + would have a knockdown appear on the PC after a 98.3 second delay. + The duration is always temporary, this one lasting 4 seconds. +*/ +void CallKnockdown(int nCutscene, object oObject, float fDur) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + effect eEffect = EffectKnockdown(); + int nTest = 0; + if (GetPlotFlag(oObject) == TRUE) + { + nTest = 1; + SetPlotFlag(oObject, FALSE); + } + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEffect, oObject, fDur); + if (nTest == 1) + SetPlotFlag(oObject, TRUE); + } +} + +void CutKnockdown(float fDelay, object oObject, float fDur = 3.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallKnockdown(nCutscene, oObject, fDur))); +} + +void CallActionAttack(int nCutscene, object oAttacker, object oAttackee, int bPassive = FALSE) +{ + if(nCutscene == GetLocalInt(oAttacker, "nCutsceneNumber")) + { + AssignCommand(oAttacker, ActionAttack(oAttackee, bPassive)); + } +} + +void CutActionAttack(float fDelay, object oAttacker, object oAttackee, int bPassive = FALSE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oAttacker, iShift), CallActionAttack(nCutscene, oAttacker, oAttackee, bPassive))); +} + +// Used to apply a death effect to an object. +/* EXAMPLE: CutDeath(98.3, oPC, TRUE); + would have a death appear on the PC after a 98.3 second delay. + The duration is always instant. TRUE is used if you want a + spectacular death. Set FALSE otherwise. +*/ + +void CallDeath(int nCutscene, object oObject, int iSpec) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + effect eEffect = EffectDeath(iSpec, TRUE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oObject); + } + +} + +void CutDeath(float fDelay, object oObject, int iSpec = FALSE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallDeath(nCutscene, oObject, iSpec))); +} + +// oObject would unlock oTarget +void CallActionUnlockObject(int nCutscene, object oObject, object oTarget) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionUnlockObject(oTarget)); + } +} + +void CutActionUnlockObject(float fDelay, object oObject, object oTarget, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionUnlockObject(nCutscene, oObject, oTarget))); +} + + +// oObject would lock oTarget +void CallActionLockObject(int nCutscene, object oObject, object oTarget) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionLockObject(oTarget)); + } +} + +void CutActionLockObject(float fDelay, object oObject, object oTarget, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionLockObject(nCutscene, oObject, oTarget))); +} + +// oObject would flee lLoc +void CallActionMoveAwayFromLocation(int nCutscene, object oObject, location lLoc, int bRun, float fDistance) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionMoveAwayFromLocation(lLoc, bRun, fDistance)); + } +} + +void CutActionMoveAwayFromLocation(float fDelay, object oObject, location lLoc, int bRun = FALSE, float fDistance = 40.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionMoveAwayFromLocation(nCutscene, oObject, lLoc, bRun, fDistance))); +} + +// oObject would flee oTarget +void CallActionMoveAwayFromObject(int nCutscene, object oObject, object oTarget, int bRun, float fDistance) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionMoveAwayFromObject(oTarget, bRun, fDistance)); + } +} + +void CutActionMoveAwayFromObject(float fDelay, object oObject, object oTarget, int bRun = FALSE, float fDistance = 40.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionMoveAwayFromObject(nCutscene, oObject, oTarget, bRun, fDistance))); +} + + +// oObject would follow oFollow +void CallActionForceFollowObject(int nCutscene, object oObject, object oFollow, float fFollowDistance) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionForceFollowObject(oFollow, fFollowDistance)); + } +} + +void CutActionForceFollowObject(float fDelay, object oObject, object oFollow, float fFollowDistance = 0.0, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionForceFollowObject(nCutscene, oObject, oFollow, fFollowDistance))); +} + + +// oObject unequips oItem +void CallActionUnequipItem(int nCutscene, object oObject, object oItem) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionUnequipItem(oItem)); + } +} + +void CutActionUnequipItem(float fDelay, object oObject, object oItem, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionUnequipItem(nCutscene, oObject, oItem))); +} + + +// oObject equips oItem in InvSlot +void CallActionEquipItem(int nCutscene, object oObject, object oItem, int nInvSlot) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionEquipItem(oItem, nInvSlot)); + } +} + +void CutActionEquipItem(float fDelay, object oObject, object oItem, int nInvSlot, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionEquipItem(nCutscene, oObject, oItem, nInvSlot))); +} + + +// oObject would sit on oChair +void CallActionSit(int nCutscene, object oObject, object oChair) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionSit(oChair)); + } +} + +void CutActionSit(float fDelay, object oObject, object oChair, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionSit(nCutscene, oObject, oChair))); +} + + +// oObject would open oDoor +void CallActionOpenDoor(int nCutscene, object oObject, object oDoor) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionOpenDoor(oDoor)); + } +} + +void CutActionOpenDoor(float fDelay, object oObject, object oDoor, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionOpenDoor(nCutscene, oObject, oDoor))); +} + +void CallActionCloseDoor(int nCutscene, object oCloser, object oDoor) +{ + if(nCutscene == GetLocalInt(oCloser, "nCutsceneNumber")) + { + AssignCommand(oCloser, ActionCloseDoor(oDoor)); + } +} + +void CutActionCloseDoor(float fDelay, object oCloser, object oDoor, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oCloser, iShift), CallActionCloseDoor(nCutscene, oCloser, oDoor))); +} + +// Used to cast a fake spell at an object. +/* EXAMPLE: CutActionCastFakeSpellAtObject(23.0, oPC, SPELL_SUNBEAM, oTarget, + PROJECTILE_PATH_TYPE_DEFAULT); would have the + PC cast a sunbeam at the oTarget, with a default + projectile path. This would happen after a 23 + second delay. +*/ +void CallActionCastFakeSpellAtObject(int nCutscene, int iSpell, object oObject, object oTarget, int iPath) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionCastFakeSpellAtObject(iSpell, oTarget, iPath)); + } +} + +void CutActionCastFakeSpellAtObject(float fDelay, int iSpell, object oObject, object oTarget, int iPath = PROJECTILE_PATH_TYPE_DEFAULT, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionCastFakeSpellAtObject(nCutscene, iSpell, oObject, oTarget, iPath))); +} + +// Used to cast a spell at an object. +void CallActionCastSpellAtObject(int nCutscene, int iSpell, object oObject, object oTarget, int nMetaMagic, int bCheat, int nDomainLevel, int iPath, int bInstantSpell) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, nDomainLevel, iPath, bInstantSpell)); + } +} + +void CutActionCastSpellAtObject(float fDelay, int iSpell, object oObject, object oTarget, int nMetaMagic=METAMAGIC_ANY, int bCheat=FALSE, int nDomainLevel=0, int iPath=PROJECTILE_PATH_TYPE_DEFAULT, int bInstantSpell=FALSE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionCastSpellAtObject(nCutscene, iSpell, oObject, oTarget, nMetaMagic, bCheat, nDomainLevel, iPath, bInstantSpell))); +} + +// Used to cast a fake spell at a location. +/* EXAMPLE: CutActionCastFakeSpellAtLocation(23.0, oPC, SPELL_SUNBEAM, lLoc, + PROJECTILE_PATH_TYPE_DEFAULT); would have the + PC cast a sunbeam at lLoc, with a default + projectile path. This would happen after a 23 + second delay. +*/ +void CallActionCastFakeSpellAtLocation(int nCutscene, int iSpell, object oObject, location lLoc, int iPath) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionCastFakeSpellAtLocation(iSpell, lLoc, iPath)); + } +} + +void CutActionCastFakeSpellAtLocation(float fDelay, int iSpell, object oObject, location lLoc, int iPath = PROJECTILE_PATH_TYPE_DEFAULT, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionCastFakeSpellAtLocation(nCutscene, iSpell, oObject, lLoc, iPath))); +} + +// Used to cast a spell at a location. + +void CallActionCastSpellAtLocation(int nCutscene, int iSpell, object oObject, location lLoc, int nMetaMagic, int bCheat, int iPath, int bInstantSpell) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + AssignCommand(oObject, ActionCastSpellAtLocation(iSpell, lLoc, nMetaMagic, bCheat, iPath, bInstantSpell)); + } +} + +void CutActionCastSpellAtLocation(float fDelay, int iSpell, object oObject, location lLoc, int nMetaMagic=METAMAGIC_ANY, int bCheat=FALSE, int iPath=PROJECTILE_PATH_TYPE_DEFAULT, int bInstantSpell=FALSE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallActionCastSpellAtLocation(nCutscene, iSpell, oObject, lLoc, nMetaMagic, bCheat, iPath, bInstantSpell))); +} + +// Used to set the PC's location so they can be jumped around as though +// they are the camera. +/* EXAMPLE: CutSetLocation(23.0, oPC); would set the location + on the PC, as a variable, as a location to be returned to later. This + would happen after a 23 second delay. +*/ +void CallSetLocation(int nCutscene, object oPC) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + SetLocalLocation(oPC, "cut_jump_location", GetLocation(oPC)); + SetLocalInt(oPC, "X2_HasStoredLocation", 1); + } +} + +void CutSetLocation(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetLocation(nCutscene, oPC))); +} + +// Used to recall the PC's location so they can be jumped back to their +// original location, stored by CutGetLocation. +/* EXAMPLE: CutRestoreLocation(23.0, oPC); would return the PC + to their original spot. This would happen after a 23 second delay. +*/ +void CallRestoreLocation(int nCutscene, object oPC) +{ + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + // Restoring location only if the pc has had a valid location set earlier. + if(GetLocalInt(oPC, "X2_HasStoredLocation") == 1) + { + location lLoc = GetLocalLocation(oPC, "cut_jump_location"); + if(GetCommandable(oPC) == FALSE) + { + SetCommandable(TRUE, oPC); + AssignCommand(oPC, JumpToLocation(lLoc)); + DelayCommand(0.2, SetCommandable(FALSE, oPC)); + } + else + AssignCommand(oPC, JumpToLocation(lLoc)); + SetLocalInt(oPC, "X2_HasStoredLocation", 0); + } + + } +} + +void CutRestoreLocation(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallRestoreLocation(nCutscene, oPC))); +} + +void CutRestorePCAppearance(int nCutscene, object oPC) +{ + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + SetCameraHeight(oPC); + } + + /*if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + int nChangedApp = GetLocalInt(oPC, "X2_CUT_CHANGE_APPEARANCE" + IntToString(nCutscene)); + if(nChangedApp == 0) // stay here only if the pc has changed in appearance by the cutscene system + return; + SetLocalInt(oPC, "X2_CUT_CHANGE_APPEARANCE" + IntToString(nCutscene), 0); + // getting original appearance of pc + int nOldApp = GetLocalInt(oPC, "X2_CUT_APPEARANCE"); + SetLocalInt(oPC, "X2_CUT_APPEARANCE", -1); // initializing + if(GetAppearanceType(oPC) == nOldApp) // current appearance is the same as before changing it + // the player has changed his appearance during the cutscene so there is no need to restore it + // (the change was probably a run-off polymorph effect) + { + return; + } + + // player has still a different appearance + int nApp = -1; + if(GetIsPossessedFamiliar(oPC)) + nApp = nOldApp; // a familiar should always be restored to the old appearance + else if(GetRacialType(oPC) == RACIAL_TYPE_DWARF && GetAppearanceType(oPC) != APPEARANCE_TYPE_DWARF) + nApp = APPEARANCE_TYPE_DWARF; + else if(GetRacialType(oPC) == RACIAL_TYPE_ELF && GetAppearanceType(oPC) != APPEARANCE_TYPE_ELF) + nApp = APPEARANCE_TYPE_ELF; + else if(GetRacialType(oPC) == RACIAL_TYPE_HALFELF && GetAppearanceType(oPC) != APPEARANCE_TYPE_HALF_ELF) + nApp = APPEARANCE_TYPE_HALF_ELF; + else if(GetRacialType(oPC) == RACIAL_TYPE_HALFORC && GetAppearanceType(oPC) != APPEARANCE_TYPE_HALF_ORC) + nApp = APPEARANCE_TYPE_HALF_ORC; + else if(GetRacialType(oPC) == RACIAL_TYPE_HALFLING && GetAppearanceType(oPC) != APPEARANCE_TYPE_HALFLING) + nApp = APPEARANCE_TYPE_HALFLING; + else if(GetRacialType(oPC) == RACIAL_TYPE_GNOME && GetAppearanceType(oPC) != RACIAL_TYPE_GNOME) + nApp = RACIAL_TYPE_GNOME; + else if(GetRacialType(oPC) == RACIAL_TYPE_HUMAN && GetAppearanceType(oPC) != RACIAL_TYPE_HUMAN) + nApp = RACIAL_TYPE_HUMAN; + else + nApp = nOldApp; + if(nApp == -1) + return; + + SetCreatureAppearanceType(oPC, nApp); + // apply inv effect so the player won't see the old form + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oPC, 1.5); + }*/ +} + +void CutRemoveHenchmenAssociates(object oPC) +{ + + object oAnimal; + object oDominated; + object oFamiliar; + object oSummoned; + int i = 1; + object oHenchman = GetHenchman(oPC, i); + while(oHenchman != OBJECT_INVALID) + { + oAnimal = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oHenchman); + oDominated = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oHenchman); + oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oHenchman); + oSummoned = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oHenchman); + DestroyObject(oAnimal); + DestroyObject(oDominated); + DestroyObject(oFamiliar); + DestroyObject(oSummoned); + i++; + oHenchman = GetHenchman(oPC, i); + } + +} + +void CallSetCutsceneMode(int nCutscene, object oPC, int iValue, int bInv, int bKeepAssociate = TRUE, int bFreezeAssociate = TRUE) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + if(iValue == FALSE) // Disable cutscene mode + { + SetCutsceneMode(oPC, iValue); + SetPlotFlag(oPC, FALSE); + SetCommandable(TRUE, oPC); + UnFreezeAssociate(oPC); + CutSetActiveCutsceneForObject(oPC, 0); + SetCameraHeight(oPC, 0.0); + } + else // enable cutscene mode + { + //SetLocalInt(oPC, "X2_CUT_APPEARANCE", -1); + AssignCommand(oPC, ClearAllActions(TRUE)); + SetActionMode(oPC, ACTION_MODE_DETECT, FALSE); + SetActionMode(oPC, ACTION_MODE_STEALTH, FALSE); + if(bInv >= TRUE) + { + + //SetLocalInt(oPC, "X2_CUT_CHANGE_APPEARANCE" + IntToString(nCutscene), 1); // flagging pc as changed appearance + //SetLocalInt(oPC, "X2_CUT_APPEARANCE", GetAppearanceType(oPC)); + + DelayCommand(0.2, ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), + oPC, 9999.0)); + + if(bInv == CUT_CAMERA_HEIGHT_VERY_LOW) + DelayCommand(0.4, SetCameraHeight(oPC, 0.3)); + //DelayCommand(0.4, SetCreatureAppearanceType(oPC, APPEARANCE_TYPE_CHICKEN)); + if(bInv == CUT_CAMERA_HEIGHT_LOW) + DelayCommand(0.4, SetCameraHeight(oPC, 1.0)); + //DelayCommand(0.4, SetCreatureAppearanceType(oPC, APPEARANCE_TYPE_HALFLING_NPC_MALE)); + if(bInv == CUT_CAMERA_HEIGHT_MEDIUM) + DelayCommand(0.4, SetCameraHeight(oPC, 1.25)); + //DelayCommand(0.4, SetCreatureAppearanceType(oPC, APPEARANCE_TYPE_HUMAN_NPC_MALE_01)); + if(bInv == CUT_CAMERA_HEIGHT_HIGH) + DelayCommand(0.4, SetCameraHeight(oPC, 1.75)); + //DelayCommand(0.4, SetCreatureAppearanceType(oPC, APPEARANCE_TYPE_HALF_ORC_NPC_MALE_01)); + if(bInv == CUT_CAMERA_HEIGHT_VERY_HIGH) + DelayCommand(0.4, SetCameraHeight(oPC, 5.0)); + //DelayCommand(0.4, SetCreatureAppearanceType(oPC, APPEARANCE_TYPE_GIANT_FROST)); + } + SetCutsceneMode(oPC, iValue); + if(bFreezeAssociate == TRUE || bFreezeAssociate == VANISH) + { + int bVanish = FALSE; + if(bFreezeAssociate == VANISH) + bVanish = TRUE; + object oDominated = FreezeAssociate(oPC, bVanish); + if (GetIsObjectValid(oDominated) == TRUE) + SetLocalObject(oPC, "oDominated", oDominated); + } + // Destroy associates. + if (bKeepAssociate == FALSE) + { + if(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC) != OBJECT_INVALID) + DestroyObject(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC)); + if(GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC) != OBJECT_INVALID) + DestroyObject(GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC)); + if(GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC) != OBJECT_INVALID) + DestroyObject(GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC)); + if(GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC) != OBJECT_INVALID) + DestroyObject(GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC)); + } + // Remove Henchmens' associates + CutRemoveHenchmenAssociates(oPC); + } + } +} + +void CutSetCutsceneMode(float fDelay, object oPC, int iValue, int bInv, int bKeepAssociate = TRUE, int bFreezeAssociate = TRUE, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetCutsceneMode(nCutscene, oPC, iValue, bInv, bKeepAssociate, bFreezeAssociate))); +} + +// Used to turn plot flags on or off. +/* EXAMPLE: CutSetPlotFlag(10.0, oObject, 1); would turn plot flag to + on after 10 seconds for the object. +*/ +void CallSetPlotFlag(int nCutscene, object oObject, int iValue) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + + SetPlotFlag(oObject, iValue); + + } +} + +void CutSetPlotFlag(float fDelay, object oObject, int iValue, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallSetPlotFlag(nCutscene, oObject, iValue))); +} + +// Used to destroy objects. Make sure they are destroyable first with the +// CutSetPlotFlag function. +/* EXAMPLE: CutDestroyObject(10.0, oObject); would destroy the object + after 10 seconds. +*/ +void CallDestroyObject(int nCutscene, object oObject) +{ + if(nCutscene == GetLocalInt(oObject, "nCutsceneNumber")) + { + + DestroyObject(oObject); + + } +} + +void CutDestroyObject(float fDelay, object oObject, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oObject, iShift), CallDestroyObject(nCutscene, oObject))); +} + +// Used to store the camera position. +/* EXAMPLE: CutStoreCameraFacing(10.0, oPC); would set the current + camera settings at the 10.0 mark. +*/ +void CallStoreCameraFacing(int nCutscene, object oPC) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + + StoreCameraFacing(); + + } +} + +void CutStoreCameraFacing(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallStoreCameraFacing(nCutscene, oPC))); +} + +// Used to restore the camera position. +/* EXAMPLE: CutRestoreCameraFacing(10.0, oPC); would set the old + camera settings back at the 10.0 mark. +*/ +void CallRestoreCameraFacing(int nCutscene, object oPC) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + + RestoreCameraFacing(); + + } +} + +void CutRestoreCameraFacing(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallRestoreCameraFacing(nCutscene, oPC))); +} + +// Used to set the screen to black. This is very useful in covering up +// and cutscene jumps that happen in the same area as the play area. +/* EXAMPLE: CutBlackScreen(10.0, oPC); would set the screen to black + at the 10.0 mark. +*/ +void CallBlackScreen(int nCutscene, object oPC) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + + BlackScreen(oPC); + + } +} + +void CutBlackScreen(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallBlackScreen(nCutscene, oPC))); +} + +// Used to remove the screen to black. +/* EXAMPLE: CutStopFade(10.0, oPC); would set the screen back from black + at the 10.0 mark. +*/ +void CallStopFade(int nCutscene, object oPC) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + + StopFade(oPC); + + } +} + +void CutStopFade(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallStopFade(nCutscene, oPC))); +} + +void CallPlayVoiceChat(int nCutscene, object oPC, int nChatID) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + AssignCommand(oPC, PlayVoiceChat(nChatID)); + } +} + +void CutPlayVoiceChat(float fDelay, object oPC, int nChatID, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallPlayVoiceChat(nCutscene, oPC, nChatID))); +} + +void CallPlaySound(int nCutscene, object oPC, string szSound) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + AssignCommand(oPC, PlaySound(szSound)); + } +} + +void CutPlaySound(float fDelay, object oPC, string szSound, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallPlaySound(nCutscene, oPC, szSound))); +} + +// Setting a background ambient sounds and playing it for the area where oPC is in. +void CallSetAmbient(int nCutscene, object oPC, int nTrack) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + // change for both day and night + AmbientSoundChangeDay(GetArea(oPC), nTrack); + AmbientSoundChangeNight(GetArea(oPC), nTrack); + + // play new ambient sounds + AmbientSoundPlay(GetArea(oPC)); + } +} + +void CutSetAmbient(float fDelay, object oPC, int nTrack, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetAmbient(nCutscene, oPC, nTrack))); +} + +// Setting a background music and playing it for the area where oPC is in. +void CallSetMusic(int nCutscene, object oPC, int nTrack) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + // change for both day and night + MusicBackgroundChangeDay(GetArea(oPC), nTrack); + MusicBackgroundChangeNight(GetArea(oPC), nTrack); + + // stop any battle music and play the new music + MusicBattleStop(GetArea(oPC)); + MusicBackgroundPlay(GetArea(oPC)); + } + +} + +void CutSetMusic(float fDelay, object oPC, int nTrack, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetMusic(nCutscene, oPC, nTrack))); +} + +// keep old background music +void CallStoreMusic(int nCutscene, object oPC) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + SetLocalInt(GetArea(oPC), "X2_MUSIC_DAY", MusicBackgroundGetDayTrack(GetArea(oPC))); + SetLocalInt(GetArea(oPC), "X2_MUSIC_NIGHT", MusicBackgroundGetNightTrack(GetArea(oPC))); + } +} + +void CutStoreMusic(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallStoreMusic(nCutscene, oPC))); +} + +// restore old background music. Notice that there is no cutscene number check - so music can +// be restoed after aborting a cutscene +void CallRestoreMusic(int nCutscene, object oPC) +{ + MusicBackgroundChangeDay(GetArea(oPC), GetLocalInt(GetArea(oPC), "X2_MUSIC_DAY")); + MusicBackgroundChangeNight(GetArea(oPC), GetLocalInt(GetArea(oPC), "X2_MUSIC_NIGHT")); +} + +void CutRestoreMusic(float fDelay, object oPC, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallRestoreMusic(nCutscene, oPC))); +} + +void CallSetTileMainColor(int nCutscene, object oPC, location lLoc, int nMainColor1, int nMainColor2) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + vector vPos = GetPositionFromLocation(lLoc); + vPos.x /= 10; + vPos.y /= 10; + lLoc = Location(GetArea(OBJECT_SELF), vPos, 0.0); + SetTileMainLightColor(lLoc, nMainColor1, nMainColor2); + RecomputeStaticLighting(GetArea(oPC)); + } +} + +void CutSetTileMainColor(float fDelay, object oPC, location lLoc, int nMainColor1, int nMainColor2, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetTileMainColor(nCutscene, oPC, lLoc, nMainColor1, nMainColor2))); +} + +void CallSetTileSourceColor(int nCutscene, object oPC, location lLoc, int nSourceColor1, int nSourceColor2) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + vector vPos = GetPositionFromLocation(lLoc); + vPos.x /= 10; + vPos.y /= 10; + lLoc = Location(GetArea(OBJECT_SELF), vPos, 0.0); + SetTileSourceLightColor(lLoc, nSourceColor1, nSourceColor2); + RecomputeStaticLighting(GetArea(oPC)); + } +} + +void CutSetTileSourceColor(float fDelay, object oPC, location lLoc, int nSourceColor1, int nSourceColor2, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetTileSourceColor(nCutscene, oPC, lLoc, nSourceColor1, nSourceColor2))); +} + +void CallSetWeather(int nCutscene, object oPC, int nWeather) +{ + if(nCutscene == GetLocalInt(oPC, "nCutsceneNumber")) + { + SetWeather(GetArea(oPC), nWeather); + } +} + +void CutSetWeather(float fDelay, object oPC, int nWeather, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetWeather(nCutscene, oPC, nWeather))); +} + +void CallSetCameraSpeed(int nCutscene, object oPC, float fMovementRateFactor) +{ + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + SetCutsceneCameraMoveRate(oPC, fMovementRateFactor); + } + +} + +void CutSetCameraSpeed(float fDelay, object oPC, float fMovementRateFactor, int iShift = TRUE) +{ + int nCutscene = GetActiveCutsceneNum(); + fDelay = CutCalculateCurrentDelay(fDelay, nCutscene); + DelayCommand(fDelay, DelayCommand(GetShift(oPC, iShift), CallSetCameraSpeed(nCutscene, oPC, fMovementRateFactor))); +} + +void CutDisableCutscene(int nCutscene, float fCleanupDelay, float fDestPCCopyDelay, int nRestoreType = RESTORE_TYPE_NORMAL) +{ + if(GetLocalInt(GetModule(), "X2_DelayType" + IntToString(nCutscene)) != CUT_DELAY_TYPE_CONSTANT) + { + fCleanupDelay = CutCalculateCurrentDelay(fCleanupDelay, nCutscene); + fDestPCCopyDelay = CutCalculateCurrentDelay(fDestPCCopyDelay, nCutscene); + } + // Setting constant delay + object oPC = GetFirstPC(); + + while(oPC != OBJECT_INVALID) + { + object oFamiliar = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + if(oFamiliar != OBJECT_INVALID && GetIsPossessedFamiliar(oFamiliar)) + oPC = oFamiliar; + + if(CutGetActiveCutsceneForObject(oPC) == nCutscene) + { + // Notice that the following functions use the delay supplied + // by the CutSetAbortDelay() function (defaults to 0) + //CutRestoreCameraFacing(fDelay1, oPC, FALSE); + string sDelayVariable = "X2_fCutscene" + IntToString(nCutscene) + "Delay"; + + //DelayCommand(fCleanupDelay - 1.0, CutRestorePCAppearance(nCutscene, oPC)); + DelayCommand(fCleanupDelay, SetLocalFloat(GetModule(), sDelayVariable, 0.0)); + DelayCommand(fCleanupDelay, CallRemoveAssociatesEffects(nCutscene, oPC)); + DelayCommand(fCleanupDelay, CallRemoveEffects(nCutscene, oPC)); + if(nRestoreType == RESTORE_TYPE_NORMAL) + DelayCommand(fCleanupDelay, CallRestoreLocation(nCutscene, oPC)); + DelayCommand(fCleanupDelay + 0.3, CallSetCutsceneMode(nCutscene, oPC, FALSE, FALSE, FALSE, FALSE)); + if(nRestoreType == RESTORE_TYPE_COPY) + DelayCommand(fCleanupDelay, CallRestorePCCopyLocation(nCutscene, oPC)); + // oPC is not the main pc for the cutscene after the following line: + // Notice that this function uses the delay supplied by the + // CutSetDestroyCopyDelay() function (defaults to 0). + + DelayCommand(fDestPCCopyDelay, CallDestroyPCCopy(nCutscene, oPC, FALSE)); // destroys copy if it exists + } + oPC = GetNextPC(); + } + DelayCommand(fCleanupDelay + 0.8, CutSetActiveCutscene(-1, CUT_DELAY_TYPE_CONSTANT)); +} + + +// Test main +//void main(){} diff --git a/src/include/x2_inc_spellhook.nss b/src/include/x2_inc_spellhook.nss new file mode 100644 index 0000000..2785789 --- /dev/null +++ b/src/include/x2_inc_spellhook.nss @@ -0,0 +1,3573 @@ +//:://///////////////////////////////////////////// +//:: Spell Hook Include File +//:: x2_inc_spellhook +//:: Copyright (c) 2003 Bioware Corp. +//::////////////////////////////////////////////// +/* + + This file acts as a hub for all code that + is hooked into the nwn spellscripts' + + If you want to implement material components + into spells or add restrictions to certain + spells, this is the place to do it. + +*/ +//::////////////////////////////////////////////// +//:: Created By: Georg Zoeller +//:: Created On: 2003-06-04 +//:: Updated On: 2003-10-25 +//::////////////////////////////////////////////// +//:: Modified By: Deva Winblood +//:: Modified Date: January 15th-16th, 2008 +//::////////////////////////////////////////////// +/* + Modified to insure no shapeshifting spells are castable upon + mounted targets. This prevents problems that can occur due + to dismounting after shape shifting, or other issues that can + occur due to preserved appearances getting out of synch. + + This can additional check can be disabled by setting the variable + X3_NO_SHAPESHIFT_SPELL_CHECK to 1 on the module object. If this + variable is set then this script will function as it did prior to + this modification. + +*/ + +const int X2_EVENT_CONCENTRATION_BROKEN = 12400; + +// Removes spell use for new spellbook system, and calculates spell fail +// chance from ASF or silence effects. +int NSB_SpellCast(object oCaster, int nSpellID, int nCastingClass, int nMetamagic, int nSpellbookType, string sComponent, object oSpellCastItem); + +// This function checks for material components or gold +// See switch PRC_MATERIAL_COMPONENTS +int MaterialComponents(object oCaster, int nSpellID, int nCastingClass, object oSpellCastItem); + +// This function checks for the Red Wizard's restricted +// spell school and prevents him from casting the spells +// that he is banned from casting. +int RedWizRestrictedSchool(object oCaster, int nSchool, int nCastingClass, object oSpellCastItem); + +// This function checks whether the Combat Medic's Healing Kicker +// feats are active, and if so imbues the spell target with additional +// beneficial effects. +void CombatMedicHealingKicker(object oCaster, object oTarget, int nSpellID); + +// Duskblade channeling. While channeling, stops non-touch spells +// from working +int DuskbladeArcaneChanneling(object oCaster, object oTarget, int nSpellID, int nCasterLevel, int nMetamagic, object oSpellCastItem); + +// Handles the "When spell is cast do this" effects from the Draconic +// series of feats +void DraconicFeatsOnSpell(object oCaster, object oTarget, object oSpellCastItem, int nSpellLevel, int nCastingClass); + +// Bard / Sorc PrC handling +// returns FALSE if it is a bard or a sorcerer spell from a character +// with an arcane PrC via bioware spellcasting rather than via PrC spellcasting +int BardSorcPrCCheck(object oCaster, int nCastingClass, object oSpellCastItem); + +// Grappling +// Rolls a Concentration check to cast a spell while grappling. +int GrappleConc(object oCaster, int nSpellLevel); + +// Blighters can't cast druid spells +int Blighter(object oCaster, int nCastingClass, object oSpellCastItem); + +// Spelldance perform check +int Spelldance(object oCaster, int nSpellLevel, int nCastingClass); + +// Dazzling Illusion feat +// Dazzles enemies within radius +void DazzlingIllusion(object oCaster, int nSchool); + +// Battle Blessing check +int BattleBlessing(object oCaster, int nCastingClass); + +// Use Magic Device Check. +// Returns TRUE if the Spell is allowed to be cast, either because the +// character is allowed to cast it or he has won the required UMD check +// Only active on spell scroll +int X2UseMagicDeviceCheck(object oCaster); + +// check if the spell is prohibited from being cast on items +// returns FALSE if the spell was cast on an item but is prevented +// from being cast there by its corresponding entry in des_crft_spells +// oItem - pass PRCGetSpellTargetObject in here +int X2CastOnItemWasAllowed(object oItem); + +// Sequencer Item Property Handling +// Returns TRUE (and charges the sequencer item) if the spell +// ... was cast on an item AND +// ... the item has the sequencer property +// ... the spell was non hostile +// ... the spell was not cast from an item +// in any other case, FALSE is returned an the normal spellscript will be run +// oItem - pass PRCGetSpellTargetObject in here +int X2GetSpellCastOnSequencerItem(object oItem, object oCaster, int nSpellID, int nMetamagic, int nCasterLevel, int nSaveDC, int bSpellIsHostile, object oSpellCastItem); + +int X2RunUserDefinedSpellScript(); + +// Similar to SetModuleOverrideSpellscript but only applies to the user +// of this spell. Basically tells the class to run this script when the +// spell starts. +void PRCSetUserSpecificSpellScript(string sScript); + +// Similar to SetModuleOverrideSpellscriptFinished but only applies to the +// user of this spell. This prevents the spell from continuing on if the +// ability dictates it. +void PRCSetUserSpecificSpellScriptFinished(); + +// By setting user-defined spellscripts to the player only, we +// avoid the nasty mess of spellhooking the entire module for one player's +// activities. This function is mostly only useful inside this include. +int PRCRunUserSpecificSpellScript(); + +// Useful functions for PRCRunUserSpecificSpellScript but not useful in spell +// scripts. +string PRCGetUserSpecificSpellScript(); +int PRCGetUserSpecificSpellScriptFinished(); + +//#include "prc_x2_itemprop" - Inherited from prc_x2_craft +//#include "prc_alterations" +#include "prc_x2_craft" +//#include "x3_inc_horse" +#include "prc_inc_spells" +#include "prc_inc_combat" +//#include "inc_utility" +#include "prc_inc_itmrstr" +#include "prc_inc_burn" +//#include "inc_newspellbook" +//#include "prc_sp_func" +//#include "psi_inc_manifest" +//#include "prc_inc_combmove" +#include "pnp_shft_main" +#include "inc_dynconv" +#include "inc_npc" + +int DruidSpontSummon(object oCaster, int nCastingClass, int nSpellID, int nSpellLevel) +{ + if(nCastingClass != CLASS_TYPE_DRUID) + return TRUE; + + if(GetLocalInt(oCaster, "PRC_SpontSummon")) + { + DeleteLocalInt(oCaster, "PRC_SpontSummon"); + int nMetamagic = GetMetaMagicFeat();//we need bioware metamagic here + int nSpellLevel = PRCGetSpellLevelForClass(nSpellID, CLASS_TYPE_DRUID); + nSpellLevel += GetMetaMagicSpellLevelAdjustment(nMetamagic); + int nSummonSpell; + switch(nSpellLevel) + { + case 0: return TRUE; + case 1: nSummonSpell = SPELL_SUMMON_CREATURE_I; break; + case 2: nSummonSpell = SPELL_SUMMON_CREATURE_II; break; + case 3: nSummonSpell = SPELL_SUMMON_CREATURE_III; break; + case 4: nSummonSpell = SPELL_SUMMON_CREATURE_IV; break; + case 5: nSummonSpell = SPELL_SUMMON_CREATURE_V; break; + case 6: nSummonSpell = SPELL_SUMMON_CREATURE_VI; break; + case 7: nSummonSpell = SPELL_SUMMON_CREATURE_VII; break; + case 8: nSummonSpell = SPELL_SUMMON_CREATURE_VIII; break; + case 9: nSummonSpell = SPELL_SUMMON_CREATURE_IX; break; + } + + //subradial spells + if(nSummonSpell == SPELL_SUMMON_CREATURE_VII + || nSummonSpell == SPELL_SUMMON_CREATURE_VIII + || nSummonSpell == SPELL_SUMMON_CREATURE_IX) + { + SetLocalInt(oCaster, "DomainOrigSpell", nSummonSpell); + SetLocalInt(oCaster, "DomainCastLevel", nSpellLevel); + SetLocalInt(oCaster, "DomainCastClass", CLASS_TYPE_DRUID); + StartDynamicConversation("prc_domain_conv", oCaster, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oCaster); + } + else + ActionCastSpell(nSummonSpell, 0, 0, 0, METAMAGIC_NONE, CLASS_TYPE_DRUID); + + //Don't cast original spell + return FALSE; + } + + return TRUE; +} + +int ArcaneSpellFailure(object oCaster, int nCastingClass, int nSpellLevel, int nMetamagic, string sComponents) +{ + if(!GetIsArcaneClass(nCastingClass)) + return FALSE; + + // They don't suffer ASF + if(nCastingClass == CLASS_TYPE_FACTOTUM) + return FALSE; + + // Hammer of Witches - wielder cannot cast arcane spells + if(GetIsObjectValid(GetItemPossessedBy(oCaster, "WOL_HammerWitches"))) + return TRUE; + + if(FindSubString(sComponents, "S") == -1) + return FALSE; + + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCaster); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster); + int nAC = GetBaseAC(oArmor); + int nASF = GetArcaneSpellFailure(oCaster); + int bBattleCaster = GetHasFeat(FEAT_BATTLE_CASTER, oCaster); + + //Classes with reduced ASF + // Beguiler/Dread Necromancer/Sublime Chord can cast in light armor. + if(nCastingClass == CLASS_TYPE_BEGUILER + || nCastingClass == CLASS_TYPE_DREAD_NECROMANCER + || nCastingClass == CLASS_TYPE_SUBLIME_CHORD) + { + //armors + switch(nAC) + { + case 1: nASF -= 5; break;//light + case 2: nASF -= 10; break;//light + case 3: nASF -= 20; break;//light + case 4: nASF -= bBattleCaster ? 20 : 0; break;//medium; + case 5: nASF -= bBattleCaster ? 30 : 0; break;//medium + default: break; + } + } + // Hexblade can cast in light/medium armour and while using small shield. + else if(nCastingClass == CLASS_TYPE_HEXBLADE) + { + //shields + if(GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD) nASF -= 5; + //armors + switch(nAC) + { + case 1: nASF -= 5; break; + case 2: nASF -= 10; break; + case 3: nASF -= 20; break; + case 4: nASF -= 20; break; + case 5: nASF -= 30; break; + case 6: nASF -= bBattleCaster ? 40 : 0; break; + case 7: nASF -= bBattleCaster ? 40 : 0; break; + case 8: nASF -= bBattleCaster ? 45 : 0; break; + default: break; + } + } + // Bards cannot cast in light armour and while using small shield in 3e +/* else if(nCastingClass == CLASS_TYPE_BARD) + { + int nLvl = GetLevelByClass(CLASS_TYPE_BARD, oCaster); + int nShield = GetBaseItemType(oShield); + //armors + switch(nAC) + { + case 1: nASF -= 5; break;//light + case 2: nASF -= 10; break;//light + case 3: nASF -= 20; break;//light + case 4: nASF -= bBattleCaster ? 20 : 0; break;//medium; + case 5: nASF -= bBattleCaster ? 30 : 0; break;//medium + default: break; + } + //shields + switch(nShield) + { + case BASE_ITEM_SMALLSHIELD: nASF -= 5; break; + } + } */ + // Duskblade can cast in light/medium armour and while using small/large shield. + else if(nCastingClass == CLASS_TYPE_DUSKBLADE) + { + int nLvl = GetLevelByClass(CLASS_TYPE_DUSKBLADE, oCaster); + int nShield = GetBaseItemType(oShield); + //armors + switch(nAC) + { + case 1: nASF -= 5; break; + case 2: nASF -= 10; break; + case 3: nASF -= 20; break; + case 4: nASF -= (nLvl >= 4 || bBattleCaster) ? 20 : 0; break; + case 5: nASF -= (nLvl >= 4 || bBattleCaster) ? 30 : 0; break; + case 6: nASF -= (nLvl >= 4 && bBattleCaster) ? 40 : 0; break; + case 7: nASF -= (nLvl >= 4 && bBattleCaster) ? 40 : 0; break; + case 8: nASF -= (nLvl >= 4 && bBattleCaster) ? 45 : 0; break; + default: break; + } + //shields + switch(nShield) + { + case BASE_ITEM_SMALLSHIELD: nASF -= 5; break; + case BASE_ITEM_LARGESHIELD: nASF -= 15; break; + } + } + // Suel Archanamach gets the Ignore Spell Failure Chance feats +// Suel Archanamach gets the Ignore Spell Failure Chance feats + else if(nCastingClass == CLASS_TYPE_SUEL_ARCHANAMACH) + { + int nLvl = GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oCaster); + + if (nLvl >= 28) nASF -= 50; + else if(nLvl >= 25) nASF -= 45; + else if(nLvl >= 22) nASF -= 40; + else if(nLvl >= 19) nASF -= 35; + else if(nLvl >= 16) nASF -= 30; + else if(nLvl >= 13) nASF -= 25; + else if(nLvl >= 10) nASF -= 20; + else if(nLvl >= 7) nASF -= 15; + else if(nLvl >= 4) nASF -= 10; + else if(nLvl >= 1) nASF -= 5; + } + // Warmage can cast in light/medium armour and while using small shield. + else if(nCastingClass == CLASS_TYPE_WARMAGE) + { + int nLvl = GetLevelByClass(CLASS_TYPE_WARMAGE, oCaster); + //armors + switch(nAC) + { + case 1: nASF -= 5; break; + case 2: nASF -= 10; break; + case 3: nASF -= 20; break; + case 4: nASF -= (nLvl >= 8 || bBattleCaster) ? 20 : 0; break; + case 5: nASF -= (nLvl >= 8 || bBattleCaster) ? 30 : 0; break; + case 6: nASF -= (nLvl >= 8 && bBattleCaster) ? 40 : 0; break; + case 7: nASF -= (nLvl >= 8 && bBattleCaster) ? 40 : 0; break; + case 8: nASF -= (nLvl >= 8 && bBattleCaster) ? 45 : 0; break; + default: break; + } + //shields + if(GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD) + nASF -= 5; + } + // Knight of the Weave ignores spell failure in light/medium + else if(nCastingClass == CLASS_TYPE_KNIGHT_WEAVE) + { + int nLvl = GetLevelByClass(CLASS_TYPE_KNIGHT_WEAVE, oCaster); + if (nLvl >= 2) //Doesn't start until 2nd level + { + //armors + switch(nAC) + { + case 1: nASF -= 5; break; + case 2: nASF -= 10; break; + case 3: nASF -= 20; break; + case 4: nASF -= (nLvl >= 8 || bBattleCaster) ? 20 : 0; break; + case 5: nASF -= (nLvl >= 8 || bBattleCaster) ? 30 : 0; break; + case 6: nASF -= (nLvl >= 8 && bBattleCaster) ? 40 : 0; break; + case 7: nASF -= (nLvl >= 8 && bBattleCaster) ? 40 : 0; break; + case 8: nASF -= (nLvl >= 8 && bBattleCaster) ? 45 : 0; break; + default: break; + } + } + } + // Redspawn Arcaniss can cast in light armour and while using small shields. + else if(nCastingClass == CLASS_TYPE_SORCERER && !GetLevelByClass(CLASS_TYPE_SORCERER, oCaster) && GetRacialType(oCaster) == RACIAL_TYPE_REDSPAWN_ARCANISS) + { + int nShield = GetBaseItemType(oShield); + //armors + switch(nAC) + { + case 1: nASF -= 5; break; + case 2: nASF -= 10; break; + case 3: nASF -= 20; break; + case 4: nASF -= (bBattleCaster) ? 20 : 0; break; + case 5: nASF -= (bBattleCaster) ? 30 : 0; break; + default: break; + } + //shields + switch(nShield) + { + case BASE_ITEM_SMALLSHIELD: nASF -= 5; break; + } + } + + if(Random(100) < nASF) + { + int nFail = TRUE; + // Still spell helps + if(nMetamagic & METAMAGIC_STILL + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_1, oCaster) && nSpellLevel <= 3) + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_2, oCaster) && nSpellLevel <= 6) + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_3, oCaster) && nSpellLevel <= 9)) + { + nFail = FALSE; + } + if(nFail) + { + //52946 = Spell failed due to arcane spell failure! + FloatingTextStrRefOnCreature(52946, oCaster, FALSE); + return TRUE; + } + } + return FALSE; +} + +int SilenceDeafnessFailure(object oCaster, int nSpellLevel, int nMetamagic, string sComponents) +{ + if(FindSubString(sComponents, "V") == -1) + return FALSE; + + if(PRCGetHasEffect(EFFECT_TYPE_SILENCE, oCaster) + || (PRCGetHasEffect(EFFECT_TYPE_DEAF, oCaster) && Random(100) < 20)) + { + //auto-silent exceptions + if(nMetamagic & METAMAGIC_SILENT + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_1, oCaster) && nSpellLevel <= 3) + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_2, oCaster) && nSpellLevel <= 6) + || (GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_3, oCaster) && nSpellLevel <= 9)) + { + return FALSE; + } + else + { + //3734 = Spell failed! + FloatingTextStrRefOnCreature(3734, oCaster, FALSE); + return TRUE; + } + } + return FALSE; +} + +int Forsaker(object oCaster, object oTarget) +{ + // Friendly spells autofail on Forsakers + if (GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget) && GetIsFriend(oTarget, oCaster)) + { + FloatingTextStringOnCreature("Target is a Forsaker, spell failed!", oCaster, FALSE); + return FALSE; + } + + // Forsakers can't use magic + if (GetLevelByClass(CLASS_TYPE_FORSAKER, oCaster)) + { + FloatingTextStringOnCreature("Forsakers cannot cast spells!", oCaster, FALSE); + return FALSE; + } + + return TRUE; +} + +int NSB_SpellCast(object oCaster, int nSpellID, int nCastingClass, int nMetamagic, int nSpellbookType, string sComponent, object oSpellCastItem) +{ +//DoDebug("PRC last spell cast class = "+IntToString(PRCGetLastSpellCastClass())); +//DoDebug("Primary Arcane Class = "+IntToString(GetPrimaryArcaneClass(oPC))); +//DoDebug("Caster Level = "+IntToString(PRCGetCasterLevel(oPC))); +//DoDebug("NSB_Class = "+GetStringByStrRef(StringToInt(Get2DACache("classes", "Name", NSB_Class)))); + + // check if the spell was cast from original spellbook or from item + int bNormalCasting = (GetLastSpellCastClass() != CLASS_TYPE_INVALID || oSpellCastItem != OBJECT_INVALID || GetLocalInt(oCaster, "SpellIsSLA")); + int NSB_Class = GetLocalInt(oCaster, "NSB_Class"); + int nDomainCast = GetLocalInt(oCaster, "DomainCast"); + + if(nDomainCast) + { + int nBurnSpell = GetLocalInt(oCaster, "Domain_BurnableSpell") - 1; + if(nBurnSpell != -1) + { + if(!GetHasSpell(nBurnSpell, oCaster)) + { + //Stop casting + DeleteLocalInt(oCaster, "DomainCast"); + DeleteLocalInt(oCaster, "Domain_BurnableSpell"); + return FALSE; + } + else + { + DecrementRemainingSpellUses(oCaster, nBurnSpell); + SetLocalInt(oCaster, "DomainCastSpell" + IntToString(nDomainCast), TRUE); + } + } + else + { + DeleteLocalInt(oCaster, "NSB_Class"); + if(NSB_Class != CLASS_TYPE_MYSTIC && NSB_Class != CLASS_TYPE_NIGHTSTALKER) + SetLocalInt(oCaster, "DomainCastSpell" + IntToString(nDomainCast), TRUE); + } + DeleteLocalInt(oCaster, "DomainCast"); + DeleteLocalInt(oCaster, "Domain_BurnableSpell"); + } + + //if for some reason NSB variables were not removed and the player is not casting from new spellbook + //remove the variables now + if(bNormalCasting) + { + if(NSB_Class) + { + //clean local vars + DeleteLocalInt(oCaster, "NSB_SpellLevel"); + DeleteLocalInt(oCaster, "NSB_Class"); + DeleteLocalInt(oCaster, "NSB_SpellbookID"); + } + return TRUE; + } + + //this shuld be executed only for new spellbook spells + else if(NSB_Class) + { + int nSpellLevel = GetLocalInt(oCaster, "NSB_SpellLevel"); + int nCount; + string sArray = "NewSpellbookMem_" + IntToString(nCastingClass); + string sMessage; + + if(nSpellbookType == SPELLBOOK_TYPE_PREPARED) + { + int nSpellbookID = GetLocalInt(oCaster, "NSB_SpellbookID"); + nCount = persistant_array_get_int(oCaster, sArray, nSpellbookID); + if(nCount < 1) + return FALSE; + + nCount--; + persistant_array_set_int(oCaster, sArray, nSpellbookID, nCount); + + int nRealSpellID = StringToInt(Get2DACache(GetFileForClass(nCastingClass), "RealSpellID", nSpellbookID)); + string sSpellName = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nRealSpellID))); + // "You have " + IntToString(nCount) + " castings of " + sSpellName + " remaining" + sMessage = ReplaceChars(GetStringByStrRef(16828410), "", IntToString(nCount)); + sMessage = ReplaceChars(sMessage, "", sSpellName); + } + else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + nCount = persistant_array_get_int(oCaster, sArray, nSpellLevel); + if(nCount < 1) + return FALSE; + + nCount--; + persistant_array_set_int(oCaster, sArray, nSpellLevel, nCount); + + // "You have " + IntToString(nCount) + " castings of spells of level " + IntToString(nSpellLevel) + " remaining" + sMessage = ReplaceChars(GetStringByStrRef(16828408), "", IntToString(nCount)); + sMessage = ReplaceChars(sMessage, "", IntToString(nSpellLevel)); + } + FloatingTextStringOnCreature(sMessage, oCaster, FALSE); + + // Arcane classes roll ASF if the spell has a somatic component + // OR if the spell has a vocal component, silence and deafness can cause failure + if(ArcaneSpellFailure(oCaster, nCastingClass, nSpellLevel, nMetamagic, sComponent) + || SilenceDeafnessFailure(oCaster, nSpellLevel, nMetamagic, sComponent)) + return FALSE; + + return TRUE; + } + return TRUE; +} + +int MaterialComponents(object oCaster, int nSpellID, int nCastingClass, object oSpellCastItem) +{ + int nSwitch = GetPRCSwitch(PRC_MATERIAL_COMPONENTS); + + if(!nSwitch) + return TRUE; + + // exceptions + if(GetHasFeat(FEAT_IGNORE_MATERIALS, oCaster) //caster has ignore material components feat + || GetIsDM(oCaster) || GetIsDMPossessed(oCaster) //caster is DM + || oSpellCastItem != OBJECT_INVALID //spell was cast from an item + || nCastingClass == CLASS_TYPE_RUNESCARRED || GetLocalInt(oCaster, "SpellIsSLA"))//spell is a spell-like ability + { + return TRUE; + } + + // Components and Names + string sComp1 = Get2DACache("prc_spells", "Component1", nSpellID); + string sCompName1 = Get2DACache("prc_spells", "CompName1", nSpellID); + string sComp2 = Get2DACache("prc_spells", "Component2", nSpellID); + string sCompName2 = Get2DACache("prc_spells", "CompName2", nSpellID); + string sComp3 = Get2DACache("prc_spells", "Component3", nSpellID); + string sCompName3 = Get2DACache("prc_spells", "CompName3", nSpellID); + string sComp4 = Get2DACache("prc_spells", "Component4", nSpellID); + string sCompName4 = Get2DACache("prc_spells", "CompName4", nSpellID); + int nGold = StringToInt(Get2DACache("prc_spells", "GP", nSpellID)); + + // These are set to false if the spell has a component + int nHasComp1 = sComp1 == "" ? TRUE: FALSE; + int nHasComp2 = sComp2 == "" ? TRUE: FALSE; + int nHasComp3 = sComp3 == "" ? TRUE: FALSE; + int nHasComp4 = sComp4 == "" ? TRUE: FALSE; + + // The spell doesn't require any material components + if(nHasComp1 && nHasComp2 && nHasComp3 && nHasComp4 && !nGold) + return TRUE; + + // Set the return value to false + int nReturn = FALSE; + + // Set test variables + int nComponents = TRUE; + int nCost = TRUE; + + // Component Objects to destroy + object oComp1, oComp2, oComp3, oComp4; + + string sSpell = GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellID))); + + if(nSwitch == 1 || nSwitch == 3) + { + nComponents = FALSE; + + // Check if caster has spell component pouch + object oPouch = GetItemPossessedBy(oCaster, "prc_spellpouch"); + + if((GetHasFeat(FEAT_ESCHEW_MATERIALS, oCaster) || GetIsObjectValid(oPouch)) + && nGold < 1) + { + nComponents = TRUE; + } + else + { + string sMes = "Material component missing: "; + + // Look for items in players inventory + if(!nHasComp1) + { + oComp1 = GetItemPossessedBy(oCaster, sComp1); + if(GetIsObjectValid(oComp1)) + nHasComp1 = TRUE; + else + FloatingTextStringOnCreature(sMes + sCompName1, oCaster, FALSE); + } + + if(!nHasComp2) + { + oComp2 = GetItemPossessedBy(oCaster, sComp2); + if(GetIsObjectValid(oComp2)) + nHasComp2 = TRUE; + else + FloatingTextStringOnCreature(sMes + sCompName2, oCaster, FALSE); + } + + if(!nHasComp3) + { + oComp3 = GetItemPossessedBy(oCaster, sComp3); + if(GetIsObjectValid(oComp3)) + nHasComp3 = TRUE; + else + FloatingTextStringOnCreature(sMes + sCompName3, oCaster, FALSE); + } + + if(!nHasComp4) + { + oComp4 = GetItemPossessedBy(oCaster, sComp4); + if(GetIsObjectValid(oComp4)) + nHasComp4 = TRUE; + else + FloatingTextStringOnCreature(sMes + sCompName4, oCaster, FALSE); + } + } + + if(nHasComp1 && nHasComp2 && nHasComp3 && nHasComp4) + nComponents = TRUE; + else + FloatingTextStringOnCreature("You do not have the appropriate material components to cast " + sSpell, oCaster, FALSE); + } + if(nSwitch == 2 || nSwitch == 3) + { + nCost = FALSE; + + // Now check to see if they have enough gold + if(GetGold(oCaster) >= nGold) + nCost = TRUE; + else + FloatingTextStringOnCreature("You do not have enough gold to cast " + sSpell, oCaster, FALSE); + } + + // Checked for the spell components, now the final test. + if(nComponents && nCost) + { + // We've got all the components + nReturn = TRUE; + + if(nSwitch == 1 || nSwitch == 3) + { + int nStack = 0; + + // Component 1 + nStack = GetNumStackedItems(oComp1); + + if(nStack > 1) + DelayCommand(0.6, SetItemStackSize (oComp1, --nStack)); + else + DelayCommand(0.6, DestroyObject(oComp1)); + + // Component 2 + nStack = GetNumStackedItems(oComp2); + + if(nStack > 1) + DelayCommand(0.6, SetItemStackSize (oComp2, --nStack)); + else + DelayCommand(0.6, DestroyObject(oComp2)); + + // Component 3 + nStack = GetNumStackedItems(oComp3); + + if(nStack > 1) + DelayCommand(0.6, SetItemStackSize (oComp3, --nStack)); + else + DelayCommand(0.6, DestroyObject(oComp3)); + + // Component 4 + nStack = GetNumStackedItems(oComp4); + + if(nStack > 1) + DelayCommand(0.6, SetItemStackSize (oComp4, --nStack)); + else + DelayCommand(0.6, DestroyObject(oComp4)); + } + if(nSwitch == 2 || nSwitch == 3) + { + TakeGoldFromCreature(nGold, oCaster, TRUE); + } + } + + // return our value + return nReturn; +} + +int SpellRestrictClass(int nCastingClass, int nSwitch) +{ + if(nSwitch == 3) + return TRUE; + if(nSwitch == 1) + return FALSE; + + //else nSwitch == 2 + if(nCastingClass == CLASS_TYPE_CLERIC + || nCastingClass == CLASS_TYPE_FAVOURED_SOUL + || nCastingClass == CLASS_TYPE_OCULAR + || nCastingClass == CLASS_TYPE_SHAMAN + || nCastingClass == CLASS_TYPE_HEALER + || nCastingClass == CLASS_TYPE_TEMPLAR) + return TRUE; + + return FALSE; +} + +int SpellAlignmentRestrictions(object oCaster, int nSpellID, int nCastingClass) +{ + int nSwitch = GetPRCSwitch(PRC_SPELL_ALIGNMENT_RESTRICT); + + if(!nSwitch) + return TRUE; + + int nShift = GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT); + + int nAlignGE = GetGoodEvilValue(oCaster); + int nAlignLC = GetLawChaosValue(oCaster); + int nAdjust; + int nDescriptor = GetLocalInt(oCaster, PRC_DESCRIPTOR); + if(!nDescriptor) + nDescriptor = GetDescriptorFlags(nSpellID); + + if(nDescriptor & DESCRIPTOR_EVIL) + { + if(nAlignGE > 69 && SpellRestrictClass(nCastingClass, nSwitch)) + { + FloatingTextStringOnCreature("Your alignment prohibits casting spells with this descriptor!", oCaster, FALSE); + return FALSE; + } + else if (nShift) + { + nAdjust = FloatToInt(sqrt(IntToFloat(nAlignGE)) / 2); + if(nAdjust) AdjustAlignment(oCaster, ALIGNMENT_EVIL, nAdjust, FALSE); + } + } + if(nDescriptor & DESCRIPTOR_GOOD) + { + if(nAlignGE < 31 && SpellRestrictClass(nCastingClass, nSwitch)) + { + FloatingTextStringOnCreature("Your alignment prohibits casting spells with this descriptor!", oCaster, FALSE); + return FALSE; + } + else if (nShift) + { + nAdjust = FloatToInt(sqrt(IntToFloat(100 - nAlignGE)) / 2); + if(nAdjust) AdjustAlignment(oCaster, ALIGNMENT_GOOD, nAdjust, FALSE); + } + } + if(nDescriptor & DESCRIPTOR_LAWFUL) + { + if(nAlignLC < 31 && SpellRestrictClass(nCastingClass, nSwitch)) + { + FloatingTextStringOnCreature("Your alignment prohibits casting spells with this descriptor!", oCaster, FALSE); + return FALSE; + } + else if (nShift) + { + nAdjust = FloatToInt(sqrt(IntToFloat(100 - nAlignLC)) / 2); + if(nAdjust) AdjustAlignment(oCaster, ALIGNMENT_LAWFUL, nAdjust, FALSE); + } + } + if(nDescriptor & DESCRIPTOR_CHAOTIC) + { + if(nAlignLC > 69 && SpellRestrictClass(nCastingClass, nSwitch)) + { + FloatingTextStringOnCreature("Your alignment prohibits casting spells with this descriptor!", oCaster, FALSE); + return FALSE; + } + else if (nShift) + { + nAdjust = FloatToInt(sqrt(IntToFloat(nAlignLC)) / 2); + if(nAdjust) AdjustAlignment(oCaster, ALIGNMENT_CHAOTIC, nAdjust, FALSE); + } + } + return TRUE; +} + +int RedWizRestrictedSchool(object oCaster, int nSchool, int nCastingClass, object oSpellCastItem) +{ + // No need for wasting CPU on non-Red Wizards + if(GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster)) + { + //can’t cast prohibited spells from scrolls or fire them from wands + if(GetIsObjectValid(oSpellCastItem)) + { + int nType = GetBaseItemType(oSpellCastItem); + if(nType != BASE_ITEM_MAGICWAND + && nType != BASE_ITEM_ENCHANTED_WAND + && nType != BASE_ITEM_SCROLL + && nType != BASE_ITEM_SPELLSCROLL + && nType != BASE_ITEM_ENCHANTED_SCROLL) + return TRUE; + } + + // Determine forbidden schools + int iRWRes; + switch(nSchool) + { + case SPELL_SCHOOL_ABJURATION: iRWRes = FEAT_RW_RES_ABJ; break; + case SPELL_SCHOOL_CONJURATION: iRWRes = FEAT_RW_RES_CON; break; + case SPELL_SCHOOL_DIVINATION: iRWRes = FEAT_RW_RES_DIV; break; + case SPELL_SCHOOL_ENCHANTMENT: iRWRes = FEAT_RW_RES_ENC; break; + case SPELL_SCHOOL_EVOCATION: iRWRes = FEAT_RW_RES_EVO; break; + case SPELL_SCHOOL_ILLUSION: iRWRes = FEAT_RW_RES_ILL; break; + case SPELL_SCHOOL_NECROMANCY: iRWRes = FEAT_RW_RES_NEC; break; + case SPELL_SCHOOL_TRANSMUTATION: iRWRes = FEAT_RW_RES_TRS; break; + } + + // Compare the spell's school versus the restricted schools + if(iRWRes && GetHasFeat(iRWRes, oCaster)) + { + FloatingTextStrRefOnCreature(16822359, oCaster, FALSE); // "You cannot cast spells of your prohibited schools. Spell terminated." + return FALSE; + } + // Other arcane casters cannot benefit from red wizard bonuses + if(GetIsArcaneClass(nCastingClass) && nCastingClass != CLASS_TYPE_WIZARD) + { + FloatingTextStringOnCreature("You have attempted to illegaly merge another arcane caster with a Red Wizard. All spellcasting will now fail.", oCaster, FALSE); + return FALSE; + } + } + + return TRUE; +} + +int PnPSpellSchools(object oCaster, int nCastingClass, int nSchool, object oSpellCastItem) +{ + if(GetPRCSwitch(PRC_PNP_SPELL_SCHOOLS) + && nCastingClass == CLASS_TYPE_WIZARD) + { + //can’t cast prohibited spells from scrolls or fire them from wands + if(GetIsObjectValid(oSpellCastItem)) + { + int nType = GetBaseItemType(oSpellCastItem); + if(nType != BASE_ITEM_MAGICWAND + && nType != BASE_ITEM_ENCHANTED_WAND + && nType != BASE_ITEM_SCROLL + && nType != BASE_ITEM_SPELLSCROLL + && nType != BASE_ITEM_ENCHANTED_SCROLL) + return TRUE; + } + + int nFeat; + switch(nSchool) + { + case SPELL_SCHOOL_ABJURATION: nFeat = 2265; break; + case SPELL_SCHOOL_CONJURATION: nFeat = 2266; break; + case SPELL_SCHOOL_DIVINATION: nFeat = 2267; break; + case SPELL_SCHOOL_ENCHANTMENT: nFeat = 2268; break; + case SPELL_SCHOOL_EVOCATION: nFeat = 2269; break; + case SPELL_SCHOOL_ILLUSION: nFeat = 2270; break; + case SPELL_SCHOOL_NECROMANCY: nFeat = 2271; break; + case SPELL_SCHOOL_TRANSMUTATION: nFeat = 2272; break; + default: nFeat = 0; + } + if(nFeat && GetHasFeat(nFeat, oCaster)) + { + FloatingTextStringOnCreature("You cannot cast spells of an opposition school.", oCaster, FALSE); + return FALSE; + } + } + + return TRUE; +} + +int ShifterCasting(object oCaster, object oSpellCastItem, int nSpellLevel, int nMetamagic, string sComponent) +{ + // The variable tells that the new form is unable to cast spells (very inaccurate, determined by racial type) + // with somatic or vocal components and is lacking Natural Spell feat + if(GetLocalInt(oCaster, "PRC_Shifting_RestrictSpells")) + { + if(GetIsObjectValid(oSpellCastItem)) + { + // Potion drinking is not restricted + if(GetBaseItemType(oSpellCastItem) == BASE_ITEM_ENCHANTED_POTION + || GetBaseItemType(oSpellCastItem) == BASE_ITEM_POTIONS) + return TRUE; + + //OnHit properties on equipped items not restricted + int nSlot; + for(nSlot = 0; nSlot= 13) + { + nMax = 10; + nVal = 2; + } + for(i=1; i<=nMax; i++) + { + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER" + IntToString(i) , nSID); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_L" + IntToString(i), nCasterLevel); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_M" + IntToString(i), nMetamagic); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_D" + IntToString(i), PRCGetSaveDC(oTarget, oCaster)); + } + SetLocalInt(oItem, "X2_L_NUMCHANNELTRIGGERS", nMax); + //mark it as discharging + SetLocalInt(oItem, "DuskbladeChannelDischarge", nVal); + DelayCommand(fDelay, DuskbladeCleanUp(oItem, nMax)); + + itemproperty ipTest = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1); + IPSafeAddItemProperty(oItem ,ipTest, fDelay); + + //make attack + ClearAllActions(); + effect eNone; + if (nClass >= 13) PerformAttackRound(oTarget, oCaster, eNone, 0.0, 0, 0, 0, FALSE, "Arcane Channelling Hit", "Arcane Channelling Miss"); + else if (nClass >= 13) PerformAttack(oTarget, oCaster, eNone, 0.0, 0, 0, 0, "Arcane Channelling Hit", "Arcane Channelling Miss"); + // Target is valid and we know it's an enemy and we're in combat + DelayCommand(0.25, AssignCommand(oCaster, ActionAttack(oTarget))); + FloatingTextStringOnCreature("Duskblade Channeling Deactivated", oCaster, FALSE); + DeleteLocalInt(oCaster, "DuskbladeChannelActive"); + return FALSE; + } + } + + return TRUE; +} + +//PnP familiar - deliver touch spell +int DeliverTouchSpell(object oCaster, object oTarget, int nSpellID, int nCasterLevel, int nSaveDC, int nMetamagic, object oSpellCastItem) +{ + if(GetPRCSwitch(PRC_PNP_FAMILIARS)) + { + // Don't channel from objects + if(oSpellCastItem != OBJECT_INVALID) + return TRUE; + + if(GetAssociateTypeNPC(oTarget) == ASSOCIATE_TYPE_FAMILIAR && GetMasterNPC(oTarget) == oCaster) + { + if(GetHitDice(oTarget) > 2) + { + if(!GetLocalInt(oCaster, "PRC_SPELL_HOLD") //holding the charge doesnt work + && (Get2DACache("spells", "Range", nSpellID) == "T")) //only touch spells + { + //find the item + object oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget); + if(!GetIsObjectValid(oItem)) oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget); + if(!GetIsObjectValid(oItem)) oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget); + if(GetIsObjectValid(oItem) + && !GetLocalInt(oItem, "X2_L_NUMCHANNELTRIGGERS")) + { + //valid spell, store + //very similar to duskblade chanelling + effect eVisual = EffectVisualEffect(VFX_IMP_BREACH); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, oTarget); + //NOTE: I add +1 to the SpellId to spell 0 can be used to trap failure + int nSID = nSpellID + 1; + + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER" , nSID); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_L", nCasterLevel); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_M", nMetamagic); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_D", nSaveDC); + SetLocalInt(oItem, "X2_L_NUMCHANNELTRIGGERS", 1); + DelayCommand(60.0, DuskbladeCleanUp(oItem, 1)); + + itemproperty ipTest = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1); + IPSafeAddItemProperty(oItem, ipTest, 60.0); + + return FALSE;//don't cast + } + } + } + } + } + + return TRUE; +} + +void SpellSharing(object oCaster, object oTarget, int nSpellID, int nCasterLevel, int nSaveDC, int nMetamagic, object oSpellCastItem) +{ + if((Get2DACache("spells", "Range", nSpellID) == "P" || oTarget == oCaster) // Either of these is legal + && (Get2DACache("prc_spells", "NoShare", nSpellID) != "1") + && !GetLocalInt(oCaster, "PRC_SPELL_HOLD") //holding the charge doesnt work + && !GetLocalInt(oCaster, "SpellIsSLA") // no spell-like abilities + && !GetIsObjectValid(oSpellCastItem)) // no item spells + { + int bAll = GetPRCSwitch(PRC_ENABLE_SPELL_SHARING); //enables spell sharing for all compaions (bioware and PnP) + int bFam = GetPRCSwitch(PRC_PNP_FAMILIARS); //enables spell sharing only for PnP familiars + int bComp = GetPRCSwitch(PRC_PNP_ANIMAL_COMPANIONS); //enables spell sharing only for PnP animal companions + int bBond = GetLevelByClass(CLASS_TYPE_BONDED_SUMMONNER, oCaster); + + location lCaster = GetLocation(oCaster); + + //RADIUS_SIZE_MEDIUM = 10 feet - the source book says it's 5, but that will make it very difficult to use in NWN + object oComp = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lCaster, TRUE, OBJECT_TYPE_CREATURE); + while(GetIsObjectValid(oComp)) + { + if(GetMasterNPC(oComp) == oCaster) + { + int nType = GetAssociateTypeNPC(oComp); + + if((nType == ASSOCIATE_TYPE_FAMILIAR && (bAll || bFam || bBond)) + || (nType == ASSOCIATE_TYPE_ANIMALCOMPANION && (bAll || bComp)) + || nType == ASSOCIATE_TYPE_CELESTIALCOMPANION + || oComp == GetLocalObject(oCaster, "oX3PaladinMount")) + { + AssignCommand(oComp, ClearAllActions()); + AssignCommand(oComp, ActionCastSpell(nSpellID, nCasterLevel, 0, nSaveDC, nMetamagic, CLASS_TYPE_INVALID, FALSE, TRUE, oComp)); + } + } + oComp = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lCaster, TRUE, OBJECT_TYPE_CREATURE); + } + } +} + +void DraconicFeatsOnSpell(object oCaster, object oTarget, object oSpellCastItem, int nSpellLevel, int nCastingClass) +{ + //ensure the spell is arcane + if(!GetIsArcaneClass(nCastingClass, oCaster)) + return; + + ///////Draconic Vigor//// + if(GetHasFeat(FEAT_DRACONIC_VIGOR, oCaster)) + { + effect eHeal = EffectHeal(nSpellLevel); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oCaster); + } + + ///////Draconic Armor//// + if(GetHasFeat(FEAT_DRACONIC_ARMOR, oCaster)) + { + effect eDamRed = EffectDamageReduction(nSpellLevel, DAMAGE_POWER_PLUS_ONE); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDamRed, oCaster, 6.0f); + } + + ///////Draconic Persuasion//// + if(GetHasFeat(FEAT_DRACONIC_PERSUADE, oCaster)) + { + int nBonus = FloatToInt(1.5f * IntToFloat(nSpellLevel)); + effect eCha = EffectSkillIncrease(SKILL_BLUFF, nBonus); + effect eCha2 = EffectSkillIncrease(SKILL_PERFORM, nBonus); + effect eCha3 = EffectSkillIncrease(SKILL_INTIMIDATE, nBonus); + effect eLink = EffectLinkEffects(eCha, eCha2); + eLink = EffectLinkEffects(eLink, eCha3); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oCaster, 6.0f); + } + + ///////Draconic Presence//// + if(GetHasFeat(FEAT_DRACONIC_PRESENCE, oCaster)) + { + //set up checks + object oScare; + int bCreaturesLeft = TRUE; + int nNextCreature = 1; + + //set up fear effects + effect eVis = EffectVisualEffect(VFX_IMP_FEAR_S); + effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE); + + effect eLink = EffectLinkEffects(EffectShaken(), eDur); + + int nDC = 10 + nSpellLevel + GetAbilityModifier(ABILITY_CHARISMA, oCaster); + int nDuration = 6 * nSpellLevel; + + //cycle through creatures within the AoE + while(bCreaturesLeft) + { + oScare = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oCaster, nNextCreature, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + + if(oScare == OBJECT_INVALID) + bCreaturesLeft = FALSE; + + if(oScare != oCaster && GetDistanceToObject(oScare) < FeetToMeters(15.0)) + { + //dragons are immune, so make sure it's not a dragon + if(MyPRCGetRacialType(oScare)!= RACIAL_TYPE_DRAGON) + { + //Fire cast spell at event for the specified target + SignalEvent(oScare, EventSpellCastAt(oCaster, SPELLABILITY_AURA_FEAR)); + //Make a saving throw check + if(!PRCMySavingThrow(SAVING_THROW_WILL, oScare, nDC, SAVING_THROW_TYPE_FEAR) && !GetIsImmune(oScare, IMMUNITY_TYPE_FEAR) && !GetIsImmune(oScare, IMMUNITY_TYPE_MIND_SPELLS)) + { + //Apply the VFX impact and effects + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oScare, RoundsToSeconds(nDuration)); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oScare); + }//end will save processing + } //end dragon check + nNextCreature++; + }//end target check + //if no more creatures within range, end it + else + bCreaturesLeft = FALSE; + }//end while + } + + ///////Draconic Claw//// + if(GetHasFeat(FEAT_DRACONIC_CLAW, oCaster)) + { + // Clawswipes only work on powers manifested by the Diamond Dragon, not by items he uses. + if(oSpellCastItem != OBJECT_INVALID) + { + FloatingTextStringOnCreature("You do not gain clawswipes from Items.", oCaster, FALSE); + return; + } + + //get the proper sized claw + string sResRef = "prc_claw_1d6m_"; + sResRef += GetAffixForSize(PRCGetCreatureSize(oCaster)); + object oClaw = GetObjectByTag(sResRef); + effect eInvalid; + + if(TakeSwiftAction(oCaster)) + { + //grab the closest enemy to swipe at + oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, oCaster, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + if(oTarget != oCaster && GetDistanceToObject(oTarget) < FeetToMeters(15.0)) + { + PerformAttack(oTarget, oCaster, eInvalid, 0.0, 0, 0, DAMAGE_TYPE_SLASHING, "*Clawswipe Hit*", "*Clawswipe Missed*", FALSE, oClaw); + } + } + } +} + +void DazzlingIllusion(object oCaster, int nSchool) +{ + // No need for wasting CPU on non-Dazzles + if(GetHasFeat(FEAT_DAZZLING_ILLUSION, oCaster)) + { + if(nSchool == SPELL_SCHOOL_ILLUSION) + { + effect eLink = EffectLinkEffects(EffectDazzle(), EffectVisualEffect(VFX_IMP_BLINDDEAD_DN_CYAN)); + location lTarget = GetLocation(oCaster); + object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), lTarget, TRUE, OBJECT_TYPE_CREATURE); + //Cycle through the targets within the spell shape until an invalid object is captured. + while(GetIsObjectValid(oTarget)) + { + if(!GetIsFriend(oTarget, oCaster) && !PRCGetHasEffect(EFFECT_TYPE_BLINDNESS, oTarget)) + { + DelayCommand(0.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, 6.0)); + } + //Select the next target within the spell shape. + oTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(30.0), lTarget, TRUE, OBJECT_TYPE_CREATURE); + } + } + } +} + +void EnergyAbjuration(object oCaster, int nSchool, int nSpellLevel) +{ + // No need for wasting CPU on non-Abjures + if(GetHasFeat(FEAT_ENERGY_ABJURATION, oCaster)) + { + if(nSchool == SPELL_SCHOOL_ABJURATION) + { + int nAmount = (1 + nSpellLevel) * 5; + + int nDamageType = DAMAGE_TYPE_ACID | DAMAGE_TYPE_COLD | DAMAGE_TYPE_ELECTRICAL | DAMAGE_TYPE_FIRE | DAMAGE_TYPE_SONIC; + + effect eResist = EffectDamageResistance(nDamageType, nAmount, nAmount); + + effect eDur = EffectVisualEffect(VFX_DUR_PROTECTION_ELEMENTS); + effect eVfx = EffectVisualEffect(VFX_IMP_ELEMENTAL_PROTECTION); + effect eEnd = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE); + + effect eLink = EffectLinkEffects(eResist, eDur); + eLink = EffectLinkEffects(eLink, eEnd); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVfx, oCaster); + + + /* eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_FIRE, nAmount, nAmount)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_ACID, nAmount, nAmount)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_SONIC, nAmount, nAmount)); + eLink = EffectLinkEffects(eLink, EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nAmount, nAmount)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_PROTECTION_ELEMENTS)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_IMP_ELEMENTAL_PROTECTION)); + eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE)); */ + + if (DEBUG) DoDebug("Energy Abjuration triggered! DR Amount: " + IntToString(nAmount)); + DelayCommand(0.0, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oCaster)); + } + } +} + +void InsightfulDivination(object oCaster, int nSchool, int nSpellLevel) +{ + if(GetHasFeat(FEAT_INSIGHTFUL_DIVINATION, oCaster)) + { + if(nSchool == SPELL_SCHOOL_DIVINATION) + { + int nAmount = 1 + nSpellLevel; + SetLocalInt(oCaster, "InsightfulDivination", nAmount); + } + } +} + +void TougheningTransmutation(object oCaster, int nSchool) +{ + if (DEBUG) DoDebug("Toughening Transmutation called"); + + if(GetHasFeat(FEAT_TOUGHENING_TRANSMUTATION, oCaster)) + { + if(nSchool == SPELL_SCHOOL_TRANSMUTATION) + { + effect eDR = EffectDamageReduction(5, DAMAGE_POWER_PLUS_ONE); + + if (DEBUG) DoDebug("School Detected: " + IntToString(nSchool)); + DelayCommand(0.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDR, oCaster, 7.0)); + } + } + else + { + if (DEBUG) DoDebug("You do not have the Toughening Transmutation feat."); + } +} + +void CloudyConjuration(object oCaster, int nSchool, object oSpellCastItem) +{ + if(GetHasFeat(FEAT_CLOUDY_CONJURATION, oCaster) && !GetIsObjectValid(oSpellCastItem)) // Doesn't work on spells cast from items. + { + if(nSchool == SPELL_SCHOOL_CONJURATION) + { + effect eAOE = EffectAreaOfEffect(VFX_MOB_CLOUDY_CONJURATION); + DelayCommand(0.0, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAOE, PRCGetSpellTargetLocation(), 6.0)); + } + } +} + +//ebonfowl: runs the reserve feat script +void OnePingOnly(object oCaster) +{ + //Only works if you have a reserve feat + if(GetLocalInt(oCaster, "ReserveFeatsRunning") == TRUE) + { + DelayCommand(1.0, ExecuteScript("prc_reservefeat", oCaster)); + } +} + +//ebonfowl: runs the damage backlash for Mystic Backlash +void MysticBacklash(object oCaster) +{ + int nDamage = GetLocalInt(oCaster, "DoMysticBacklash"); + effect eBacklash = EffectDamage(nDamage, DAMAGE_TYPE_MAGICAL); + eBacklash = SupernaturalEffect(eBacklash); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eBacklash, oCaster); +} + +//spellweave affects only one target, mark it here +void EldritchSpellweave(object oCaster, object oTarget, int nSpellLevel, int bSpellIsHostile) +{ + if(GetLocalInt(oCaster, "INV_SPELLWEAVE")) + { + //we need a valid target + if(!GetIsObjectValid(oTarget)) + return; + + //hostile spell + if(!GetIsEnemy(oTarget, oCaster)) + return; + + //and active blast essence + if(!GetLocalInt(oCaster, "BlastEssence") + || GetLocalInt(oCaster, "BlastEssence") == INVOKE_CORRUPTING_BLAST) + return; + + //final test: spell level >= essence level + if(nSpellLevel >= (GetLocalInt(oCaster, "EssenceData") & 0xF)) + { + //everything is OK, mark the target for eldritch spellweave + SetLocalObject(oCaster, "SPELLWEAVE_TARGET", oTarget); + DeleteLocalInt(oCaster, "INV_SPELLWEAVE"); + } + else + SendMessageToPC(oCaster, "Eldritch Spellweave: The level of this spell is too low to use with current blast essence."); + } +} + +//:: Returns True if oPC has a Secondary PrC that should prevent them from using +//:: the Bioware spellbook as a Sublime Chord +int CheckSecondaryPrC(object oPC = OBJECT_SELF) +{ + if(DEBUG) DoDebug("x2_inc_spellhook: CheckSecondaryPrC >>> Starting", oPC); + + int bBard = GetLevelByClass(CLASS_TYPE_BARD, oPC); + int bBeguiler = GetLevelByClass(CLASS_TYPE_BEGUILER, oPC); + int bDuskblade = GetLevelByClass(CLASS_TYPE_DUSKBLADE, oPC); + int bSorcerer = GetLevelByClass(CLASS_TYPE_SORCERER, oPC); + int bWarmage = GetLevelByClass(CLASS_TYPE_WARMAGE, oPC); + + int nRace = GetRacialType(oPC); + + if (bBard) + { + if(DEBUG) DoDebug("x2_inc_spellhook: CheckSecondaryPrC >>> Entering Bard", oPC); + if (GetHasFeat(FEAT_FEY_SPELLCASTING_GLOURA)) return TRUE; + if (GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_AOTS_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ALIENIST_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ANIMA_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_BSINGER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_CMANCER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_DHEART_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_DSONG_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_GRAZZT_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_HARPERM_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_HATHRAN_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_HAVOC_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_JPM_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_MAESTER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_MHARPER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_SSWORD_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_TIAMAT_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_UNSEEN_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_BARD)) return TRUE; + if (GetHasFeat(FEAT_WWOC_SPELLCASTING_BARD)) return TRUE; + } + else if (bBeguiler) + { + if (GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_AOTS_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ALIENIST_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ANIMA_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_BSINGER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_CMANCER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_DHEART_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_DSONG_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_GRAZZT_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_HARPERM_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_HATHRAN_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_HAVOC_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_JPM_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_MAESTER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_MHARPER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_SSWORD_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_TIAMAT_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_UNSEEN_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_BEGUILER)) return TRUE; + if (GetHasFeat(FEAT_WWOC_SPELLCASTING_BEGUILER)) return TRUE; + + + + + } + else if (bDuskblade) + { + if (GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_AOTS_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ANIMA_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_BSINGER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_CMANCER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_DHEART_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_DSONG_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_GRAZZT_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_HARPERM_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_HATHRAN_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_HAVOC_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_JPM_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_MAESTER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_MHARPER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_SSWORD_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_TIAMAT_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_TNECRO_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_UNSEEN_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_DUSKBLADE)) return TRUE; + if (GetHasFeat(FEAT_WWOC_SPELLCASTING_DUSKBLADE)) return TRUE; + + + } + else if (bSorcerer) + { + if (GetHasFeat(FEAT_ABERRATION_SPELLCASTING_DRIDER)) return TRUE; + if (GetHasFeat(FEAT_MONSTROUS_SPELLCASTING_ARKAMOI)) return TRUE; + if (GetHasFeat(FEAT_MONSTROUS_SPELLCASTING_MARRUTACT)) return TRUE; + if (GetHasFeat(FEAT_MONSTROUS_SPELLCASTING_REDSPAWN_ARCANISS)) return TRUE; + if (GetHasFeat(FEAT_OUTSIDER_SPELLCASTING_RAKSHASA)) return TRUE; + if (GetHasFeat(FEAT_SHAPECHANGER_SPELLCASTING_ARANEA)) return TRUE; + if (GetHasFeat(FEAT_ABCHAMP_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_AOTS_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ALIENIST_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ANIMA_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_BSINGER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_BONDED_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_CMANCER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_DHEART_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_DSONG_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_FMM_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_GRAZZT_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_HARPERM_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_HATHRAN_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_HAVOC_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_JPM_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_MAESTER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_MHARPER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_SSWORD_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_TIAMAT_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_TNECRO_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_UNSEEN_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_WAYFARER_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_WWOC_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_SORCERER)) return TRUE; + if (GetHasFeat(FEAT_WWOC_SPELLCASTING_SORCERER)) return TRUE; + } + else if (bWarmage) + { + if (GetHasFeat(FEAT_AOTS_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ALCHEM_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ANIMA_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ARCHMAGE_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ARCTRICK_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ASMODEUS_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_BSINGER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_BLDMAGUS_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_BONDED_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_CMANCER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_DIABOLIST_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_DHEART_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_DSONG_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_EKNIGHT_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ENLIGHTENEDFIST_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ELESAVANT_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ETHEURGE_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_FMM_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_FROSTMAGE_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_FOCHLUCAN_LYRIST_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_GRAZZT_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_HARPERM_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_HATHRAN_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_HAVOC_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_JPM_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_JUDICATOR_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_MAESTER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_MAGEKILLER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_MHARPER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_MYSTIC_THEURGE_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_NOCTUMANCER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_OOZEMASTER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_PALEMASTER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_RAGEMAGE_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_SHADOWADEPT_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_SOULCASTER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_SPELLDANCER_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_SSWORD_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_TIAMAT_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_ULTMAGUS_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_UNSEEN_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_VIRTUOSO_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_WILDMAGE_SPELLCASTING_WARMAGE)) return TRUE; + if (GetHasFeat(FEAT_WWOC_SPELLCASTING_WARMAGE)) return TRUE; + } + return FALSE; +} + +int BardSorcPrCCheck(object oCaster, int nCastingClass, object oSpellCastItem) +{ + // If it's an item, get the hell out of here + if (GetIsObjectValid(oSpellCastItem)) + return TRUE; + + // Eldritch Spellblast was breaking otherwise + if (GetLocalInt(oCaster, "EldritchSpellBlast")) + { + if(DEBUG) DoDebug("x2_inc_spellhook >> EldritchSpellBlast Found"); + return TRUE; + } + + //check its a sorc spell + if(nCastingClass == CLASS_TYPE_SORCERER) + { + if (CheckSecondaryPrC(oCaster) == TRUE) + { + if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sorcerer w/RHD found.", oCaster); + return TRUE; + } + //no need to check further if new spellbooks are disabled + if(GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK)) + return TRUE; + //check they have sorc levels + if(!GetLevelByClass(CLASS_TYPE_SORCERER, oCaster)) + return TRUE; + //check if they are casting via new spellbook + if(GetLocalInt(oCaster, "NSB_Class") != CLASS_TYPE_SORCERER && GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCaster)) + return FALSE; + //check if they are casting via new spellbook + if(GetLocalInt(oCaster, "NSB_Class") == CLASS_TYPE_SORCERER) + return TRUE; + if(GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster) > 0 && CheckSecondaryPrC(oCaster) == TRUE) + { + if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sublime Chord w/RHD found.", oCaster); + FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE); + return FALSE; + } + if (CheckSecondaryPrC(oCaster) == TRUE) + { + if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sorcerer w/RHD found.", oCaster); + FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE); + return FALSE; + } + //check they have arcane PrC or Draconic Arcane Grace/Breath + if(!(GetArcanePRCLevels(oCaster, nCastingClass) - GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster)) + && !(GetHasFeat(FEAT_DRACONIC_GRACE, oCaster) || GetHasFeat(FEAT_DRACONIC_BREATH, oCaster))) + return TRUE; + + //check they have sorc in first arcane slot + //if(GetPrimaryArcaneClass() != CLASS_TYPE_SORCERER) + if(GetPrCAdjustedCasterLevelByType(TYPE_ARCANE, oCaster, TRUE) != GetPrCAdjustedCasterLevel(CLASS_TYPE_SORCERER, oCaster, TRUE)) + return TRUE; + + //at this point, they must be using the bioware spellbook + //from a class that adds to sorc + FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE); + return FALSE; + } + + //check its a bard spell + if(nCastingClass == CLASS_TYPE_BARD) + { + if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> nCastingClass is Bard.", oCaster); + //no need to check further if new spellbooks are disabled + if(GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK)) + { + if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> PRC_BARD_DISALLOW_NEWSPELLBOOK.", oCaster); + return TRUE; + } + //check they have bard levels + if(!GetLevelByClass(CLASS_TYPE_BARD, oCaster)) + { + if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Not a bard.", oCaster); + return TRUE; + } + //check if they are casting via new spellbook + if(GetLocalInt(oCaster, "NSB_Class") == CLASS_TYPE_BARD) + { + if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Using new spellbook.", oCaster); + return TRUE; + } + if(GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster) > 0 && CheckSecondaryPrC(oCaster) == TRUE) + { + if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Sublime Chord w/RHD found.", oCaster); + FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE); + return FALSE; + } + if (CheckSecondaryPrC(oCaster) == TRUE) + { + if (DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Bard w/RHD found.", oCaster); + FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE); + return FALSE; + } + //check they have arcane PrC or Draconic Arcane Grace/Breath + if(!(GetArcanePRCLevels(oCaster, nCastingClass) - GetLevelByClass(CLASS_TYPE_SUBLIME_CHORD, oCaster)) + && !(GetHasFeat(FEAT_DRACONIC_GRACE, oCaster) || GetHasFeat(FEAT_DRACONIC_BREATH, oCaster))) + { + if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> First Sublime Chord check.", oCaster); + return TRUE; + } + + //check they have bard in first arcane slot + //if(GetPrimaryArcaneClass() != CLASS_TYPE_BARD) + if(GetPrCAdjustedCasterLevelByType(TYPE_ARCANE, oCaster, TRUE) != GetPrCAdjustedCasterLevelByType(CLASS_TYPE_BARD, oCaster, TRUE)) + { + if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> GetPrCAdjustedCasterLevelByType.", oCaster); + return TRUE; + } + //at this point, they must be using the bioware spellbook + //from a class that adds to bard + FloatingTextStringOnCreature("You must use the new spellbook on the class radial.", oCaster, FALSE); + return FALSE; + } + + if(DEBUG) DoDebug("x2_inc_spellhook: BardSorcPrCCheck >>> Returning TRUE.", oCaster); + return TRUE; +} + + +int KOTCHeavenDevotion(object oCaster, object oTarget, int nCasterAlignment, int nSpellSchool) +{ + if(GetLevelByClass(CLASS_TYPE_KNIGHT_CHALICE, oTarget) >= 5) + { + if(MyPRCGetRacialType(oCaster) == RACIAL_TYPE_OUTSIDER) + { + if(nCasterAlignment == ALIGNMENT_EVIL) + { + if(nSpellSchool == SPELL_SCHOOL_ENCHANTMENT) + { + return FALSE; + } + } + } + } + return TRUE; +} + +void CombatMedicHealingKicker(object oCaster, object oTarget, int nSpellID) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_COMBAT_MEDIC, oCaster); + int nKicker = GetLocalInt(oCaster, "Heal_Kicker"); + + if(!nLevel || !nKicker || oTarget == oCaster) //Cannot use on self + return; + + if(!GetIsOfSubschool(nSpellID, SUBSCHOOL_HEALING)) //If the spell that was just cast isn't healing, stop now + return; + + //Three if/elseif statements. They check which of the healing kickers we use. + //If no Healing Kicker localints are set, this if block should be ignored. + int bRemoveUses; + if(nKicker == 1) + { + /* Sanctuary effect, with special DC and 1 round duration + * Script stuff taken from the spell by the same name + */ + int nDC = 15 + nLevel + GetAbilityModifier(ABILITY_WISDOM, oCaster); + effect eLink = EffectLinkEffects(EffectVisualEffect(VFX_DUR_SANCTUARY), EffectSanctuary(nDC)); + + //Apply the Sanctuary VFX impact and effects + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, 6.0); + + bRemoveUses = TRUE; + } + else if(nKicker == 2) + { + /* Reflex save increase, 1 round duration + */ + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HASTE), oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSavingThrowIncrease(SAVING_THROW_REFLEX, nLevel), oTarget, 6.0); + + bRemoveUses = TRUE; + } + else if(nKicker == 3) + { + /* Aid effect, with special HP bonus and 1 minute duration + * Script stuff taken from the spell by the same name + */ + int nBonus = 8 + nLevel; + effect eLink = EffectLinkEffects(EffectAttackIncrease(1), + EffectSavingThrowIncrease(SAVING_THROW_ALL, 1, SAVING_THROW_TYPE_FEAR)); + + //Apply the Aid VFX impact and effects + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HOLY_AID), oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, 60.0); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectTemporaryHitpoints(nBonus), oTarget, 60.0); + + bRemoveUses = TRUE; + } + + if(bRemoveUses) + { + DeleteLocalInt(oCaster, "Heal_Kicker"); + DecrementRemainingFeatUses(oCaster, FEAT_HEALING_KICKER_1); + DecrementRemainingFeatUses(oCaster, FEAT_HEALING_KICKER_2); + DecrementRemainingFeatUses(oCaster, FEAT_HEALING_KICKER_3); + } +} + +// Performs the attack portion of the battlecast ability for the havoc mage +void Battlecast(object oCaster, object oTarget, object oSpellCastItem, int nSpellLevel) +{ + int nLevel = GetLevelByClass(CLASS_TYPE_HAVOC_MAGE, oCaster); + + // If battlecast is turned off, exit + if(!nLevel || !GetLocalInt(oCaster, "HavocMageBattlecast")) + return; + + // Battlecast only works on spells cast by the Havoc Mage, not by items he uses. + if(oSpellCastItem != OBJECT_INVALID) + { + FloatingTextStringOnCreature("You do not gain Battlecast from Items.", oCaster, FALSE); + return; + } + + //if its not being cast on a hostile target or its at a location + //get the nearest living seen hostile insead + if(!spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oCaster) + || !GetIsObjectValid(oTarget)) + { + oTarget = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, oCaster, 1, + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + } + + effect eVis = EffectVisualEffect(VFX_IMP_DIVINE_STRIKE_HOLY); + + // Don't want to smack allies upside the head when casting a spell. + if(spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oCaster) + && oTarget != oCaster + && GetDistanceToObject(oTarget) < FeetToMeters(15.0)) + { + // Make sure the levels are right for both the caster and the spells. + // Level 8 spells and under at level 5 + // Level 4 spells and under at level 3 + // Level 2 spells and under at level 1 + if((nLevel == 5 && 9 > nSpellLevel) + || (nLevel > 2 && 5 > nSpellLevel) + || (nLevel > 0 && 3 > nSpellLevel)) + PerformAttack(oTarget, oCaster, eVis, 0.0, 0, 0, 0, "*Battlecast Hit*", "*Battlecast Missed*"); + } +} + + +//Archmage and Heirophant SLA slection/storage setting +int ClassSLAStore(object oCaster, int nSpellID, int nCastingClass, int nSpellLevel) +{ + int nSLAID = GetLocalInt(oCaster, "PRC_SLA_Store"); + if(nSLAID) + { + FloatingTextStringOnCreature("SLA "+IntToString(nSLAID)+" stored", oCaster, FALSE); + int nMetamagic = GetMetaMagicFeat(); + + // Look up spell level if invalid (radial spells) + if(nSpellLevel < 0 || nSpellLevel >= 10) + { + string sInnateLevel = Get2DACache("spells", "Innate", nSpellID); + nSpellLevel = StringToInt(sInnateLevel); + } + + SetPersistantLocalInt(oCaster, "PRC_SLA_SpellID_"+IntToString(nSLAID), nSpellID+1); + SetPersistantLocalInt(oCaster, "PRC_SLA_Class_"+IntToString(nSLAID), nCastingClass); + SetPersistantLocalInt(oCaster, "PRC_SLA_Meta_"+IntToString(nSLAID), nMetamagic); + + if(nMetamagic & METAMAGIC_QUICKEN) nSpellLevel += 4; + if(nMetamagic & METAMAGIC_STILL) nSpellLevel += 1; + if(nMetamagic & METAMAGIC_SILENT) nSpellLevel += 1; + if(nMetamagic & METAMAGIC_MAXIMIZE) nSpellLevel += 3; + if(nMetamagic & METAMAGIC_EMPOWER) nSpellLevel += 2; + if(nMetamagic & METAMAGIC_EXTEND) nSpellLevel += 1; + + int nUses = 1; + switch(nSpellLevel) + { + default: + case 9: + case 8: nUses = 1; break; + case 7: + case 6: nUses = 2; break; + case 5: + case 4: nUses = 3; break; + case 3: + case 2: nUses = 4; break; + case 1: + case 0: nUses = 5; break; + } + SetPersistantLocalInt(oCaster, "PRC_SLA_Uses_"+IntToString(nSLAID), nUses); + DeleteLocalInt(oCaster, "PRC_SLA_Store"); + return FALSE; + } + return TRUE; +} + +int PnPSomaticComponents(object oCaster, object oSpellCastItem, string sComponent, int nMetamagic) +{ + if(GetPRCSwitch(PRC_PNP_SOMATIC_COMPOMENTS) || GetPRCSwitch(PRC_PNP_SOMATIC_ITEMS)) + { + object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster); + int nHandFree; + if(!GetIsObjectValid(oItem) || GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD || GetBaseItemType(oItem) == BASE_ITEM_MAGICWAND || GetBaseItemType(oItem) == BASE_ITEM_ENCHANTED_WAND) + nHandFree = TRUE; + oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster); + if(!GetIsObjectValid(oItem) || GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD || GetBaseItemType(oItem) == BASE_ITEM_MAGICWAND || GetBaseItemType(oItem) == BASE_ITEM_ENCHANTED_WAND) + nHandFree = TRUE; + if(GetHasFeat(FEAT_SOMATIC_WEAPONRY, oCaster)) + nHandFree = TRUE; + if(nMetamagic & METAMAGIC_STILL) + nHandFree = TRUE; + + if(!nHandFree) + { + int nHandRequired; + oItem = oSpellCastItem; + //check item is not equiped + if(GetIsObjectValid(oItem) && GetPRCSwitch(PRC_PNP_SOMATIC_ITEMS)) + { + nHandRequired = TRUE; + int nSlot; + for(nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++) + { + if(GetItemInSlot(nSlot, oCaster) == oItem) + nHandRequired = FALSE; + } + } + //check its a real spell and that it requires a free hand + if(!GetIsObjectValid(oItem) && GetPRCSwitch(PRC_PNP_SOMATIC_COMPOMENTS)) + { + if(sComponent == "VS" + || sComponent == "SV" + || sComponent == "S") + nHandRequired = TRUE; + } + + if(nHandRequired) + { + FloatingTextStringOnCreature("You do not have any free hands.", oCaster, FALSE); + // Items were being consumed anyway. This should stop it. + AssignCommand(oItem, SetIsDestroyable(FALSE, FALSE, FALSE)); + AssignCommand(oItem, DelayCommand(0.3, SetIsDestroyable(TRUE, FALSE, FALSE))); + return FALSE; + } + } + } + + return TRUE; +} + +int PRCSpellEffects(object oCaster, object oTarget, int nSpellID, int nSpellLevel, int nCastingClass, int bSpellIsHostile, int nMetamagic) +{ + // Pnp Tensers Transformation + if(GetPRCSwitch(PRC_PNP_TENSERS_TRANSFORMATION)) + { + if(GetHasSpellEffect(SPELL_TENSERS_TRANSFORMATION, oCaster)) + return FALSE; + } + + // Gaseous Form check + if(GetHasSpellEffect(SPELL_GASEOUS_FORM, oCaster)) + { + if(nMetamagic & METAMAGIC_STILL && nMetamagic & METAMAGIC_SILENT) + { + } + else if(GetIsDivineClass(nCastingClass, oCaster) || GetIsArcaneClass(nCastingClass, oCaster)) + return FALSE; + } + + // Violet Rain check + if(GetHasSpellEffect(SPELL_EVIL_WEATHER_VIOLET_RAIN, oCaster)) + { + if(GetIsDivineClass(nCastingClass, oCaster)) + return FALSE; + } + + // PnP Timestop + if(GetPRCSwitch(PRC_TIMESTOP_NO_HOSTILE)) + { + if(GetHasSpellEffect(SPELL_TIME_STOP, oCaster) + || GetHasSpellEffect(4032, oCaster) //epic spell: Greater Timestop + || GetHasSpellEffect(14236, oCaster) //psionic power: Temporal Acceleration + || GetHasSpellEffect(18428, oCaster)) //Mystery MYST_SHADOW_TIME + { + if(!GetIsObjectValid(oTarget) + || oTarget != oCaster + || bSpellIsHostile) + { + return FALSE; + } + } + } + + // Spell Barriers + if(GetHasSpellEffect(SPELL_OTILUKES_RESILIENT_SPHERE, oTarget)) + { + if(GetDistanceBetween(oCaster, oTarget) > 1.524) + { + return FALSE; + } + } + if(GetHasSpellEffect(SPELL_PRISMATIC_SPHERE, oTarget)) + { + if(GetDistanceBetween(oCaster, oTarget) > 3.048) + { + return FALSE; + } + } + + // Null Psionics Field/Anti-Magic Field + if(GetHasSpellEffect(SPELL_ANTIMAGIC_FIELD, oCaster) + || GetHasSpellEffect(POWER_NULL_PSIONICS_FIELD, oCaster)) + { + return FALSE; + } + + // Scrying blocks all powers except for a few special case ones. + int nScry = GetLocalInt(oCaster, "ScrySpellId"); + if(nScry) + { + if(nScry == SPELL_GREATER_SCRYING || nScry == 18415) // MYST_FAR_SIGHT + { + if(nSpellID != SPELL_DETECT_EVIL + && nSpellID != SPELL_DETECT_GOOD + && nSpellID != SPELL_DETECT_LAW + && nSpellID != SPELL_DETECT_CHAOS) + return FALSE; + } + if(nScry == POWER_CLAIRTANGENT_HAND) + { + if(nSpellID != POWER_FARHAND) + return FALSE; + } + if(nScry == 18410) // MYST_EPHEMERAL_IMAGE + return TRUE; // Can cast anything through this one. + + // By default you can't cast anything while scrying + return FALSE; + } + + // Word of Peace + if(GetLocalInt(oCaster, "TrueWardOfPeace") && bSpellIsHostile) + { + return FALSE; + } + + // Dark Discorporation Check + if(GetLocalInt(oCaster, "DarkDiscorporation")) + { + return FALSE; + } + + // Ectoplasmic Shambler + if(GetLocalInt(oCaster, "PRC_IsInEctoplasmicShambler")) + { + if(!GetIsSkillSuccessful(oCaster, SKILL_CONCENTRATION, (15 + nSpellLevel))) + { + FloatingTextStrRefOnCreature(16824061, oCaster, FALSE); // "Ectoplasmic Shambler has disrupted your concentration." + return FALSE; + } + } + + // Jarring Song + if(GetHasSpellEffect(SPELL_VIRTUOSO_JARRING_SONG, oCaster)) + { + if(!GetIsSkillSuccessful(oCaster, SKILL_CONCENTRATION, (15 + nSpellLevel))) + { + FloatingTextStringOnCreature("Jarring Song has disrupted your concentration.", oCaster, FALSE); + return FALSE; + } + } + + //Aura of the Sun - shadow spells make check or fail + if(GetHasSpellEffect(SPELL_AURA_OF_THE_SUN)) + { + if(GetIsOfSubschool(nSpellID, SUBSCHOOL_SHADOW) || GetHasDescriptor(nSpellID, DESCRIPTOR_DARKNESS)) + { + int nDC = GetLocalInt(oCaster, "PRCAuraSunDC"); + int nCheck = d20(1) + PRCGetCasterLevel(oCaster); + + //caster level check + if(nCheck < nDC) return FALSE; + } + } + return TRUE; +} + +int Spellfire(object oCaster, object oTarget) +{ + if(GetHasFeat(FEAT_SPELLFIRE_WIELDER, oCaster)) + { + int nStored = GetPersistantLocalInt(oCaster, "SpellfireLevelStored"); + int nCON = GetAbilityScore(oCaster, ABILITY_CONSTITUTION); + int nTest = nStored > 4 * nCON ? 25 : nStored > 3 * nCON ? 20 : 0; + if(nTest) + { + if(!GetIsSkillSuccessful(oCaster, SKILL_CONCENTRATION, nTest)) + return FALSE; + } + } + if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") && GetIsFriend(oTarget, oCaster)) + { + if(CheckSpellfire(oCaster, oTarget, TRUE)) + { + PRCShowSpellResist(oCaster, oTarget, SPELL_RESIST_MANTLE); + return FALSE; + } + } + + return TRUE; +} + +int Wildstrike(object oCaster) +{ + // 50% chance + if(GetLocalInt(oCaster, "WildMageStrike") && d2() == 2) + { + AssignCommand(oCaster, ClearAllActions()); + AssignCommand(oCaster, ActionCastSpell(499)); + return FALSE; + } + + return TRUE; +} + +int CorruptOrSanctified(object oCaster, int nSpellID, int nCasterAlignment, int nSpellbookType) +{ + //Check for each Corrupt and Sanctified spell + int bCorruptOrSanctified = 0; + + if(nSpellID == SPELL_AYAILLAS_RADIANT_BURST + || nSpellID == SPELL_BRILLIANT_EMANATION + || nSpellID == SPELL_DIVINE_INSPIRATION + || nSpellID == SPELL_DIAMOND_SPRAY + || nSpellID == SPELL_DRAGON_CLOUD + || nSpellID == SPELL_EXALTED_FURY + || nSpellID == SPELL_HAMMER_OF_RIGHTEOUSNESS + || nSpellID == SPELL_PHIERANS_RESOLVE + || nSpellID == SPELL_PHOENIX_FIRE + || nSpellID == SPELL_RAIN_OF_EMBERS + || nSpellID == SPELL_SICKEN_EVIL + || nSpellID == SPELL_STORM_OF_SHARDS + || nSpellID == SPELL_SUNMANTLE + || nSpellID == SPELL_TWILIGHT_LUCK) + bCorruptOrSanctified = 1; + + else if(nSpellID == SPELL_ABSORB_STRENGTH + || nSpellID == SPELL_APOCALYPSE_FROM_THE_SKY + || nSpellID == SPELL_CLAWS_OF_THE_BEBILITH + || nSpellID == SPELL_DEATH_BY_THORNS + || nSpellID == SPELL_EVIL_WEATHER + || nSpellID == SPELL_FANGS_OF_THE_VAMPIRE_KING + || nSpellID == SPELL_LAHMS_FINGER_DARTS + || nSpellID == SPELL_POWER_LEECH + || nSpellID == SPELL_RAPTURE_OF_RUPTURE + || nSpellID == SPELL_RED_FESTER + || nSpellID == SPELL_ROTTING_CURSE_OF_URFESTRA + || nSpellID == SPELL_SEETHING_EYEBANE + || nSpellID == SPELL_TOUCH_OF_JUIBLEX) + bCorruptOrSanctified = 2; + + if(bCorruptOrSanctified) + { + // check if the caster is a spontaneous caster + if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) + { + SendMessageToPC(oCaster, "Spontaneous casters cannot cast this spell!"); + return FALSE; + } + //check for immunity to ability damage - sorry undead buddies + if(GetIsImmune(oCaster, IMMUNITY_TYPE_ABILITY_DECREASE)) + { + if(nSpellID != SPELL_TWILIGHT_LUCK + && nSpellID != SPELL_DIAMOND_SPRAY) + { + SendMessageToPC(oCaster, "You must be able to take ability damage to cast this spell!"); + return FALSE; + } + } + //Check for alignment restrictions + if(bCorruptOrSanctified == 1 + && nCasterAlignment == ALIGNMENT_EVIL) + { + SendMessageToPC(oCaster, "You cannot cast Sanctified spells if you are evil."); + return FALSE; + } + if(bCorruptOrSanctified == 2 + && nCasterAlignment == ALIGNMENT_GOOD) + { + SendMessageToPC(oCaster, "You cannot cast Corrupt spells if you are good."); + return FALSE; + } + } + + return TRUE; +} + +int GrappleConc(object oCaster, int nSpellLevel) +{ + if(GetLocalInt(oCaster, "IsGrappled")) + { + return GetIsSkillSuccessful(oCaster, SKILL_CONCENTRATION, (20 + nSpellLevel)); + } + return TRUE; +} + +int X2UseMagicDeviceCheck(object oCaster) +{ + int nRet = ExecuteScriptAndReturnInt("x2_pc_umdcheck", oCaster); + return nRet; +} + +//------------------------------------------------------------------------------ +// GZ: This is a filter I added to prevent spells from firing their original spell +// script when they were cast on items and do not have special coding for that +// case. If you add spells that can be cast on items you need to put them into +// des_crft_spells.2da +//------------------------------------------------------------------------------ +int X2CastOnItemWasAllowed(object oItem) +{ + int bAllow = (Get2DACache(X2_CI_CRAFTING_SP_2DA,"CastOnItems",PRCGetSpellId()) == "1"); + if (!bAllow) + { + FloatingTextStrRefOnCreature(83453, OBJECT_SELF); // not cast spell on item + } + return bAllow; + +} + +//------------------------------------------------------------------------------ +// Execute a user overridden spell script. +//------------------------------------------------------------------------------ +int X2RunUserDefinedSpellScript() +{ + // See x2_inc_switches for details on this code + string sScript = GetModuleOverrideSpellscript(); + if (sScript != "") + { + ExecuteScript(sScript,OBJECT_SELF); + if (GetModuleOverrideSpellScriptFinished() == TRUE) + { + return FALSE; + } + } + return TRUE; +} + +//------------------------------------------------------------------------------ +// Set the user-specific spell script +//------------------------------------------------------------------------------ +void PRCSetUserSpecificSpellScript(string sScript) +{ + SetLocalString(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT", sScript); +} + +//------------------------------------------------------------------------------ +// Get the user-specific spell script +//------------------------------------------------------------------------------ +string PRCGetUserSpecificSpellScript() +{ + return GetLocalString(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT"); +} + +//------------------------------------------------------------------------------ +// Finish the spell, if necessary +//------------------------------------------------------------------------------ +void PRCSetUserSpecificSpellScriptFinished() +{ + SetLocalInt(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT_DONE", TRUE); +} + +//------------------------------------------------------------------------------ +// Figure out if we should finish the spell. +//------------------------------------------------------------------------------ +int PRCGetUserSpecificSpellScriptFinished() +{ + int iRet = GetLocalInt(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT_DONE"); + DeleteLocalInt(OBJECT_SELF, "PRC_OVERRIDE_SPELLSCRIPT_DONE"); + return iRet; +} + +//------------------------------------------------------------------------------ +// Run a user-specific spell script for classes that use spellhooking. +//------------------------------------------------------------------------------ +int PRCRunUserSpecificSpellScript() +{ + string sScript = PRCGetUserSpecificSpellScript(); + if (sScript != "") + { + ExecuteScript(sScript,OBJECT_SELF); + if (PRCGetUserSpecificSpellScriptFinished() == TRUE) + { + return FALSE; + } + } + return TRUE; +} + +//------------------------------------------------------------------------------ +// Created Brent Knowles, Georg Zoeller 2003-07-31 +// Returns TRUE (and charges the sequencer item) if the spell +// ... was cast on an item AND +// ... the item has the sequencer property +// ... the spell was non hostile +// ... the spell was not cast from an item +// in any other case, FALSE is returned an the normal spellscript will be run +//------------------------------------------------------------------------------ +int X2GetSpellCastOnSequencerItem(object oItem, object oCaster, int nSpellID, int nMetamagic, int nCasterLevel, int nSaveDC, int bSpellIsHostile, object oSpellCastItem) +{ + if(GetIsObjectValid(oSpellCastItem)) // spell cast from item? + { + // we allow scrolls + int nBt = GetBaseItemType(oSpellCastItem); + if(nBt !=BASE_ITEM_SPELLSCROLL && nBt != 105) + { + FloatingTextStrRefOnCreature(83373, oCaster); + return TRUE; // wasted! + } + } + + if(bSpellIsHostile) + { + int nMaxChanSpells = IPGetItemChannelingProperty(oItem); + + if(nMaxChanSpells < 1) + { + FloatingTextStrRefOnCreature(83885, oCaster); + return TRUE; // no hostile spells on sequencers, sorry ya munchkins :) + } + + int nNumberOfTriggers = GetLocalInt(oItem, "X2_L_NUMCHANNELTRIGGERS"); + // is there still space left on the sequencer? + if (nNumberOfTriggers < nMaxChanSpells) + { + // success visual and store spell-id on item. + effect eVisual = EffectVisualEffect(VFX_IMP_BREACH); + nNumberOfTriggers++; + //NOTE: I add +1 to the SpellId to spell 0 can be used to trap failure + int nSID = nSpellID+1; + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER" +IntToString(nNumberOfTriggers), nSID); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_L"+IntToString(nNumberOfTriggers), nCasterLevel); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_M"+IntToString(nNumberOfTriggers), nMetamagic); + SetLocalInt(oItem, "X2_L_CHANNELTRIGGER_D"+IntToString(nNumberOfTriggers), nSaveDC); + SetLocalInt(oItem, "X2_L_NUMCHANNELTRIGGERS", nNumberOfTriggers); + //add an OnHit:DischargeSequencer property + itemproperty ipTest = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1); + IPSafeAddItemProperty(oItem ,ipTest, 99999999.9); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, oCaster); + FloatingTextStrRefOnCreature(83884, oCaster); + } + else + FloatingTextStrRefOnCreature(83859, oCaster); + } + else + { + int nMaxSeqSpells = IPGetItemSequencerProperty(oItem); // get number of maximum spells that can be stored + + if(nMaxSeqSpells < 1) + { + return FALSE; + } + + int nNumberOfTriggers = GetLocalInt(oItem, "X2_L_NUMTRIGGERS"); + // is there still space left on the sequencer? + if (nNumberOfTriggers < nMaxSeqSpells) + { + // success visual and store spell-id on item. + effect eVisual = EffectVisualEffect(VFX_IMP_BREACH); + nNumberOfTriggers++; + //NOTE: I add +1 to the SpellId to spell 0 can be used to trap failure + int nSID = nSpellID+1; + SetLocalInt(oItem, "X2_L_SPELLTRIGGER" +IntToString(nNumberOfTriggers), nSID); + SetLocalInt(oItem, "X2_L_SPELLTRIGGER_L"+IntToString(nNumberOfTriggers), nCasterLevel); + SetLocalInt(oItem, "X2_L_SPELLTRIGGER_M"+IntToString(nNumberOfTriggers), nMetamagic); + SetLocalInt(oItem, "X2_L_SPELLTRIGGER_D"+IntToString(nNumberOfTriggers), nSaveDC); + SetLocalInt(oItem, "X2_L_NUMTRIGGERS", nNumberOfTriggers); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVisual, oCaster); + FloatingTextStrRefOnCreature(83884, oCaster); + } + else + FloatingTextStrRefOnCreature(83859, oCaster); + } + + return TRUE; // in any case, spell is used up from here, so do not fire regular spellscript +} + +//------------------------------------------------------------------------------ +// * This is our little concentration system for black blade of disaster +// * if the mage tries to cast any kind of spell, the blade is signaled an event to die +//------------------------------------------------------------------------------ +void X2BreakConcentrationSpells() +{ + //end Dragonsong Lyrist songs + DeleteLocalInt(OBJECT_SELF, "SpellConc"); + + if(GetPRCSwitch(PRC_PNP_BLACK_BLADE_OF_DISASTER)) + { + //this is also in summon HB + //but needed here to handle quickend spells + //Disintegrate is cast from the blade so doenst end the summon + object oAssoc = GetAssociate(ASSOCIATE_TYPE_SUMMONED); + if(GetIsObjectValid(oAssoc)) + { + if(GetTag(oAssoc) == "x2_s_bblade") // black blade of disaster + { + if(GetLocalInt(oAssoc, "X2_L_CREATURE_NEEDS_CONCENTRATION")) + { + SignalEvent(oAssoc, EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); + } + } + } + } +} + +//------------------------------------------------------------------------------ +// being hit by any kind of negative effect affecting the caster's ability to concentrate +// will cause a break condition for concentration spells +//------------------------------------------------------------------------------ +int X2GetBreakConcentrationCondition(object oPlayer) +{ + effect e1 = GetFirstEffect(oPlayer); + int nType; + int bRet = FALSE; + while (GetIsEffectValid(e1) && !bRet) + { + nType = GetEffectType(e1); + + 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) + { + bRet = TRUE; + } + e1 = GetNextEffect(oPlayer); + } + return bRet; +} + +void X2DoBreakConcentrationCheck() +{ + object oMaster = GetMaster(); + if (GetLocalInt(OBJECT_SELF,"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(OBJECT_SELF,EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); + } + else if (X2GetBreakConcentrationCondition(oMaster)) + { + SignalEvent(OBJECT_SELF,EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); + } + } + } +} + +//------------------------------------------------------------------------------ +// This function will return TRUE if the spell that is cast is a shape shifting +// spell. +//------------------------------------------------------------------------------ +int X3ShapeShiftSpell(object oTarget, int nSpellID) +{ + string sUp = GetStringUpperCase(Get2DACache("x3restrict", "SHAPESHIFT", nSpellID)); + if(sUp == "YES") + return TRUE; + return FALSE; +} + +void VoidCounterspellExploitCheck(object oCaster) +{ + if(GetCurrentAction(oCaster) == ACTION_COUNTERSPELL) + { + ClearAllActions(); + SendMessageToPC(oCaster,"Because of the infinite spell casting exploit, you cannot use counterspell in this manner."); + } +} + +int CounterspellExploitCheck(object oCaster) +{ + if(GetCurrentAction(oCaster) == ACTION_COUNTERSPELL) + { + ClearAllActions(); + SendMessageToPC(oCaster,"Because of the infinite spell casting exploit, you cannot use counterspell in this manner."); + return FALSE; + } + else + { + DelayCommand(0.1, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.2, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.3, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.4, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.5, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.6, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.7, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.8, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(0.9, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(1.0, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(2.0, VoidCounterspellExploitCheck(oCaster)); + DelayCommand(3.0, VoidCounterspellExploitCheck(oCaster)); + } + + return TRUE; +} + +int Blighter(object oCaster, int nCastingClass, object oSpellCastItem) +{ + // If it's an item, exit + if (GetIsObjectValid(oSpellCastItem)) + return TRUE; + + if (nCastingClass == CLASS_TYPE_DRUID && GetLevelByClass(CLASS_TYPE_BLIGHTER, oCaster) > 0) + { + SendMessageToPC(oCaster,"Blighters cannot cast druid spells."); + return FALSE; + } + + return TRUE; +} + +int BattleBlessing(object oCaster, int nCastingClass) +{ + if (nCastingClass != CLASS_TYPE_PALADIN && GetLocalInt(oCaster, "BattleBlessingActive")) + { + SendMessageToPC(oCaster,"You cannot cast non-Paladin spells with Battle Blessing active."); + return FALSE; + } + + return TRUE; +} + +int Spelldance(object oCaster, int nSpellLevel, int nCastingClass) +{ + //ensure the spell is arcane + if(!GetIsArcaneClass(nCastingClass, oCaster)) + return TRUE; + + if (DEBUG) FloatingTextStringOnCreature("Spelldance in Spellhook", oCaster, FALSE); + + int nDance = GetLocalInt(oCaster, "Spelldance"); + if (DEBUG) FloatingTextStringOnCreature("Spelldance value in Spellhook: "+IntToString(nDance), oCaster, FALSE); + if (nDance) + { + if(nDance & METAMAGIC_EXTEND) + nSpellLevel += 1; + if(nDance & METAMAGIC_EMPOWER) + nSpellLevel += 2; + if(nDance & METAMAGIC_MAXIMIZE) + nSpellLevel += 3; + + if (DEBUG) FloatingTextStringOnCreature("Spelldances in Spellhook #2", oCaster, FALSE); + + if(!GetIsSkillSuccessful(oCaster, SKILL_PERFORM, 10+nSpellLevel)) //Failed + { + FloatingTextStringOnCreature("Spelldance failed", oCaster, FALSE); + if (DEBUG) FloatingTextStringOnCreature("Spelldances in Spellhook #3", oCaster, FALSE); + return FALSE; + } + } + return TRUE; +} + +// Does the counterspelling for Warp Spell, +// and the storing of the spell for later use +int WarpSpell(object oCaster, int nSpellId) +{ + int nWarp = GetLocalInt(oCaster, "WarpSpell"); + // If Warp Spell isn't set, just keep going + if (!nWarp) return TRUE; + + object oShadow = GetLocalObject(oCaster, "WarpSpell"); // The one who cast Warp Spell + + DeleteLocalInt(oCaster, "WarpSpell"); + DeleteLocalObject(oCaster, "WarpSpell"); // Done regardless of success or failure + + int nLevel = PRCGetCasterLevel(oCaster); + if (d20() + nWarp > d20() + nLevel) // Won the caster level check + { + // Set a marker on the Shadowcaster + SetLocalInt(oShadow, "WarpSpellSuccess", TRUE); + FloatingTextStringOnCreature("You have successfully warped your opponent's "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))), oShadow, FALSE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SOUL_TRAP), oCaster); + return FALSE; + } + + FloatingTextStringOnCreature("Your warp spell has failed", oShadow, FALSE); + return TRUE; +} + +// Does the counterspelling for Innate Counterspell +// and the storing of the spell for later use +int InnateCounterspell(object oCaster, int nSpellId, int nSpellLevel) +{ + int nWarp = GetLocalInt(oCaster, "InnateCounterspell"); + // If Innate Counterspell isn't set, just keep going + if (!nWarp) return TRUE; + + object oShadow = GetLocalObject(oCaster, "InnateCounterspell"); // The one who cast Innate Counterspell + + DeleteLocalInt(oCaster, "InnateCounterspell"); + DeleteLocalObject(oCaster, "InnateCounterspell"); // Done regardless of success or failure + + int nLevel = PRCGetCasterLevel(oCaster); + if (GetIsSkillSuccessful(oCaster, SKILL_SPELLCRAFT, 15 + nSpellLevel)) // Identified the spell to counter + { + SetLocalInt(oShadow, "BurnSpellLevel", nSpellLevel); + if (BurnSpell(oShadow)) + { + DeleteLocalInt(oShadow, "BurnSpellLevel"); + // Set a marker on the Noctumancer at the right level + if (GetLevelByClass(CLASS_TYPE_NOCTUMANCER) >= 7) + { + int nStore = PRCMin(1, nSpellLevel/2); + SetLocalInt(oShadow, "InnateCounterSuccess", nStore); + FloatingTextStringOnCreature("You have one free mystery of "+IntToString(nStore)+" level", oShadow, FALSE); + } + if (GetLevelByClass(CLASS_TYPE_NOCTUMANCER) >= 10) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectSpellImmunity(nSpellId)), oShadow, 60.0); + + FloatingTextStringOnCreature("You have successfully counterspelled your opponent's "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))), oShadow, FALSE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SOUL_TRAP), oCaster); + return FALSE; + } + } + + FloatingTextStringOnCreature("Your innate counterspell has failed", oShadow, FALSE); + return TRUE; +} + +// Stores the spell for use with Echo Spell +void EchoSpell(object oCaster, int nSpellId) +{ + int nEcho = GetLocalInt(oCaster, "EchoSpell"); + // If Echo Spell isn't set, just skip + if (nEcho) + { + object oShadow = GetLocalObject(oCaster, "EchoSpell"); // The one who cast Echo Spell + SetLocalInt(oShadow, "EchoedSpell", nSpellId); + FloatingTextStringOnCreature("You have echoed " + GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))) + " and have one round to cast it", oShadow, FALSE); + DelayCommand(9.0, DeleteLocalInt(oShadow, "EchoedSpell")); + DeleteLocalInt(oCaster, "EchoSpell"); + DeleteLocalObject(oCaster, "EchoSpell"); + } +} + +// Flood of Shadow +int FloodShadow(object oCaster, int nSpellId) +{ + if (!GetLocalInt(oCaster, "FloodShadow") || GetLocalInt(oCaster, "ShadowEvoking")) + return TRUE; + + if (!GetIsSkillSuccessful(oCaster, SKILL_SPELLCRAFT, 15 + StringToInt(Get2DACache("spells", "Innate", nSpellId)))) // Skill check + { + if (GetIsPC(oCaster)) FloatingTextStringOnCreature("You have failed to overcome Flood of Shadow", oCaster, FALSE); + return FALSE; + } + + return TRUE; +} + +void MasterWand(object oCaster, object oSpellCastItem, int nSpellLevel) +{ + if (DEBUG) DoDebug("MasterWand - Entered"); + if (!GetHasFeat(FEAT_MASTER_WAND, oCaster)) return; // Does nothing without the feat, obviously + if (!GetLocalInt(oCaster, "MasterWand")) return; // Needs to be active as well + + if (DEBUG) DoDebug("MasterWand - Active"); + + int nType = GetBaseItemType(oSpellCastItem); + + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) // Has to be a wand, obv + { + if (DEBUG) DoDebug("MasterWand - Wand"); + SetLocalInt(oCaster, "BurnSpellLevel", nSpellLevel); + if (BurnSpell(oCaster)) // Burn a spell of the appropriate level + { + if (DEBUG) DoDebug("MasterWand - Burned Spell"); + DeleteLocalInt(oCaster, "BurnSpellLevel"); + SetItemCharges(oSpellCastItem, GetItemCharges(oSpellCastItem)+1); // add the use back to the item + } + } +} + +int WandEquipped(object oCaster, object oSpellCastItem) +{ + //if (!GetPRCSwitch(PRC_EQUIP_WAND)) return TRUE; // If this is set to anything other than 0, it skips the check. + + int nType = GetBaseItemType(oSpellCastItem); + + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) // Has to be a wand, obv + { + if(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster) == oSpellCastItem || GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster) == oSpellCastItem) // Needs to be equipped + { + return TRUE; + } + else + { + FloatingTextStringOnCreature("You must equip a wand to cast from it.", oCaster, FALSE); + return FALSE; // It's a wand not equipped + } + } + + return TRUE; +} + +void DoubleWandWielder(object oCaster, object oSpellCastItem, object oTarget) +{ + if (!GetHasFeat(FEAT_DOUBLE_WAND_WIELDER, oCaster)) return; // Does nothing without the feat, obviously + if (!GetLocalInt(oCaster, "DoubleWand")) return; // Needs to be active as well + + int nType = GetBaseItemType(oSpellCastItem); + + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) // Has to be a wand, obv + { + object oOffHand; + if(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster) == oSpellCastItem) // Find the other hand + oOffHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster); + else + oOffHand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster); + + int nOffType = GetBaseItemType(oOffHand); + + if(nOffType == BASE_ITEM_MAGICWAND || nOffType == BASE_ITEM_ENCHANTED_WAND) // Has to be a wand, obv + { + int nCharges = GetItemCharges(oOffHand); + + if (nCharges > 1) // Need to have enough charges for this + { + //code for getting new ip type + int nCasterLevel; + int nSpell; + itemproperty ipTest = GetFirstItemProperty(oOffHand); + while(GetIsItemPropertyValid(ipTest)) + { + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_CASTER_LEVEL) + { + nCasterLevel = GetItemPropertyCostTableValue(ipTest); + if (DEBUG) DoDebug("DoubleWandWielder: caster level from item = "+IntToString(nCasterLevel)); + } + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL) + { + nSpell = GetItemPropertySubType(ipTest); + if (DEBUG) DoDebug("DoubleWandWielder: iprop subtype = "+IntToString(nSpell)); + nSpell = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSpell)); + if (DEBUG) DoDebug("DoubleWandWielder: spell from item = "+IntToString(nSpell)); + } + ipTest = GetNextItemProperty(oOffHand); + } + // if we didn't find a caster level on the item, it must be Bioware item casting + if(!nCasterLevel) + { + nCasterLevel = GetCasterLevel(oCaster); + if (DEBUG) DoDebug("DoubleWandWielder: bioware item casting with caster level = "+IntToString(nCasterLevel)); + } + + ActionCastSpell(nSpell, nCasterLevel, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, FALSE, FALSE, oTarget, TRUE); + SetItemCharges(oOffHand, nCharges - 2); + } + } + } +} + +void MeldArcaneFocus(object oCaster, object oTarget) +{ + int nDC = GetLocalInt(oCaster, "ArcaneFocusBound"); + if (nDC) // MELD_ARCANE_FOCUS + { + if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_SPELL)) + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oTarget, 6.0); + } +} + +// Does the caster level check for Witchborn Binder's Mage Shackles +int MageShackles(object oCaster, int nSpellId) +{ + int nWarp = GetLocalInt(oCaster, "MageShackles"); + // If Mage Shackles aren't present, just keep going + if (!nWarp) return TRUE; + + object oMeldshaper = GetLocalObject(oCaster, "MageShacklesShaper"); // The one who created the Shackles + + int nLevel = PRCGetCasterLevel(oCaster); + if (nWarp > PRCGetCasterLevel(oCaster)+d20()) // Do they beat the DC for the caster level check or not? + { + FloatingTextStringOnCreature("Your mage shackles have successfully blocked your opponent's "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))), oMeldshaper, FALSE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SOUL_TRAP), oCaster); + return FALSE; + } + + return TRUE; +} + +// Does the counterspelling for Word of Abrogation +int Abrogation(object oCaster, int nCasterLevel, int nSpellId) +{ + location lTarget = GetLocation(oCaster); + int nCnt = 1; + // Find the meldshaper + object oMeldshaper = GetNearestCreatureToLocation(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, lTarget, nCnt); + while(GetIsObjectValid(oMeldshaper) && GetDistanceBetween(oMeldshaper, oCaster) <= FeetToMeters(60.0f)) + { + int nWarp = GetLocalInt(oMeldshaper, "Abrogation"); + if(nWarp) + { + // Clean up the ability + DeleteLocalInt(oMeldshaper, "Abrogation"); + PRCRemoveSpellEffects(MELD_WITCH_ABROGATION, oMeldshaper, oMeldshaper); + GZPRCRemoveSpellEffects(MELD_WITCH_ABROGATION, oMeldshaper, FALSE); + + if (nWarp+d20() >= nCasterLevel+11) // Do you beat their caster level? + { + FloatingTextStringOnCreature("Your word of abrogation has successfully blocked your opponent's "+GetStringByStrRef(StringToInt(Get2DACache("spells", "Name", nSpellId))), oMeldshaper, FALSE); + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SOUL_TRAP), oCaster); + return FALSE; + } + else + FloatingTextStringOnCreature("Your word of abrogation has failed", oMeldshaper, FALSE); + } + nCnt++; + oMeldshaper = GetNearestCreatureToLocation(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, lTarget, nCnt); + } + + return TRUE; +} + +// Does damage for Grim Integument +void Integument(object oCaster, int nCastingClass) +{ + if (GetIsArcaneClass(nCastingClass) && GetHasSpellEffect(MELD_WITCH_INTEGUMENT, oCaster)) + { + int nEssentia = GetLocalInt(oCaster, "GIEssentia"); + object oMeldshaper = GetLocalObject(oCaster, "GIMeldshaper"); // The one who created the Shackles + int nDC = 10 + nEssentia + GetAbilityModifier(ABILITY_CONSTITUTION, oMeldshaper); + int nDam = d6(nEssentia); + if(PRCMySavingThrow(SAVING_THROW_FORT, oCaster, nDC, SAVING_THROW_TYPE_NONE)) + { + if (GetHasMettle(oCaster, SAVING_THROW_FORT)) + nDam = 0; + + nDam /= 2; + } + + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, DAMAGE_TYPE_POSITIVE), oCaster); + } +} + +void WyrmbaneFrightful(object oPC) +{ + object oWOL = GetItemPossessedBy(oPC, "WOL_Wyrmbane"); + + // You get nothing if you aren't wielding the legacy item + if(oWOL != GetItemInSlot(INVENTORY_SLOT_HEAD, oPC)) return; + if (GetHasFeat(FEAT_GREATER_LEGACY, oPC)) + ExecuteScript("prc_fright_pres", oPC); +} + +int Nondetection(object oTarget, object oCaster, int nSchool, int nCasterLevel) +{ + int nReturn = TRUE; + if (nSchool == SPELL_SCHOOL_DIVINATION) + { + if(GetHasSpellEffect(SPELL_NONDETECTION, oTarget) || GetLevelByClass(CLASS_TYPE_WILD_MAGE, oTarget) >= 6 || GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oTarget) >= 5 || GetHasSpellEffect(MELD_ENIGMA_HELM, oTarget) || GetRacialType(oTarget) == RACIAL_TYPE_SKULK) + { + // Caster level check or the Divination fails. + int nTarget = PRCGetCasterLevel(oTarget) + 11; + if (GetRacialType(oTarget) == RACIAL_TYPE_SKULK && 20 > nTarget) nTarget = 20; + if(nTarget > nCasterLevel + d20()) + { + FloatingTextStringOnCreature(GetName(oTarget) + " has Nondetection active.", oCaster, FALSE); + return FALSE; + } + } + } + return nReturn; +} + +int AmonInfluence(object oTarget, object oCaster, int nSpellId, int nSpellLevel, int bSpellIsHostile) +{ + // Have to have a bad pact with Amon for this to trigger on beneficial spells + if (GetHasSpellEffect(VESTIGE_AMON, oTarget) && !GetLocalInt(oTarget, "PactQuality"+IntToString(VESTIGE_AMON)) && !bSpellIsHostile) + { + // Law, fire, and sun domain casters + if (GetHasFeat(FEAT_FIRE_DOMAIN_POWER, oCaster) || GetHasFeat(FEAT_SUN_DOMAIN_POWER, oCaster)) + { + if(PRCMySavingThrow(SAVING_THROW_WILL, oTarget, PRCGetSaveDC(oTarget, oCaster, nSpellId), SAVING_THROW_TYPE_SPELL)) + { + FloatingTextStringOnCreature("You have made a poor pact, and Amon forces you to resist the spell", oTarget, FALSE); + return FALSE; + }//end will save processing + } + } + + return TRUE; +} + +int RonoveInfluence(object oCaster, object oSpellCastItem) +{ + if (GetHasSpellEffect(VESTIGE_RONOVE, oCaster) && !GetLocalInt(oCaster, "PactQuality"+IntToString(VESTIGE_RONOVE))) + { + if(GetIsObjectValid(oSpellCastItem)) + { + // Potion drinking is restricted + if(GetBaseItemType(oSpellCastItem) == BASE_ITEM_ENCHANTED_POTION + || GetBaseItemType(oSpellCastItem) == BASE_ITEM_POTIONS) + return FALSE; + } + } + + return TRUE; +} + +int HaagentiTransmutation(object oTarget, int nSpellID) +{ + if (GetHasSpellEffect(VESTIGE_HAAGENTI, oTarget) && GetLocalInt(oTarget, "ExploitVestige") != VESTIGE_HAAGENTI_IMMUNE_TRANS) + { + if(GetIsOfSubschool(nSpellID, SUBSCHOOL_POLYMORPH)) + return FALSE; + } + + return TRUE; +} + + +int KarsiteInability(object oCaster, int nCastingClass) +{ + if (GetRacialType(oCaster) == RACIAL_TYPE_KARSITE) + { + //:: Check if the spell originates from an item + object oSpellSource = GetSpellCastItem(); + if (GetIsObjectValid(oSpellSource)) + { + //:: Spell is cast from an item; allow it + return TRUE; + } + if (GetIsDivineClass(nCastingClass, oCaster) || GetIsArcaneClass(nCastingClass, oCaster)) + { + return FALSE; + } + } + + // Default: allow + return TRUE; +} + +/* int KarsiteInability(object oCaster, int nCastingClass) +{ + if (GetRacialType(oCaster) == RACIAL_TYPE_KARSITE) + { + if(GetIsDivineClass(nCastingClass, oCaster) || GetIsArcaneClass(nCastingClass, oCaster)) + return FALSE; + } + + return TRUE; +} */ + +int UrCleric(object oCaster, int nCastingClass) +{ + if (GetLevelByClass(CLASS_TYPE_UR_PRIEST, oCaster) && GetIsDivineClass(nCastingClass, oCaster) && nCastingClass != CLASS_TYPE_UR_PRIEST) + { + return FALSE; + } + + return TRUE; +} + +int ShadowWeaveLightSpells(object oCaster, int nSpellID) +{ + if(GetHasDescriptor(nSpellID, DESCRIPTOR_LIGHT) && GetHasFeat(FEAT_SHADOWWEAVE, oCaster)) + { + return FALSE; + } + + return TRUE; +} + +void ArkamoiStrength(object oCaster, int nCastingClass) +{ + // This race only + if (GetRacialType(oCaster) != RACIAL_TYPE_ARKAMOI) + return; + + int nStrength = GetLocalInt(oCaster, "StrengthFromMagic"); + + // Only applies to arcane spells + if (GetIsArcaneClass(nCastingClass, oCaster)) + { + // First time here + if (!nStrength) + { + SetLocalInt(oCaster, "StrengthFromMagic", 1); + DelayCommand(60.0, DeleteLocalInt(oCaster, "StrengthFromMagic")); + DelayCommand(60.0, FloatingTextStringOnCreature("Arkamoi Strength from Magic reset", oCaster, FALSE)); + } + else if (5 > nStrength) // nStrength equals something, can't go above five + SetLocalInt(oCaster, "StrengthFromMagic", nStrength + 1); + PRCRemoveSpellEffects(SPELL_ARKAMOI_STRENGTH, oCaster, oCaster); + GZPRCRemoveSpellEffects(SPELL_ARKAMOI_STRENGTH, oCaster, FALSE); + ActionCastSpellOnSelf(SPELL_ARKAMOI_STRENGTH); + FloatingTextStringOnCreature("Arkamoi Strength from Magic at "+IntToString(nStrength+1), oCaster, FALSE); + } +} + +void RedspawnHealing(object oCaster, int nSpellID, int nSpellLevel) +{ + if(GetHasDescriptor(nSpellID, DESCRIPTOR_FIRE) && GetRacialType(oCaster) == RACIAL_TYPE_REDSPAWN_ARCANISS) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nSpellLevel*2), oCaster); + } +} + +void WarsoulTyrant(object oCaster, int nCastingClass) +{ + // This race only + if (GetRacialType(oCaster) != RACIAL_TYPE_HOBGOBLIN_WARSOUL) + return; + + int nStrength = GetLocalInt(oCaster, "WarsoulTyrant"); + + // Only applies to arcane spells + if (GetIsArcaneClass(nCastingClass, oCaster)) + { + // First time here + if (nStrength) + DelayCommand(1.0, DeleteLocalInt(oCaster, "WarsoulTyrant")); + } +} + +// this will execute the prespellcastcode, whose full functionality is incoded in X2PreSpellCastCode2(), +// as a script, to save loading time for spells scripts and reduce memory usage of NWN +// the prespellcode takes up roughly 250 kByte compiled code, meaning that every spell script that +// calls it directly as a function (e.g.: X2PreSpellCastCode2) will be between 100 kByte to 250 kByte +// larger, than a spell script calling the prespellcode via ExecuteScript (e.g. X2PreSpellCastCode) +// Although ExecuteScript is slightly slower than a direct function call, quite likely overall performance is +// increased, because for every new spell 100-250 kByte less code need to be loaded into memory +// and NWN has more free memory available to keep more spells scripts (and other crucial scripts) +//in RAM +/*int X2PreSpellCastCode() +{ + object oCaster = OBJECT_SELF; + + // SetLocalInt(oCaster, "PSCC_Ret", 0); + ExecuteScript("prc_prespell", oCaster); + + int nReturn = GetLocalInt(oCaster, "PSCC_Ret"); + // DeleteLocalInt(oCaster, "PSCC_Ret"); + + return nReturn; +} +moved to prc_spellhook */ + +//------------------------------------------------------------------------------ +// if FALSE is returned by this function, the spell will not be cast +// the order in which the functions are called here DOES MATTER, changing it +// WILL break the crafting subsystems +//------------------------------------------------------------------------------ +int X2PreSpellCastCode2() +{ + object oCaster = OBJECT_SELF; + object oTarget = PRCGetSpellTargetObject(); + object oSpellCastItem = PRCGetSpellCastItem(); + int nOrigSpellID = GetSpellId(); + int nSpellID = PRCGetSpellId(); + int nCastingClass = PRCGetLastSpellCastClass(); + int nSpellLevel = PRCGetSpellLevelForClass(nSpellID, nCastingClass); + int nSchool = GetSpellSchool(nSpellID); + int nCasterLevel = PRCGetCasterLevel(oCaster); + int nMetamagic = PRCGetMetaMagicFeat(oCaster, FALSE); + int nSaveDC = PRCGetSaveDC(OBJECT_INVALID, oCaster); + int bSpellIsHostile = Get2DACache("spells", "HostileSetting", nOrigSpellID) == "1"; + int nSpellbookType = GetSpellbookTypeForClass(nCastingClass); + int nCasterAlignment = GetAlignmentGoodEvil(oCaster); + string sComponent = GetStringUpperCase(Get2DACache("spells", "VS", nSpellID)); + + //--------------------------------------------------------------------------- + // This small addition will check to see if the target is mounted and the + // spell is therefor one that should not be permitted. + //--------------------------------------------------------------------------- + if(!GetLocalInt(GetModule(),"X3_NO_SHAPESHIFT_SPELL_CHECK")) + { + if(PRCHorseGetIsMounted(oTarget) && X3ShapeShiftSpell(oTarget, nSpellID)) + { + if(GetIsPC(oTarget)) + { + FloatingTextStrRefOnCreature(111982,oTarget,FALSE); + } + return FALSE; + } + } + + int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oCaster); + + //--------------------------------------------------------------------------- + // Now check if we cast from an item (scroll, staff etc) + //--------------------------------------------------------------------------- + if(GetIsObjectValid(oSpellCastItem)) + { + //--------------------------------------------------------------------------- + // Check for the new restricted itemproperties + //--------------------------------------------------------------------------- + if(nContinue + && !CheckPRCLimitations(oSpellCastItem, oCaster)) + { + SendMessageToPC(oCaster, "You cannot use "+GetName(oSpellCastItem)); + nContinue = FALSE; + } + + //--------------------------------------------------------------------------- + // Baelnorn attempting to use items while projection + //--------------------------------------------------------------------------- + if(nContinue + && GetLocalInt(oCaster, "BaelnornProjection_Active"))// If projection is active AND + { + nContinue = FALSE; // Prevent casting + } + + //casting from staffs uses caster DC calculations + if(nContinue + && (GetBaseItemType(oSpellCastItem) == BASE_ITEM_MAGICSTAFF + || GetBaseItemType(oSpellCastItem) == BASE_ITEM_CRAFTED_STAFF) + && GetPRCSwitch(PRC_STAFF_CASTER_LEVEL)) + { + int nDC = 10 + StringToInt(lookup_spell_innate(nSpellID)); + nDC += (GetAbilityScoreForClass(GetPrimaryArcaneClass(oCaster), oCaster)-10)/2; + SetLocalInt(oCaster, PRC_DC_BASE_OVERRIDE, nDC); + DelayCommand(0.01, DeleteLocalInt(oCaster, PRC_DC_BASE_OVERRIDE)); + } + } + + //--------------------------------------------------------------------------- + // Break any spell require maintaining concentration + //--------------------------------------------------------------------------- + X2BreakConcentrationSpells(); + + //--------------------------------------------------------------------------- + // No casting while using expertise + //--------------------------------------------------------------------------- + if(nContinue) + if (GetActionMode(oCaster, ACTION_MODE_EXPERTISE) || GetActionMode(oCaster, ACTION_MODE_IMPROVED_EXPERTISE)) + { + SendMessageToPC(oCaster, "Combat Expertise only works with attack actions."); + nContinue = FALSE; + } + + //--------------------------------------------------------------------------- + // Run Spelldance perform check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = Spelldance(oCaster, nSpellLevel, nCastingClass); + + //--------------------------------------------------------------------------- + // Check for PRC spell effects + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PRCSpellEffects(oCaster, oTarget, nSpellID, nSpellLevel, nCastingClass, bSpellIsHostile, nMetamagic); + + //--------------------------------------------------------------------------- + // Run Grappling Concentration Check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = GrappleConc(oCaster, nSpellLevel); + + //--------------------------------------------------------------------------- + // Blighters stop Druid casting + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = Blighter(oCaster, nCastingClass, oSpellCastItem); + + //--------------------------------------------------------------------------- + // Karsite stop Arcane/Divine casting + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = KarsiteInability(oCaster, nCastingClass); + + //--------------------------------------------------------------------------- + // Ur-Priest stops cleric casting + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = UrCleric(oCaster, nCastingClass); + + //--------------------------------------------------------------------------- + // Shadow Weave blocks casting light descriptor spells + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = ShadowWeaveLightSpells(oCaster, nSpellID); + + //--------------------------------------------------------------------------- + // Forsakers cancel friendly casting + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = Forsaker(oCaster, oTarget); + + //--------------------------------------------------------------------------- + // Battle Blessing stops non-Paladin casting + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = BattleBlessing(oCaster, nCastingClass); + + //--------------------------------------------------------------------------- + // Nondetection can block divinations + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = Nondetection(oTarget, oCaster, nSchool, nCasterLevel); + + //--------------------------------------------------------------------------- + // Mystery Effects + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = WarpSpell(oCaster, nSpellID); + + if (nContinue) + nContinue = InnateCounterspell(oCaster, nSpellID, nSpellLevel); + + if (nContinue) + nContinue = FloodShadow(oCaster, nSpellID); + + //--------------------------------------------------------------------------- + // Incarnum Effects + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = MageShackles(oCaster, nSpellID); + + if (nContinue) + nContinue = Abrogation(oCaster, nCasterLevel, nSpellID); + + //--------------------------------------------------------------------------- + // Binding Effects + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = AmonInfluence(oTarget, oCaster, nSpellID, nSpellLevel, bSpellIsHostile); + + if (nContinue) + nContinue = RonoveInfluence(oCaster, oSpellCastItem); + + if (nContinue) + nContinue = HaagentiTransmutation(oTarget, nSpellID); + + //--------------------------------------------------------------------------- + // Check for Corrupt or Sanctified spells + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = CorruptOrSanctified(oCaster, nSpellID, nCasterAlignment, nSpellbookType); + + //--------------------------------------------------------------------------- + // Run Knight of the Chalice Heavenly Devotion check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = KOTCHeavenDevotion(oCaster, oTarget, nCasterAlignment, nSchool); + + //--------------------------------------------------------------------------- + // Spellfire + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = Spellfire(oCaster, oTarget); + + //--------------------------------------------------------------------------- + // This stuff is only interesting for player characters we assume that use + // magic device always works and NPCs don't use the crafting feats or + // sequencers anyway. Thus, any NON PC spellcaster always exits this script + // with TRUE (unless they are DM possessed or in the Wild Magic Area in + // Chapter 2 of Hordes of the Underdark. + //--------------------------------------------------------------------------- + int bIsPC = GetIsPC(oCaster) || GetPRCSwitch(PRC_NPC_HAS_PC_SPELLCASTING) + || GetIsDMPossessed(oCaster) || GetLocalInt(GetArea(oCaster), "X2_L_WILD_MAGIC"); + + if(bIsPC) + { + //--------------------------------------------------------------------------- + // Run Bard/Sorc PrC check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = BardSorcPrCCheck(oCaster, nCastingClass, oSpellCastItem); + + //--------------------------------------------------------------------------- + // Equipped Wand switches + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = WandEquipped(oCaster, oSpellCastItem); + + //--------------------------------------------------------------------------- + // Alignment Restrictions Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = SpellAlignmentRestrictions(oCaster, nSpellID, nCastingClass); + + //--------------------------------------------------------------------------- + // Druid spontaneous summoning + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = DruidSpontSummon(oCaster, nCastingClass, nSpellID, nSpellLevel); + + //--------------------------------------------------------------------------- + // Run New Spellbook Spell Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = NSB_SpellCast(oCaster, nSpellID, nCastingClass, nMetamagic, nSpellbookType, sComponent, oSpellCastItem); + + //--------------------------------------------------------------------------- + // Run counterspell exploit check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = CounterspellExploitCheck(oCaster); + + //--------------------------------------------------------------------------- + // PnP spellschools + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PnPSpellSchools(oCaster, nCastingClass, nSchool, oSpellCastItem); + + //--------------------------------------------------------------------------- + // Run Red Wizard School Restriction Check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = RedWizRestrictedSchool(oCaster, nSchool, nCastingClass, oSpellCastItem); + + //--------------------------------------------------------------------------- + // PnP somatic components + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = PnPSomaticComponents(oCaster, oSpellCastItem, sComponent, nMetamagic); + + //----------------------------------------------------------------------- + // Shifting casting restrictions + //----------------------------------------------------------------------- + if(nContinue) + nContinue = ShifterCasting(oCaster, oSpellCastItem, nSpellLevel, nMetamagic, sComponent); + + //--------------------------------------------------------------------------- + // Run Material Component Check + //--------------------------------------------------------------------------- + if(nContinue) + nContinue = MaterialComponents(oCaster, nSpellID, nCastingClass, oSpellCastItem); + + //--------------------------------------------------------------------------- + // Run Class Spell-like-ability Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = ClassSLAStore(oCaster, nSpellID, nCastingClass, nSpellLevel); + + //--------------------------------------------------------------------------- + // Run Wild Mage's Wildstrike + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = Wildstrike(oCaster); + + //--------------------------------------------------------------------------- + // Run Inscribe Rune Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = InscribeRune(); + + //--------------------------------------------------------------------------- + // Run Attune Gem Check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = AttuneGem(); + + //--------------------------------------------------------------------------- + // Run use magic device skill check + //--------------------------------------------------------------------------- + if (nContinue) + nContinue = X2UseMagicDeviceCheck(oCaster); + + //----------------------------------------------------------------------- + // run any user defined spellscript here + //----------------------------------------------------------------------- + if (nContinue) + nContinue = X2RunUserDefinedSpellScript(); + + //----------------------------------------------------------------------- + // run any object-specific spellscript here + //----------------------------------------------------------------------- + if (nContinue) + nContinue = PRCRunUserSpecificSpellScript(); + + //----------------------------------------------------------------------- + // Check if spell was used for Duskblade channeling + //----------------------------------------------------------------------- + if (nContinue) + nContinue = DuskbladeArcaneChanneling(oCaster, oTarget, nSpellID, nCasterLevel, nMetamagic, oSpellCastItem); + + //----------------------------------------------------------------------- + // PnP Familiar - deliver touch spell + //----------------------------------------------------------------------- + if(nContinue) + nContinue = DeliverTouchSpell(oCaster, oTarget, nSpellID, nCasterLevel, nSaveDC, nMetamagic, oSpellCastItem); + + //--------------------------------------------------------------------------- + // The following code is only of interest if an item was targeted + //--------------------------------------------------------------------------- + if(GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM) + { + //----------------------------------------------------------------------- + // Check if spell was used to trigger item creation feat + //----------------------------------------------------------------------- + if (nContinue) + nContinue = !ExecuteScriptAndReturnInt("x2_pc_craft", oCaster); + + //----------------------------------------------------------------------- + // Check if spell was used for on a sequencer item + // Check if spell was used for Arcane Archer Imbue Arrow + // Check if spell was used for Spellsword ChannelSpell + //----------------------------------------------------------------------- + if (nContinue) + nContinue = (!X2GetSpellCastOnSequencerItem(oTarget, oCaster, nSpellID, nMetamagic, nCasterLevel, nSaveDC, bSpellIsHostile, oSpellCastItem)); + + //----------------------------------------------------------------------- + // * Execute item OnSpellCast At routing script if activated + //----------------------------------------------------------------------- + if(nContinue) + { + SetUserDefinedItemEventNumber(X2_ITEM_EVENT_SPELLCAST_AT); + //Tag-based PRC scripts first + int nRet = ExecuteScriptAndReturnInt("is_"+GetTag(oTarget), OBJECT_SELF); + if(nRet == X2_EXECUTE_SCRIPT_END) + return FALSE; + + if(GetModuleSwitchValue(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS) == TRUE) + { + nRet = ExecuteScriptAndReturnInt(GetUserDefinedItemEventScriptName(oTarget), OBJECT_SELF); + if(nRet == X2_EXECUTE_SCRIPT_END) + return FALSE; + } + } + + //----------------------------------------------------------------------- + // Prevent any spell that has no special coding to handle targetting of items + // from being cast on items. We do this because we can not predict how + // all the hundreds spells in NWN will react when cast on items + //----------------------------------------------------------------------- + if (nContinue) + nContinue = X2CastOnItemWasAllowed(oTarget); + } + } + + if(nContinue) + { + //eldritch spellweave + EldritchSpellweave(oCaster, oTarget, nSpellLevel, bSpellIsHostile); + + //spellsharing + SpellSharing(oCaster, oTarget, nSpellID, nCasterLevel, nSaveDC, nMetamagic, oSpellCastItem); + + // Combat medic healing kicker + CombatMedicHealingKicker(oCaster, oTarget, nSpellID); + + // Wyrmbane Helm Frightful Presence + WyrmbaneFrightful(oCaster); + + // Havoc Mage Battlecast + Battlecast(oCaster, oTarget, oSpellCastItem, nSpellLevel); + + // Draconic Feat effects + DraconicFeatsOnSpell(oCaster, oTarget, oSpellCastItem, nSpellLevel, nCastingClass); + + // Wand Feats + MasterWand(oCaster, oSpellCastItem, nSpellLevel); + DoubleWandWielder(oCaster, oSpellCastItem, oTarget); + + // Incarnum + MeldArcaneFocus(oCaster, oTarget); + Integument(oCaster, nCastingClass); + + // Mystery + EchoSpell(oCaster, nSpellID); + + // Races + ArkamoiStrength(oCaster, nCastingClass); + WarsoulTyrant(oCaster, nCastingClass); + RedspawnHealing(oCaster, nSpellID, nSpellLevel); + + // Feats + DazzlingIllusion(oCaster, nSchool); + EnergyAbjuration(oCaster, nSchool, nSpellLevel); + InsightfulDivination(oCaster, nSchool, nSpellLevel); + TougheningTransmutation(oCaster, nSchool); + CloudyConjuration(oCaster, nSchool, oSpellCastItem); + + // Reserve Feats + if(GetLocalInt(oCaster, "ReserveFeatsRunning")) OnePingOnly(oCaster); + if(GetLocalInt(oCaster, "DoMysticBacklash")) MysticBacklash(oCaster); + } + + if(bIsPC) + { + if(GetPRCSwitch(PRC_PW_SPELL_TRACKING)) + { + if(!GetLocalInt(oCaster, "UsingActionCastSpell")) + { + string sSpell = IntToString(nOrigSpellID)+"|"; //use original spellID + string sStored = GetPersistantLocalString(oCaster, "persist_spells"); + SetPersistantLocalString(oCaster, "persist_spells", sStored+sSpell); + } + } + + //Cleaning spell variables used for holding the charge + if(!GetLocalInt(oCaster, "PRC_SPELL_EVENT")) + { + DeleteLocalInt(oCaster, "PRC_SPELL_CHARGE_COUNT"); + DeleteLocalInt(oCaster, "PRC_SPELL_CHARGE_SPELLID"); + DeleteLocalObject(oCaster, "PRC_SPELL_CONC_TARGET"); + DeleteLocalInt(oCaster, "PRC_SPELL_METAMAGIC"); + DeleteLocalManifestation(oCaster, "PRC_POWER_HOLD_MANIFESTATION"); + DeleteLocalMystery(oCaster, "MYST_HOLD_MYST"); + } + else if(GetLocalInt(oCaster, "PRC_SPELL_CHARGE_SPELLID") != nSpellID) + { //Sanity check, in case something goes wrong with the action queue + DeleteLocalInt(oCaster, "PRC_SPELL_EVENT"); + } + DeleteLocalInt(oCaster, "SpellIsSLA"); + } + + return nContinue; +} + + +// Test main +//::void main(){} \ No newline at end of file diff --git a/src/include/x3_inc_horse.nss b/src/include/x3_inc_horse.nss new file mode 100644 index 0000000..65548a0 --- /dev/null +++ b/src/include/x3_inc_horse.nss @@ -0,0 +1,3434 @@ + +//:://///////////////////////////////////////////// +//:: Horse Mounting and Control System +//:: x3_inc_horse +//:: Copyright (c) 2007-2008 Bioware Corp. +//::////////////////////////////////////////////// +/* + Holds horse specific functionality which can be used + in other scripts to support horses as present in patch 1.69. +*/ +//::////////////////////////////////////////////// +//:: Created By: Deva B. Winblood +//:: Tweaking By: Martin "Azbest" Psikal +//:: Created On: Dec 14th, 2007 +//:: Last Update: April 21th, 2008 +//:: Help from community members: Proleric, +//:: The Krit, OldMansBeard, FriendlyFire, +//:: Bannor "JP" Bloodfist, Brian Chung, +//:: Craig Welburn, Barry_1066, Jahodnik, +//:: fluffyamoeba, Axe Murderer, and more. +//:: Thank you! +//::////////////////////////////////////////////// + +#include "x0_i0_position" +#include "X0_INC_HENAI" +#include "x3_inc_skin" + +/* + + HOW TO GET THE MOST FROM THIS HORSE SCRIPT: + =========================================== + + This script has been setup to simply work with the default horses but, some + effort was made to provide you with some simple methods to hook your own + mounts, mounting scripts, etc. into this script. + + + [HORSE BLUEPRINT VARIABLES] + =========================== + + The following variables can be set on the BLUEPRINT of a creature to extend + this mounting system. These scripts could be used to add database + functionality, or extend the system however, you like. + + bX3_IS_MOUNT = integer that should be set to 1 if this is a custom blueprint + mount that for some reason the script indicates is not a mount. + + X3_HORSE_PREMOUNT_SCRIPT = script to fire before the horse is mounted. If + this script determines it cannot mount then it will set X3_HORSE_NOMOUNT to + TRUE on the mount. You can use a script like this to create support for + saddlebags or to + use this to extend the mounting system. This will be executed by the rider + and the variable oX3_TempHorse will be set pointing to the horse so that + the custom script knows which horse this relates to. + + X3_HORSE_POSTMOUNT_SCRIPT = can be set to a script that can be fired after + this mount is mounted. This may be useful for adding things like feats and + other after mounting needs. If this + script does not exist then the standard Speed, Skill Decreases, Mounted + Archery Adjustments, etc. will be applied. If you want your own post mount + script then it is important to note you will have to apply these modifiers + if you also want them to be used within your own script. The horse that + was mounted will be referenced by oX3_TempHorse as stored on the Rider. If + you use such a custom hook script make sure to set bX3_IS_MOUNTED to TRUE by + using the SetSkinInt() function from x3_inc_skin. + on the rider after they mount. This is required in some cases for the + dismount radial to work. (mainly when working with custom mounts) + + X3_HORSE_PREDISMOUNT_SCRIPT = can be set to a script that needs to be + executed before the dismount portion continues. If X3_HORSE_NODISMOUNT is + set to TRUE then the horse dismount will be aborted. + + X3_HORSE_POSTDISMOUNT_SCRIPT = can be set to a script to be executed after + dismount to reverse steps that may have occurred with a POSTMOUNT script + such as adding feats. This script could be used to remove feats. + + It is possible you might want to add a new horse/mount type that does not + fit neatly into the appearance.2da or tails.2da with the other horse types. + The following variables if set to any number greater than 0 will override + the default settings and use what you specifiy instead. + + X3_HORSE_NULL_APPEARANCE = the appearance to use when scaling the horse for + the mounting animation. + + X3_HORSE_TAIL = the tail to use with the tails.2da that defines this horse. + + X3_HORSE_FOOTSTEP = the footstep number to use when this horse is mounted. + + X3_HORSE_MOUNT_DURATION = is the duration in seconds 0.0f that the mount + animation should take with this horse. This only needs to be set if + the mounting animation for this blueprint is faster or longer than the + default animations. + + X3_HORSE_MOUNT_SPEED = is the mount speed increase or decrease that should + be used with this mount. If the value is 0 then it will use the + HORSE_DEFAULT_SPEED_INCREASE constant value. + + X3_HORSE_DISMOUNT_DURATION = is the duration in seconds 0.0f that the dis- + mount animation should take with this hores. This only needs to be set if + the dismounting animation for this blueprint is faster or longer than the + default animations. + + X3_HORSE_RESTRICT_race = is a variable that set to TRUE this horse cannot + be mounted by the specified race. Supported races are ELF, HUMAN, HALFELF, + DWARF, HALFORC, HALFLING, and GNOME, CUSTOM# = racial type number. + + X3_CUSTOM_RACE_APPEARANCE = a variable to set on a rider if they use a custom + racial appearance. This value should be set to what appearance number they + use from appearance.2da. This will prevent custom races from being denied + mounting rights due to the script thinking the rider is shape shifted. + + X3_CUSTOM_RACE_MOUNTED_PHENO = This variable should be used on the rider if they + should use a special phenotype when mounted. This is provided to support + custom races. + + X3_CUSTOM_RACE_JOUST_PHENO = This variable should be used on the rider if + they need a special phenotype when mounted in joust mode. + + X3_CUSTOM_RACE_PHENOTYPE = This variable should be used on the rider if they + need a special phenotype when not mounted. + + X3_CUSTOM_RACE_MOUNTED_APPEARANCE = a variable to set on the rider to + indicate which appearance they should use when mounted. + + X3_HORSE_OWNER_TAG = a string that can be set on the horse that will tell + it to Add itself as a henchman to an NPC with the specified tag. + + X3_HORSE_NOT_RIDEABLE_OWNER = if this integer is set to 1 on the Horse then + Mount will not be useable and the error it will return if asked is that it + is NOT rideable due to it being owned by someone else. This is useful if + you want horses around that the PCs and Henchmen cannot mount for reasons + such as they are owned by a store, etc. + + bX3_IS_MOUNT = a variable that should be set to TRUE if a mount is added + as a henchman but, is still mountable. + + X3_NO_MOUNT_ANIMATE = if this integer is set to 1 then this mount does not EVER + animate mounting or dismounting. + + X3_TOTAL_MOUNT_ANIMATION_DELAY = a variable containing a time lot indicating + how much time the routine has before it needs to be finished. it is used for + the sake of synchronizing animation and the process running in the background, + exclusively used in mounting animation portion of the HorseMount() routine, + but can be used elsewhere. Note, that the variable is artificially set even + in case no animation is desired so that the code doesnt happen instantly. It + is not meant to be changed, unless something bad is happening timing-wise. + The value is precalculated and in our particular case it is supposed to hold + the total animation length. + + The following make it easier to implement existing mount support systems + in conjunction with this one. + + X3_HORSE_SCRIPT_MOUNT = Script to call for mounting instead of using the + default one called by the horse menu feat. This still checks to make sure + mounting in the area is legal first. This is intended for making the radial + menus do something different just for this horse and is not intended to + alter the HorseMount(), HorseDismount(), etc. functions. There are already + POSTMOUNT, POSTDISMOUNT, PREMOUNT, and PREDISMOUNT hooks which make that + functionality possible so, that is not what this variable is for. + + X3_HORSE_SCRIPT_DISMOUNT = Script to call for dismounting instead of using + the default one called by the horse menu feat. This is intended for making + the radial menus do something different just for this horse and is not + intended to alter the HorseMount(), HorseDismount(), etc. functions. There + are already POSTMOUNT, POSTDISMOUNT, PREMOUNT, and PREDISMOUNT hooks which + make that functionality possible so, that is not what this variable is for. + + X3_HORSE_SCRIPT_ASSIGN = Script to call for assign mount instead of using + the default one called by the horse menu feat. This is intended for making + the radial menus do something different just for this horse and is not + intended to alter the HorseMount(), HorseDismount(), etc. functions. There + are already POSTMOUNT, POSTDISMOUNT, PREMOUNT, and PREDISMOUNT hooks which + make that functionality possible so, that is not what this variable is for. + + bX3_HAS_SADDLEBAGS = Integer that if set to 1 on the mount will indicate + the horse has saddle bags. It will support inventory control if it is + enabled (NOT: by default). You will also want to set the dialog + X3_DLG_SADDLEBAG on the horse blueprint. Or, create your own dialog that + handles what the saddlebags one does. You will only be able to access + saddlebags of associates in your party. + + + [AREA RELATED VARIABLES TO SET] + =============================== + + X3_NO_HORSES = Horses not allowed in this area + + X3_NO_MOUNTING = Horses may not be ridden in this area and anyone attempting + to do so should be forcibly dismounted. + + X3_HITCHING_POST = if a placeable or waypoint in the area a person was in + before entering a new area has a tag of this then it will move any horses + to this object and set them to STAND_GUARD mode. + + X3_MOUNT_OK_EXCEPTION = is an integer that if set to 1 on this area will + override the external and underground restrictions for this area that may + be set module wide. + + fX3_MOUNT_MULTIPLE = is a floating point value that is multiplied times + all delays if it exists on the area. It can be used to make an area perform + the mounting animations at a different speed if you have really busy areas + and want to make the animation faster or slower. If this is not defined + then it will always be a value of 1.0. If it is set to 0.5 then delays + will be shortened by half. If it is set to 2.0 then the delays will take + 200% longer. This is something a module builder will need to be aware of + and can adjust. NOTE: This can be set on the PC as well and whichever number + is larger is the one that will be used. + + fX3_DISMOUNT_MULTIPLE = is similar to fX3_MOUNT_MULTIPLE and should only be + supplied if you want the dismount to use a different speed than the mount + multiple. It can be set on the area or on the PC. The PC will take + priority over the area. + + bX3_MOUNT_NO_ZAXIS = value to set to TRUE or 1 on the module, PC, or area + to indicate when calculating the proper mounting location you do not want + the Z Axis to be included in the measurement. This has been found to + work well in areas where you do not want the Z axis to be measured in terms + of whether to perform the mounting animation or not. + + X3_ABORT_WHEN_STUCK = if set to TRUE, distance between player and horse is + recorded and checked in each cycle against the current one, when moving to + a horse during the mounting procedure. Should the two ever be equal, meaning + that the player got stuck on his way to horse, the mounting procedure will + be terminated. This is handy in cases when horses are behind obstacles that + are hard to overcome like walls, but the timer that ensures that rider can + mount his horse even in difficult terrain, would eventually force-mount the + potential rider, which could seem like an illogical act. This doesn't need + to be used when using X3_HORSE_ACT_VS_DELAY option, where the timer starts + ticking only after the player gets as close as 1.5m to a horse. The switch + can be set on an area or horses (ie. using an OnEnter script of a trigger). + + + [MODULE RELATED VARIABLES TO SET] + ================================= + + X3_HORSE_PALADIN_USE_PHB = integer that if set to 1 on the module object + will cause the script to use paladin mount summoning durations as specified + in the Player's Handbook 3.5 edition rather than just defaulting to 24 hours. + + X3_HORSE_DISABLE_SPEED = integer that if set to 1 on the module object + will indicate you do not want a speed increase applied when a person mounts. + + X3_HORSE_DISABLE_SKILL = integer that if set to 1 on the module object will + indicate you do not want the skill decreases to be applied when a person + mounts. + + X3_HORSE_ENABLE_ACBOOST = integer that if set to 1 on the module object will + indicate that you want the PCs AC to be increased if need be to at least + match that of the horse that is being mounted. + + X3_HORSE_ENABLE_HPBOOST = integer that if set to 1 on the module object will + indicate that you want the PCs Hit Points to be increased by half of the + hit points of the mount when it is mounted. + + X3_NO_MOUNT_COMMANDABLE = integer that if set to 1 on the module object will + indicate that you do not want the SetCommandable() commands to be used with + the module. + + X3_NO_MOUNTED_COMBAT_FEAT = integer that if set to 1 on the module object + will indicate that you do not want the special code added to Bioware scripts + to try to support Mounted Combat close to how it is in Player's Handbook to + be used. + + X3_ENABLE_MOUNT_DAMAGE = integer that if set to 1 will attempt to transfer + some damage to the mount when a rider dismounts if damage occurred while + they were mounted. + + X3_ENABLE_MOUNT_DB = integer that if set to 1 will enable database and + persistent world support with this script. You will want to modify the + HORSE_Support functions related to the database so, that they write and + read properly however you have the database setup in your module. You will + also want to plan on using something like the x3_mod_def_hb script for your + module heartbeat script if you are going to use the database. This is not + used on modules by default. + + X3_NO_SHAPESHIFT_SPELL_CHECK = integer that if set to 1 on the module will + prevent the script from checking to see if a shapeshifted spell is targetted + on a mounted creature. If this variable is set to 1 then the + x2_inc_spellhook scripts will work exactly like they did before horses were + introduced with no concern whether the target is mounted or not. + + X3_MOUNT_NO_REST_DISMOUNT = integer that if set to 1 on the module will make + it so, you are able to rest while mounted. + + X3_MOUNT_NO_REST_DESPAWN = integer that if set to 1 on the module will make + it so, your paladin mount is not despawned when you rest and adheres + strictly to his summoned duration. If time is advanced by resting then + it is still possible it will despawn. + + X3_MOUNTS_EXTERNAL_ONLY = integer that if set to 1 on the module will make + it so mounts can only be ridden in external areas. There is an exception + variable that can be set on an area to override this. + + X3_MOUNTS_NO_UNDERGROUND = integer that if set to 1 on the module will make + it so mounts cannot be ridden in underground areas. There is an exception + variable that can be set on an area to override this. + + X3_HORSE_ENABLE_SADDLEBAGS = integer that if set to 1 on the module will + enable inventory support for the horse. NOTE: If you want it to use a quick + non-database method for storing the inventory place a waypoint with the tag + X3_HORSE_INVENTORY_STORAGE somewhere in an area that a PC can never get to. + If this waypoint does not exist then it will assume that the database is to + be used. If you are using a database it is adviseable that you change the + support functions because, they use the standard databases and it will often + prove slower than you may like. + + X3_SADDLEBAG_DATABASE = string to set to the name of the database to use + for storing saddlebag inventory. If no name is specified it will use the + module tag and a small modifier. + + X3_HORSE_NO_HENCHMAN_INCREASE = integer that if set to 1 on the module will + prevent the henchmen from being increased to make room for the horse. + + X3_HORSE_MAX_HENCHMEN = integer to set on the module to indicate the maximum + number of henchmen to allow it to be increased to in order to make room for + horses. By default there is no maximum. + + X3_HORSE_NO_CORPSES = integer to set on the module to 1 to indicate you + do not want lootable horse corpses created when a mounted PC or NPC dies. + + X3_RESTORE_HENCHMEN_LOCATIONS = integer to set to 1 on the module if you + want henchmen's henchmen to be restored to a location near the henchman that + is their master when a PC master of the henchman connects. This is NOT + enabled by default to prevent problems with older modules. + + X3_EXTEND_PALMOUNT = string variable that if set on the module object + will allow a person to extend this paladin mount script to call other + scripts in a daisy chain type situation such as saddle bag handling ones. + + X3_EXTEND_PALDMOUNT = string variable that if set on the module object + will allow a person to extend this paladin mount script to call other + scripts in a daisy chain type situation such as saddle bag handling ones. + + X3_HORSE_ACT_VS_DELAY = integer to set on the module to 1 to indicate you + want the system to use Actions as opposed to delays in some portions of the + mounting sequence. Doing this might provide another way to handle inaccessible + horses besides using X3_HORSE_NOT_RIDEABLE_OWNER. It may result in not + being able to access accidentally poorly placed horses due to scripts or + other factors but, it may be desired by some module designers so, it has + been provided as an option. + + + [ON THE MODULE or THE PLAYER] + ============================= + + X3_PALMOUNT_SUMMONOVR = string if set on the module or the player (player has + priority) it will do all the checks to see if a summon is okay. Then if + this variable is set it will execute the script you define here rather than + using the standard summon paladin mount function. Beware: If you use this + then handling all other aspects of this mount become your responsability. + + fX3_TIMEOUT_TO_MOUNT = value to set on the module or PC to indicate how long + the PC/NPC should attempt to move into a proper mounting animation to perform + the mounting animation. When this time is reached if it is still not in + position it will instant mount instead and will not animate. If this value is + lower than 6.0 or is not set, then the default value of 18.0 will be used. + + fX3_FREQUENCY = frequency of recursive call of the HorseMount() function to + try and initiate new pathfinding to the horse everytime until the character + reaches the mounting position or until the time limit for mounting is up or + unless X3_HORSE_ACT_VS_DELAY is set to TRUE, in which case the actionqueue + is not locked and moving towards a horse is interruptable, ie. by clicking. + if set larger than 9.0 or less than 1.0 the value defaults to 2.0 seconds. + + bX3_MOUNT_NO_ZAXIS = value to set to TRUE or 1 on the module, PC, or area + to indicate when calculating the proper mounting location you do not want + the Z Axis to be included in the measurement. This has been found to + work well in areas where you do not want the Z axis to be measured in terms + of whether to perform the mounting animation or not. + + + [SCRIPT TO OVERRIDE SKIN SYSTEM] + ================================ + + The skin system is designed to supply the horse radial menus to older characters + and it also is used to track some of the information about the state of the + PC semi-persistently. The system is setup in such a way that PRC or HCR or + other systems that use skins will still use their own skin as long as they + run first. If the horse system does not detect a skin it will create one. + Thus, if it runs before PRC or HCR create their skins there might be a problem. + To make this less of an upgrade nightmare the horse script includes support + for a hook script. If PRC, or HCR or other scripting package people create + a script named [x3_mod_pre_enter] and make that script add their skin to the + PC then this script WILL execute ALWAYS before the horse scripts create + their skin. This means all you need to do is make such a script and have + it add your skin and your scripts should function as they did before (EVEN) + if the horse scripts execute other aspects before yours. + + + [DEBUGGING AND TWEAKING SCRIPTS] + ================================ + + Press ~ in game to bring up console mode. Type DebugMode 1 and press enter. + Press ~ in game and this time type dm_runscript scriptname and press enter. + Press ~ in game and type DebugMode 0 when you want to disable debug mode. + SCRIPTS: + x3_fix_horse = use this when the PC is being treated as mounted when it is not + x3_fix_horseout = use this to make a module not designed with horses in mind + only allow horses in outside areas. + x3_fix_nocmd = set it not to use the SetCommandable with mounting toggle + x3_fix_act = set to use actions when mounting toggle + x3_fix_speed100 = mount/dismount speed multiple set to normal 100% + x3_fix_speed125 = mount/dismount speed multiple set to 1255 (25% slower) + x3_fix_speed150 = mount/dismount speed multiple set to 150% (50% slower) + x3_fix_speed200 = mount/dismount speed multiple set to 200% (100% slower) + + +*/ + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// CONSTANTS ////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +const string X3_HORSE_DATABASE = "X3HORSE"; + +// The following constants are provided to facilitate extending the system +// if need be. They are also provided in the event that a hak pack used +// in a module might rearrange the location of some of the aspects needed +// to handle horses. +// This constant points to the location in the appearance.2da where the +// unmounted horse appearances occur. +const int HORSE_APPEARANCE_OFFSET = 496; +// This constant points to the location in the tails.2da where the horse +// appearances begin. +const int HORSE_TAIL_OFFSET = 15; +// This constant lists how many horses are listed beginning at the location +// specified by the offset. +const int HORSE_NUMBER_OF_HORSES = 65; +// This is the prefix that should be used with the paladin mounts when spawning +// them. +const string HORSE_PALADIN_PREFIX = "x3_palhrs"; +// The constants that follow here specify what appearance to use when scaling +// the horse as a tail during mounting process to make the animation be handled +// properly. +const int HORSE_NULL_RACE_DWARF = 562; +const int HORSE_NULL_RACE_ELF = 563; +const int HORSE_NULL_RACE_HALFLING = 565; +const int HORSE_NULL_RACE_HUMAN = 568; +const int HORSE_NULL_RACE_HALFELF = 566; +const int HORSE_NULL_RACE_HALFORC = 567; +const int HORSE_NULL_RACE_GNOME = 564; +// The constants that follow here specify the appearance that should be used +// when the specified mount is mounted. These appearances are often required +// to set the proper speeds, radiuses, etc. They also have the complete +// phenotypes and animations associated with them. +const int HORSE_RACE_MOUNTED_DWARFM = 483; +const int HORSE_RACE_MOUNTED_DWARFF = 482; +const int HORSE_RACE_MOUNTED_ELFM = 485; +const int HORSE_RACE_MOUNTED_ELFF = 484; +const int HORSE_RACE_MOUNTED_GNOMEM = 487; +const int HORSE_RACE_MOUNTED_GNOMEF = 486; +const int HORSE_RACE_MOUNTED_HALFLINGM= 489; +const int HORSE_RACE_MOUNTED_HALFLINGF= 488; +const int HORSE_RACE_MOUNTED_HALFELFM = 491; +const int HORSE_RACE_MOUNTED_HALFELFF = 490; +const int HORSE_RACE_MOUNTED_HALFORCM = 493; +const int HORSE_RACE_MOUNTED_HALFORCF = 492; +const int HORSE_RACE_MOUNTED_HUMANM = 495; +const int HORSE_RACE_MOUNTED_HUMANF = 494; +// The following constants indicate which phenotype numbers should be used by +// the mounting system. _N specifies the mounting race started as a normal +// phenotype, and _L specifies the race started as a large phenotype. +const int HORSE_PHENOTYPE_MOUNTED_N = 3; +const int HORSE_PHENOTYPE_MOUNTED_L = 5; +const int HORSE_PHENOTYPE_JOUSTING_N = 6; +const int HORSE_PHENOTYPE_JOUSTING_L = 8; +// The following constants indicate which animation numbers indicate which +// animations can be used well with the horse system. +const int HORSE_ANIMATION_MOUNT = 41; +const int HORSE_ANIMATION_DISMOUNT = 42; +const int HORSE_ANIMATION_LOOPING_JOUST_VIOLENT_FALL = ANIMATION_LOOPING_CUSTOM3; +const int HORSE_ANIMATION_LOOPING_JOUST_GLANCE = ANIMATION_LOOPING_CUSTOM4; +const int HORSE_ANIMATION_LOOPING_JOUST_FALL = ANIMATION_LOOPING_CUSTOM5; +const int HORSE_ANIMATION_LOOPING_JOUST_STAB = ANIMATION_LOOPING_CUSTOM10; +const int HORSE_ANIMATION_LOOPING_JOUST_HELMOFF = ANIMATION_LOOPING_CUSTOM4; +// The following constant defines what footstep sound should be used when +// the horse is mounted. +const int HORSE_FOOTSTEP_SOUND = 17; +// The following constant defines the duration in seconds that it should take +// to complete the default animation. +const float HORSE_MOUNT_DURATION = 2.0f; +// The following constant defines the duration in seconds that it should take +// to complete the default animation. +const float HORSE_DISMOUNT_DURATION = 3.0f; +// The following constants refer to specific feats that might be needed to +// handle horses +const int IP_CONST_HORSE_MENU = 40; +// The constant below designates the default speed increase that should be +// granted when a person mounts a horse. +const int HORSE_DEFAULT_SPEED_INCREASE= 99; +// The following constants were added to support the new feats. When these +// constants are added to the master constants list they can be removed from +// this part of the script. +//const int FEAT_MOUNTED_COMBAT = 1087; +//const int FEAT_MOUNTED_ARCHERY = 1088; +// Scripts called by the above feats +// x3_s3_horse +// x3_s3_palmount + +// This constant is a safety to be inserted between the ClearAllActions and +// a command to play a dismount animation. If set to 0.0 or if missing, the +// dismount animation will not play properly when trying to dismount a horse +// while in motion. If you ever encounter problems with mounting animation, +// try inserting this small delay in the same fashion in the animated mounting +// procedure. All pending DelayCommands shall respect this delay accordingly +// for the sake of precise timing. Values of 0.8f and above are safe to use. +const float X3_ACTION_DELAY = 0.8f; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// PROTOTYPES //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +// FILE: x3_inc_horse FUNCTION: HorseMount() +// This function will cause the calling object to attempt to mount oHorse. +// If bAnimate is set to TRUE this will cause the calling object and oHorse +// to be involved in the complete animating process. If you bAnimate is false +// and bInstant is false the caller will still walk to the horse before mounting. +// If bAnimate is false and bInstant is TRUE then the script will immediately +// mount oHorse without any moving (this is the quickest method if needed for +// situations like cutscenes). nState is a variable used by the function itself +// and need not be adjusted when this function is called. +void HorseMount(object oHorse,int bAnimate=TRUE,int bInstant=FALSE,int nState=0); + + +// FILE: x3_inc_horse FUNCTION: HorseDismount() +// This function will cause the calling object to dismount if it is mounted. If +// bAnimate is TRUE then it will animate the dismount. If bOwner is set to +// TRUE then it will set the object that dismounted as the owner. This function +// will return the object that is the dismounted horse. +object HorseDismount(int bAnimate=TRUE,int bSetOwner=TRUE); + + +// FILE: x3_inc_horse FUNCTION: HorseSetOwner() +// This function will set oOwner as the owner of oHorse. If bAssign is set to +// TRUE it will also set this horse to be oOwners assigned mount. This is not +// done by default so more than one mount can be assigned to a single target if +// need be. +void HorseSetOwner(object oHorse,object oOwner,int bAssign=FALSE); + + +// FILE: x3_inc_horse FUNCTION: HorseRemoveOwner() +// This function will remove the owner from oHorse. This will not work on +// Paladin mounts. +void HorseRemoveOwner(object oHorse); + + +// FILE: x3_inc_horse FUNCTION: HorseGetCanBeMounted() +// This function will return TRUE if oTarget can be mounted. If oRider is +// specified it will also make sure that oTarget can be ridden by oRider. +// The bAssignMount field should be set to TRUE if the call of this function +// should ignore whether someone can mount a horse in the area or not. +int HorseGetCanBeMounted(object oTarget,object oRider=OBJECT_INVALID,int bAssignMount=FALSE); + + +// FILE: x3_inc_horse FUNCTION: HorseSummonPaladinMount() +// This function can be used to cause the calling object to summon the paladin +// mount. If bPHBDuration is set to TRUE then the mount will use the Players +// Handbook 3.0 edition rules for the duration that the mount will stay around. +object HorseSummonPaladinMount(int bPHBDuration=FALSE); + + +// FILE: x3_inc_horse FUNCTION: HorseUnsummonPaladinMount() +// This function can be used to cause the calling object to unsummon its paladin +// mount. +void HorseUnsummonPaladinMount(); + + +// FILE: x3_inc_horse FUNCTION: HorseGetIsMounted() +// This function will return TRUE if oTarget is mounted. +int HorseGetIsMounted(object oTarget); + + +// FILE: x3_inc_horse FUNCTION: HorseCreateHorse() +// This function will create a horse based on the blueprint sResRef at location +// lLoc and will set the owner of the horse to oOwner. If sTag is set to +// something other than "" it will set the horse to that tag. If nAppearance, +// nTail, and nFootstep are set to something other than -1 then it will set the +// horse to that appearance, tail, or footstep. The function will return the +// horse that is spawned. This function is setup the way it is so, that you +// could potentially use a single blueprint to store multiple appearance horses. +// sScript is a specific script that should be executed on the horse after it is +// spawned. This is again supplied to further support multiple horses with a +// single blueprint. +object HorseCreateHorse(string sResRef,location lLoc,object oOwner=OBJECT_INVALID,string sTag="",int nAppearance=-1,int nTail=-1,int nFootstep=-1,string sScript=""); + + +// FILE: x3_inc_horse FUNCTION: HorseGetOwner() +// This function will return an object pointer to whom the owner of oHorse is +// if there is a valid horse. If there is no owner it will return +// OBJECT_INVALID. +object HorseGetOwner(object oHorse); + + +// FILE: x3_inc_horse FUNCTION: HorseSetPhenotype() +// This function will set oRider to the correct mounted phenotype for riding +// a horse. If bJoust is set to TRUE it will set oRider to the mounted jousting +// phenotype. This is a special phenotype with differing animation sets and +// designed to hold the lance in a very specific way. +void HorseSetPhenotype(object oRider,int bJoust=FALSE); + + +// FILE: x3_inc_horse FUNCTION: HorseInstantMount() +// This function is primarily supplied for cutscenes and other instances where +// you simply need oRider to be switched to mounted mode as quickly as possible +// without preserving any variables or anything. However, you can store a +// resref for the horse to dismount by setting sResRef. If bJoust is set to +// TRUE then it will use the Joust Phenotypes. +// WARNING: This does not care whether someone meets the criteria to mount, +// or even if they are already mounted. It will simply set them to the +// proper appearance and mode. The ResRef is provided only in case someone +// uses HorseDismount() instead of using HorseInstantDismount() with this +// rider. It is recommended this function only be used in conjunction with +// HorseInstantDismount(). +void HorseInstantMount(object oRider,int nTail,int bJoust=FALSE,string sResRef=""); + + +// FILE: x3_inc_horse FUNCTION: HorseInstantDismount() +// This function is used to rapidly dismount oRider and does not produce a horse +// object. It's intended usage is with cutscenes or other instances where +// having oRider dismounted as quickly as possible are required. This will not +// produce horse/mount as it is primarily intende for cutscene work and not +// intended to replace the HorseDismount() function in other cases. +// WARNING: This does not protect Saddlebags so, it is recommended this only +// be used in conjunction with HorseInstantMount. If you need to protect +// saddlebag contents use HorseDismount(). +void HorseInstantDismount(object oRider); + + +// FILE: x3_inc_horse FUNCTION: HorseGetMountTail() +// This function will return the tail that should be used with the specified +// horse. +int HorseGetMountTail(object oHorse); + + +// FILE: x3_inc_horse FUNCTION: HorseGetMountFailureMessage() +// This is a companion function to HorseGetCanBeMounted. If you need a text +// message that explains why the horse cannot be mounted. +string HorseGetMountFailureMessage(object oTarget,object oRider=OBJECT_INVALID); + + +// FILE: x3_inc_horse FUNCTION: HorseAddHorseMenu() +// This function will add horse menus to the respective PC. This is only +// needed for PCs that were not created new using patch 1.69. +void HorseAddHorseMenu(object oPC); + + +// FILE: x3_inc_horse FUNCTION: HorseGetPaladinMount() +// If this rider has a paladin mount then it will be returned as the object. +// If the rider is currently riding their paladin mount then it will return +// oRider as the object. If no paladin mount currently exists for oRider it +// will return OBJECT_INVALID. +object HorseGetPaladinMount(object oRider); + + +// FILE: x3_inc_horse FUNCTION: HorseGetIsAMount() +// This will return TRUE if oTarget is a mountable creature. +int HorseGetIsAMount(object oTarget); + + +// FILE: x3_inc_horse FUNCTION: HorseGetIsDisabled() +// This function detects if oCreature is in a disabled state. Can be used to +// detect disabling effects such as death, fear, confusion, sleep, paralysis, +// petrify, stun, entangle and knockdown (also applied in a non-hostile way, +// where oCreature doesn't enter a combat state) +int HorseGetIsDisabled(object oCreature=OBJECT_SELF); + + +// FILE: x3_inc_horse FUNCTION: HorseReloadFromDatabase() +// This function is provided for use with an OnClientEnter script when you +// are using a persistent world type environment and need the PC's mounted +// state reloaded. This will also make sure that henchmen for the PC are also +// restored as they were. +void HorseReloadFromDatabase(object oPC,string sDatabase); + + +// FILE: x3_inc_horse FUNCTION: HorseSaveToDatabase() +// This will save all of the current status for the PC and the PCs henchmen. +void HorseSaveToDatabase(object oPC,string sDatabase); + + +// FILE: x3_inc_horse FUNCTION: HorseStoreInventory() +// This function is used to store inventory of the horse for later recovery. +// See x3_inc_horse for complete details. +void HorseStoreInventory(object oCreature,object oRider=OBJECT_INVALID); + + +// FILE: x3_inc_horse FUNCTION: HorseRestoreInventory() +// This function is used to return stored inventory back onto the horse +// See x3_inc_horse for complete details. If bDrop is set to TRUE then it +// will drop the contents where the horse is rather than putting them on the +// horse. bDrop was primarily intended for cases where a mounted PC dies. +void HorseRestoreInventory(object oCreature,int bDrop=FALSE); + + +// FILE: x3_inc_horse FUNCTION: HorseChangeToDefault() +// This function will set oCreature to the default racial appearance and is +// useful for reversing any situations where a creature or PC is stuck in some +// variation of a mounted appearance. This will also clear ANY information +// stored on the creature relating to mounting. NOTE: If the target had a +// tail when not mounted this function will not restore that. +void HorseChangeToDefault(object oCreature); + + +// FILE: x3_inc_horse FUNCTION: HorseIfNotDefaultAppearanceChange() +// This funtion will check the appearance of oCreature, If it is not set to +// the default racial appearance (see racialtypes.2da) then it will call the +// HorseChangeToDefault() function. +void HorseIfNotDefaultAppearanceChange(object oCreature); + + +// FILE: x3_inc_horse FUNCTION: HorseGetMyHorse() +// When oRider is dismounted, this function returns the horse currently assigned +// to oRider (OBJECT_INVALID if there is none). In certain script hooks +// (pre-mount, post-mount and post-dismount), the function returns the horse +// that is currently being mounted / dismounted. It should not be used when the +// rider is mounted, or in a pre-dismount script hook. +object HorseGetMyHorse(object oRider); + + +// FILE: x3_inc_horse FUNCTION: HorseGetHasAHorse() +// This function will return TRUE if oRider has a mount or horse. +int HorseGetHasAHorse(object oRider); + + +// FILE: x3_inc_horse FUNCTION: HorseGetHorse() +// This function will return the nNth horse that is owned by oRider. +object HorseGetHorse(object oRider,int nN=1); + + +// FILE: x3_inc_horse FUNCTION: HorseRestoreHenchmenLocations() +// This function will check the module variable X3_RESTORE_HENCHMEN_LOCATIONS +// which when set to TRUE on the module will cause this function to jump any +// npc henchmen to the PC and will also make sure their henchmen also jump. +// This function is provided so, that there exists a function which will remain +// true to whether henchmen are mounted in no mount areas and whether mounts are +// prevented from entering no mount areas. This is not the perfect function +// but, it will address some of the issues with the engine not restoring the +// locations of henchmen belonging to henchmen on a module reload. This will +// actually NOT jump henchmen to the PC. It's primary purpose is to make sure +// that henchmen belonging to the henchman are jumped to the proper location. +// It is assumed that the henchman that is their master will already be at the +// proper location due to a reload or due to a respawn from some special script +// for the server. +void HorseRestoreHenchmenLocations(object oPC); + + +// FILE: x3_inc_horse FUNCTION: HorseHitchHorses() +// This function is designed to be used in conjunction with a transition script. +// oHitch is an object to hitch horses/mounts near. If it is OBJECT_INVALID +// then a specific destination to hitch will not be used. oClicker is who +// clicked the area transistion. lPreJump is a pre-transition location which +// is used to determine where the horses should stay near (actually walk a +// small distance away) if they do not transition. +void HorseHitchHorses(object oHitch,object oClicker,location lPreJump); + + +// FILE: x3_inc_horse FUNCTION: HorseForceJump() +// This is used to make sure oJumper makes it to withing fRange of oDestination. +// The nTimeOut is used to set a maximum number of attempts that will be made. +void HorseForceJump(object oJumper,object oDestination,float fRange=2.0,int nTimeOut=10); + + +// FILE: x3_inc_horse FUNCTION: HorseMoveAssociates() +// This function is intended to be used after a transition has been made and +// oMaster has moved onto the target destination. This function will cause +// any associates that also transitioned to move away from oMaster a small +// amount to make sure they do not block movement for oMaster. This can be +// particularly important when you consider horses have a bit more personal +// space requirements than the traditional associate. +void HorseMoveAssociates(object oMaster); + + +// FILE: x3_inc_horse FUNCTION: HorsePreloadAnimations() +// The mount and dismount animations are rather complex and by default the +// aurora engine does not always preload these animations. This function was +// created to temporarily set the tail and appearance of a PC to that of one of +// the horse models that will thus, force preloading of the animations. After +// that is done the animations should played properly. This is a good function +// to call in the OnEnter script of the module or similar location. +void HorsePreloadAnimations(object oPC); + + +//////////////////////////////////////////////////////////////////////////////// +// SUPPORT FUNCTIONS +//----------------------------- +// These functions are not intended to be called by external scripts to this +// file and are simply used as support functions called by other functions +// from within this script. +//////////////////////////////////////////////////////////////////////////////// + + +int HORSE_SupportGetMountedAppearance(object oRider) +{ // PURPOSE: Return which appearance the rider should use when mounted + int nRace=GetRacialType(oRider); + int nGender=GetGender(oRider); + if (GetLocalInt(oRider,"X3_CUSTOM_RACE_MOUNTED_APPEARANCE")>0) return GetLocalInt(oRider,"X3_CUSTOM_RACE_MOUNTED_APPEARANCE"); + if (nGender==GENDER_FEMALE) + { // female + if (nRace==RACIAL_TYPE_DWARF) return HORSE_RACE_MOUNTED_DWARFF; + else if (nRace==RACIAL_TYPE_ELF) return HORSE_RACE_MOUNTED_ELFF; + else if (nRace==RACIAL_TYPE_GNOME) return HORSE_RACE_MOUNTED_GNOMEF; + else if (nRace==RACIAL_TYPE_HALFLING) return HORSE_RACE_MOUNTED_HALFLINGF; + else if (nRace==RACIAL_TYPE_HALFELF) return HORSE_RACE_MOUNTED_HALFELFF; + else if (nRace==RACIAL_TYPE_HALFORC) return HORSE_RACE_MOUNTED_HALFORCF; + else if (nRace==RACIAL_TYPE_HUMAN) return HORSE_RACE_MOUNTED_HUMANF; + } // female + else + { // male + if (nRace==RACIAL_TYPE_DWARF) return HORSE_RACE_MOUNTED_DWARFM; + else if (nRace==RACIAL_TYPE_ELF) return HORSE_RACE_MOUNTED_ELFM; + else if (nRace==RACIAL_TYPE_GNOME) return HORSE_RACE_MOUNTED_GNOMEM; + else if (nRace==RACIAL_TYPE_HALFLING) return HORSE_RACE_MOUNTED_HALFLINGM; + else if (nRace==RACIAL_TYPE_HALFELF) return HORSE_RACE_MOUNTED_HALFELFM; + else if (nRace==RACIAL_TYPE_HALFORC) return HORSE_RACE_MOUNTED_HALFORCM; + else if (nRace==RACIAL_TYPE_HUMAN) return HORSE_RACE_MOUNTED_HUMANM; + } // male + return GetAppearanceType(oRider); +} // HORSE_SupportGetMountedAppearance() + + +string HORSE_SupportRaceRestrictString(object oRider) +{ // PURPOSE: This will return the race restriction string to use for oRider + string sRet="X3_HORSE_RESTRICT_"; + int nRace=GetRacialType(oRider); + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid + if (nRace==RACIAL_TYPE_DWARF) sRet=sRet+"DWARF"; + else if (nRace==RACIAL_TYPE_ELF) sRet=sRet+"ELF"; + else if (nRace==RACIAL_TYPE_GNOME) sRet=sRet+"GNOME"; + else if (nRace==RACIAL_TYPE_HALFELF) sRet=sRet+"HALFELF"; + else if (nRace==RACIAL_TYPE_HALFLING) sRet=sRet+"HALFLING"; + else if (nRace==RACIAL_TYPE_HALFORC) sRet=sRet+"HALFORC"; + else if (nRace==RACIAL_TYPE_HUMAN) sRet=sRet+"HUMAN"; + else {sRet=sRet+"CUSTOM"+IntToString(nRace);} + } // valid + return sRet; +} // HORSE_SupportRaceRestrictString(oRider) + + +string HORSE_SupportMountResRef(object oRider) +{ // PURPOSE: Return the resref of the mount oRider is on + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetSkinString(oRider,"sX3_HorseResRef"); + } // valid parameter + return ""; +} // HORSE_SupportMountResRef() + + +int HORSE_SupportMountAppearance(object oRider) +{ // PURPOSE: Return the appearance of the mount oRider is on + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetSkinInt(oRider,"nX3_HorseAppearance"); + } // valid parameter + return -1; +} // HORSE_SupportMountAppearance() + + +int HORSE_SupportMountTail(object oRider) +{ // PURPOSE: Return the tail of the mount oRider is on + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetCreatureTailType(oRider); + } // valid parameter + return -1; +} // HORSE_SupportMountTail() + + +int HORSE_SupportMountFootstep(object oRider) +{ // PURPOSE: Return the footstep sound of the rider is supposed to be when not + // mounted + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetSkinInt(oRider,"nX3_StoredFootstep"); + } // valid parameter + return -1; +} // HORSE_SupportMountFootstep() + + +string HORSE_SupportMountTag(object oRider) +{ // PURPOSE: Return the tag of the mount that rider is riding + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetSkinString(oRider,"sX3_HorseMountTag"); + } // valid parameter + return ""; +} // HORSE_SupportMountTag() + + +string HORSE_SupportMountScript(object oRider) +{ // PURPOSE: Return the post spawn script to run for the horse oRider is riding + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetLocalString(oRider,"sX3_HorseMountString"); + } // valid parameter + return ""; +} // HORSE_SupportMountScript() + + +int HORSE_SupportRiderAppearance(object oRider) +{ // PURPOSE: Return the appearance of oRider when not mounted + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + return GetSkinInt(oRider,"nX3_HorseRiderAppearance"); + } // valid parameter + return -1; +} // HORSE_SupportRiderAppearance() + + +int HORSE_SupportRiderPhenotype(object oRider) +{ // PURPOSE: Return the phenotype of the rider when not mounted + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + if (GetLocalInt(oRider,"X3_CUSTOM_RACE_PHENOTYPE")) return GetLocalInt(oRider,"X3_CUSTOM_RACE_PHENOTYPE"); + return GetSkinInt(oRider,"nX3_HorseRiderPhenotype"); + } // valid parameter + return -1; +} // HORSE_SupportRiderPhenotype() + + +void HORSE_SupportCleanVariables(object oTarget) +{ // PURPOSE: Remove any mount related variables after dismount + DeleteLocalInt(oTarget,"X3_HORSE_NULL_APPEARANCE"); + //DeleteSkinInt(oTarget,"X3_HORSE_APPEARANCE"); + DeleteLocalInt(oTarget,"X3_HORSE_TAIL"); + //DeleteSkinInt(oTarget,"nX3_HorseRiderAppearance"); + //DeleteSkinInt(oTarget,"nX3_HorseRiderPhenotype"); + //DeleteSkinInt(oTarget,"nX3_StoredFootstep"); + DeleteSkinString(oTarget,"X3_HORSE_PREDISMOUNT_SCRIPT"); + DeleteSkinString(oTarget,"X3_HORSE_POSTDISMOUNT_SCRIPT"); + DeleteLocalString(oTarget,"X3_HORSE_PREMOUNT_SCRIPT"); + DeleteLocalString(oTarget,"X3_HORSE_POSTMOUNT_SCRIPT"); + DeleteSkinString(oTarget,"sX3_HorseResRef"); + DeleteSkinString(oTarget,"sX3_HorseMountTag"); + DeleteLocalString(oTarget,"sX3_HorseMountScript"); + DeleteLocalInt(oTarget,"X3_NO_MOUNT_CUTSCENE"); + DeleteLocalInt(oTarget,"X3_NO_MOUNT_ANIMATE"); + DeleteLocalInt(oTarget,"nX3_RiderHP"); + DeleteSkinInt(oTarget,"nX3_HorseHP"); + DeleteLocalInt(oTarget,"nX3_HorsePortrait"); + DeleteLocalInt(oTarget,"bX3_HAS_SADDLEBAGS"); + DeleteLocalString(oTarget,"sDB_Inv"); + DeleteSkinInt(oTarget,"bX3_IS_MOUNTED"); + DeleteLocalInt(oTarget,"nX3_StoredMountState"); + DeleteLocalInt(oTarget,"X3_ABORT_WHEN_STUCK"); + //DeleteSkinInt(oTarget,"nX3_HorseRiderTail"); +} // HORSE_SupportCleanVariables() + + +void HORSE_SupportMountCleanVariables(object oRider) +{ // PURPOSE: Clean variables off of oRider related to the mount + if (GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + HORSE_SupportCleanVariables(oRider); + } // valid parameter +} // HORSE_SupportMountCleanVariables() + + +location HORSE_SupportGetMountLocation(object oCreature,object oRider,float fDirection=90.0) +{ // PURPOSE: To Locate the location to place the mount + object oArea=GetArea(oCreature); + vector vPos=GetPosition(oCreature); + float fFace=GetFacing(oCreature); + location lLoc; + float fDist=0.8*StringToFloat(Get2DAString("appearance","WING_TAIL_SCALE",HORSE_SupportGetMountedAppearance(oRider))); + lLoc=Location(oArea,vPos+fDist*AngleToVector(fFace-fDirection),fFace); + return lLoc; +} // HORSE_SupportGetMountLocation() + + +int HORSE_SupportNullAppearance(object oHorse,object oRider) +{ // PURPOSE: This will return which appearance should be used to handle animation + int nRace; + if (GetLocalInt(oHorse,"X3_HORSE_NULL_APPEARANCE")>0) return GetLocalInt(oHorse,"X3_HORSE_NULL_APPEARANCE"); + nRace=GetRacialType(oRider); + if (nRace==RACIAL_TYPE_DWARF) return HORSE_NULL_RACE_DWARF; + else if (nRace==RACIAL_TYPE_ELF) return HORSE_NULL_RACE_ELF; + else if (nRace==RACIAL_TYPE_GNOME) return HORSE_NULL_RACE_GNOME; + else if (nRace==RACIAL_TYPE_HALFELF) return HORSE_NULL_RACE_HALFELF; + else if (nRace==RACIAL_TYPE_HALFLING) return HORSE_NULL_RACE_HALFLING; + else if (nRace==RACIAL_TYPE_HALFORC) return HORSE_NULL_RACE_HALFORC; + return HORSE_NULL_RACE_HUMAN; +} // HORSE_SupportNullAppearance() + + +int Horse_SupportRaceAppearance(int nAppearance) +{ // PURPOSE: This will return TRUE if the appearance passed as a parameter + // is an appearance used as part of the mounting process. + if (nAppearance==HORSE_RACE_MOUNTED_DWARFF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_DWARFM) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_ELFF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_ELFM) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_GNOMEF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_GNOMEM) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HALFELFF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HALFELFM) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HALFLINGF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HALFLINGM) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HALFORCF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HALFORCM) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HUMANF) return TRUE; + else if (nAppearance==HORSE_RACE_MOUNTED_HUMANM) return TRUE; + return FALSE; +} // Horse_SupportRaceAppearance() + + +void HORSE_SupportIncreaseSpeed(object oRider,object oHorse) +{ // PURPOSE: Change the movement speed based on that of the horse (wont work unless EffectMovementSpeedIncrease() bug is fixed) + if (GetLocalInt(GetModule(),"X3_HORSE_DISABLE_SPEED")) return; + int nSpeed=GetLocalInt(oHorse,"X3_HORSE_MOUNT_SPEED"); + effect eMovement; + float fSpeed; + int nMonkEpic; + int nMonk=GetLevelByClass(CLASS_TYPE_MONK,oRider); + if (nSpeed==0) nSpeed=HORSE_DEFAULT_SPEED_INCREASE; + if (nSpeed>50) nSpeed=50; // upper limit + if (nSpeed<-100) nSpeed=-100; + if (nMonk>2) + { // find out monk adjustment + nSpeed-=10*nMonk/3; + //nSpeed-=GetBuggedSpeedAdjustment(oRider)+10*nMonk/3; + } // find out monk adjustment + if (GetLevelByClass(CLASS_TYPE_BARBARIAN,oRider)>0) + { // barbarian class adjustment + nSpeed-=10; + //nSpeed-=GetBuggedSpeedAdjustment(oRider)+10; + } // barbarian class adjustment + if (nSpeed<-150) nSpeed=-150; + SetLocalInt(oRider,"nX3_SpeedIncrease",nSpeed); + eMovement=SupernaturalEffect(EffectMovementSpeedIncrease(nSpeed)); // EffectMovementSpeedIncrease() with negative numbers is the same as EffectMovementSpeedDecrease() with positive numbers + ApplyEffectToObject(DURATION_TYPE_PERMANENT,eMovement,oRider); +} // HORSE_SupportIncreaseSpeed() + + +void HORSE_SupportOriginalSpeed(object oRider) +{ // PURPOSE: Remove speed increases based upon mounted + if (GetLocalInt(GetModule(),"X3_HORSE_DISABLE_SPEED")) return; + effect eSearch=GetFirstEffect(oRider); + while(GetIsEffectValid(eSearch)) + { // cycle through effects + if((GetEffectType(eSearch) == EFFECT_TYPE_MOVEMENT_SPEED_INCREASE) && + (GetEffectDurationType(eSearch) == DURATION_TYPE_PERMANENT) && + (GetEffectSubType(eSearch) == SUBTYPE_SUPERNATURAL)) + { // check to see if matches conditions + RemoveEffect(oRider,eSearch); + } // check to see if matches conditions + eSearch=GetNextEffect(oRider); + } // cycle through effects +} // HORSE_SupportOriginalSpeed() + + +void HORSE_SupportAdjustMountedArcheryPenalty(object oRider) +{ // PURPOSE: Check for feats and adjust penalties to archery while mounted + int nMountedArcheryPenalty=GetLocalInt(oRider,"nX3_MountedArchery"); + int nNewPenalty; + effect eEffect; + int bMatched; + //if (HorseGetIsMounted(oRider)&&GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oRider))) + if (HorseGetIsMounted(oRider)&&GetLocalInt(oRider,"bX3_M_ARCHERY")) + { // Rider is mounted and has a ranged weapon - apply penalty + nNewPenalty=4; + if (GetHasFeat(FEAT_MOUNTED_ARCHERY,oRider)) nNewPenalty=2; + if (nNewPenalty!=nMountedArcheryPenalty&&nMountedArcheryPenalty>0) + { // remove existing penalties before applying the new one + eEffect=GetFirstEffect(oRider); + bMatched=FALSE; + while(GetIsEffectValid(eEffect)&&!bMatched) + { // traverse effects + if ((GetEffectType(eEffect)==EFFECT_TYPE_ATTACK_DECREASE)&& + (GetEffectSubType(eEffect)==SUBTYPE_SUPERNATURAL)&& + (GetEffectDurationType(eEffect)==DURATION_TYPE_PERMANENT)) + { // match + RemoveEffect(oRider,eEffect); + bMatched=TRUE; + } // match + eEffect=GetNextEffect(oRider); + } // traverse effects + } // remove existing penalties before applying the new one + ApplyEffectToObject(DURATION_TYPE_PERMANENT,SupernaturalEffect(EffectAttackDecrease(nNewPenalty)),oRider); + SetLocalInt(oRider,"nX3_MountedArchery",nNewPenalty); + } // Rider is mounted and has a ranged weapon - apply penalty + else if (nMountedArcheryPenalty) + { // no penalty needed - remove it + eEffect=GetFirstEffect(oRider); + bMatched=FALSE; + while(GetIsEffectValid(eEffect)&&!bMatched) + { // traverse effects + if ((GetEffectType(eEffect)==EFFECT_TYPE_ATTACK_DECREASE)&& + (GetEffectSubType(eEffect)==SUBTYPE_SUPERNATURAL)&& + (GetEffectDurationType(eEffect)==DURATION_TYPE_PERMANENT)) + { // match + RemoveEffect(oRider,eEffect); + bMatched=TRUE; + } // match + eEffect=GetNextEffect(oRider); + } // traverse effects + DeleteLocalInt(oRider,"nX3_MountedArchery"); + } // no penalty needed - remove it +} // HORSE_SupportAdjustMountedArcheryPenalty() + + +void HORSE_SupportApplyMountedSkillDecreases(object oRider) +{ // PURPOSE: Applies decreases to skills while mounted + if (GetLocalInt(GetModule(),"X3_HORSE_DISABLE_SKILL")) return; + object oMod=GetModule(); + effect eDisarmTrap = SupernaturalEffect(EffectSkillDecrease(SKILL_DISABLE_TRAP , 50)); + effect eOpenLock = SupernaturalEffect(EffectSkillDecrease(SKILL_OPEN_LOCK , 50)); + effect eHide = SupernaturalEffect(EffectSkillDecrease(SKILL_HIDE , 50)); + effect eMove = SupernaturalEffect(EffectSkillDecrease(SKILL_MOVE_SILENTLY, 50)); + effect ePickPocket = SupernaturalEffect(EffectSkillDecrease(SKILL_PICK_POCKET , 50)); + effect eSetTrap = SupernaturalEffect(EffectSkillDecrease(SKILL_SET_TRAP , 50)); + effect eTumble = SupernaturalEffect(EffectSkillDecrease(SKILL_TUMBLE , 50)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,eDisarmTrap,oRider)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,eOpenLock,oRider)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,eHide,oRider)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,eMove,oRider)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,ePickPocket,oRider)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,eSetTrap,oRider)); + AssignCommand(oMod,ApplyEffectToObject(DURATION_TYPE_PERMANENT,eTumble,oRider)); +} // HORSE_SupportApplyMountedSkillDecreases() + + +void HORSE_SupportRemoveMountedSkillDecreases(object oRider) +{ // PURPOSE: To remove any negative effects caused by being mounted + if (GetLocalInt(GetModule(),"X3_HORSE_DISABLE_SKILL")) return; + effect ePenalty=GetFirstEffect(oRider); + while (GetIsEffectValid(ePenalty)) + { // traverse effects + if ((GetEffectType(ePenalty)==EFFECT_TYPE_SKILL_DECREASE)&& + (GetEffectSubType(ePenalty)==SUBTYPE_SUPERNATURAL)&& + (GetEffectDurationType(ePenalty)==DURATION_TYPE_PERMANENT)) + { // match + RemoveEffect(oRider, ePenalty); + } // match + ePenalty=GetNextEffect(oRider); + } // traverse effects +} // HORSE_SupportRemoveMountedSkillDecreases() + + +void HORSE_SupportResetUnmountedAppearance(object oRider) +{ // PURPOSE: Reset oRider to an unmounted appearance + int nApp=GetSkinInt(oRider,"nX3_HorseRiderAppearance"); + int nPheno=GetSkinInt(oRider,"nX3_HorseRiderPhenotype"); + int nFoot=GetSkinInt(oRider,"nX3_StoredFootstep"); + int nTail=GetSkinInt(oRider,"nX3_HorseRiderTail"); + SetCreatureTailType(nTail,oRider); + SetPhenoType(nPheno,oRider); + SetCreatureAppearanceType(oRider,nApp); + SetFootstepType(nFoot,oRider); +} // HORSE_SupportResetUnmountedAppearance() + + +int HORSE_SupportAbsoluteMinute() +{ // PURPOSE: Returns the current absolute time expressed in minutes + int nTime=GetTimeMinute()+GetTimeHour()*60+GetCalendarDay()*60*24; + int nYear=GetCalendarYear(); + if (nYear>20) nYear=nYear%20; + nTime=nTime+GetCalendarMonth()*60*24*30+nYear*60*24*30*12; + return nTime; +} // HORSE_SupportAbsoluteMinute() + + +void HORSE_SupportMonitorPaladinUnsummon(object oPaladin) +{ // PURPOSE: Monitor whether to unsummon the paladin mount + // see hook scripts x3_s2_palmount and x3_s2_paldmount + object oMount=GetLocalObject(oPaladin,"oX3_PALADIN_MOUNT"); + int nTime; + if (GetIsObjectValid(oPaladin)&&oMount==oPaladin) + { // valid + if (GetIsObjectValid(oMount)) + { // paladin mount exists + SetLocalObject(oPaladin,"oX3_PALADIN_MOUNT",oMount); + nTime=HORSE_SupportAbsoluteMinute(); + if (nTime>=GetLocalInt(oPaladin,"nX3_PALADIN_UNSUMMON")&&!GetCutsceneMode(oPaladin)) + { // unsummon as long as not in a cutscene + AssignCommand(oPaladin,HorseUnsummonPaladinMount()); + } // unsummon as long as not in a cutscene + DelayCommand(10.0,HORSE_SupportMonitorPaladinUnsummon(oPaladin)); + } // paladin mount exists + } // valid +} // HORSE_SupportMonitorPaladinUnsummon() + + +void HORSE_SupportApplyACBonus(object oRider,object oHorse) +{ // PURPOSE: Apply AC bonus + if (!GetLocalInt(GetModule(),"X3_HORSE_ENABLE_ACBOOST")) return; + effect eEffect; + int nRiderAC=GetAC(oRider); + int nHorseAC=GetAC(oHorse); + int nDiff=nHorseAC-nRiderAC; + if (nDiff<1) return; + eEffect=SupernaturalEffect(EffectACIncrease(nDiff,AC_NATURAL_BONUS)); + AssignCommand(GetModule(),ApplyEffectToObject(DURATION_TYPE_PERMANENT,eEffect,oRider)); +} // HORSE_SupportApplyACBonus() + + +void HORSE_SupportRemoveACBonus(object oRider) +{ // PURPOSE: Remove AC Bonus + if (!GetLocalInt(GetModule(),"X3_HORSE_ENABLE_ACBOOST")) return; + effect eEffect; + eEffect=GetFirstEffect(oRider); + while(GetIsEffectValid(eEffect)) + { // traverse effects + if ((GetEffectType(eEffect)==EFFECT_TYPE_AC_INCREASE)&& + (GetEffectSubType(eEffect)==SUBTYPE_SUPERNATURAL)&& + (GetEffectDurationType(eEffect)==DURATION_TYPE_PERMANENT)) + { // remove + RemoveEffect(oRider,eEffect); + return; + } // remove + eEffect=GetNextEffect(oRider); + } // traverse effects +} // HORSE_SupportRemoveACBonus() + + +void HORSE_SupportApplyHPBonus(object oRider,object oHorse) +{ // PURPOSE: Apply HP bonus + if (!GetLocalInt(GetModule(),"X3_HORSE_ENABLE_HPBOOST")) return; + effect eEffect; + int nHP=GetCurrentHitPoints(oHorse); + nHP=nHP/2; + if (nHP<1) nHP=1; + eEffect=SupernaturalEffect(EffectTemporaryHitpoints(nHP)); + AssignCommand(GetModule(),ApplyEffectToObject(DURATION_TYPE_PERMANENT,eEffect,oRider)); +} // HORSE_SupportApplyHPBonus() + + +void HORSE_SupportRemoveHPBonus(object oRider) +{ // PURPOSE: Remove HP Bonus + if (!GetLocalInt(GetModule(),"X3_HORSE_ENABLE_HPBOOST")) return; + effect eEffect; + eEffect=GetFirstEffect(oRider); + while(GetIsEffectValid(eEffect)) + { // traverse effects + if ((GetEffectType(eEffect)==EFFECT_TYPE_TEMPORARY_HITPOINTS)&& + (GetEffectSubType(eEffect)==SUBTYPE_SUPERNATURAL)&& + (GetEffectDurationType(eEffect)==DURATION_TYPE_PERMANENT)) + { // remove + RemoveEffect(oRider,eEffect); + return; + } // remove + eEffect=GetNextEffect(oRider); + } // traverse effects +} // HORSE_SupportRemoveHPBonus() + + +void HORSE_SupportHandleDamage(object oRider,object oHorse) +{ // PURPOSE: Handle resetting damage to the horse back to its original setting + // and also support damage sharing with the horse if that feature is enabled. + int nRiderCHP=GetCurrentHitPoints(oRider); + int nHorseCHP=GetCurrentHitPoints(oHorse); + int nRiderSHP=GetLocalInt(oRider,"nX3_RiderHP"); + int nHorseSHP=GetSkinInt(oRider,"nX3_HorseHP"); + int nDamage; + effect eDamage; + effect eHeal; + effect ePCDamage; + int nAmount; + if (!GetIsObjectValid(oRider)||!GetIsObjectValid(oHorse)) return; + if (nRiderCHPnHorseSHP&&nHorseSHP!=0) + { // restore horse damage + eDamage=EffectDamage(nHorseCHP-nHorseSHP); + nHorseCHP=nHorseSHP; + AssignCommand(GetModule(),ApplyEffectToObject(DURATION_TYPE_INSTANT,eDamage,oHorse)); + } // restore horse damage + if (nDamage>0) + { // damage was received while mounted + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DAMAGE")) + { // share damage with the mount + nAmount=nDamage/2; + if (nAmount>(nHorseCHP-1)) nAmount=nHorseCHP-1; + eHeal=EffectHeal(nAmount); + eDamage=EffectDamage(nAmount); + ePCDamage=EffectDamage(nDamage-nAmount); + if (GetCurrentHitPoints(oRider)>(nRiderSHP-(nDamage-nAmount))) AssignCommand(GetModule(),ApplyEffectToObject(DURATION_TYPE_INSTANT,ePCDamage,oRider)); + else if (GetCurrentHitPoints(oRider)<(nRiderSHP-(nDamage-nAmount))) AssignCommand(GetModule(),ApplyEffectToObject(DURATION_TYPE_INSTANT,eHeal,oRider)); + AssignCommand(GetModule(),ApplyEffectToObject(DURATION_TYPE_INSTANT,eDamage,oHorse)); + } // share damage with the mount + } // damage was received while mounted +} // HORSE_SupportHandleDamage() + + +void KillTheHorse(object oHorse,int bStart=TRUE) +{ // PURPOSE: To Kill the horse + effect eDeath=EffectDeath(); + object oItem=GetItemInSlot(INVENTORY_SLOT_CARMOUR,oHorse); + //SendMessageToPC(GetFirstPC(),"KillTheHorse("+GetName(oHorse)+","+IntToString(bStart)+")"); + if (GetBaseItemType(oItem)==BASE_ITEM_CREATUREITEM&&bStart) + { // destroy + //SendMessageToPC(GetFirstPC(),"Destroy CARMOUR '"+GetName(oHorse)+"'"); + SetPlotFlag(oItem,FALSE); + SetDroppableFlag(oItem,FALSE); + DestroyObject(oItem); + } // destroy + if (bStart) oItem=GetFirstItemInInventory(oHorse); + else { oItem=GetNextItemInInventory(oHorse); } + if (GetIsObjectValid(oItem)) + { // valid + //SendMessageToPC(GetFirstPC(),"Item '"+GetName(oItem)+"'"); + if (GetBaseItemType(oItem)==BASE_ITEM_CREATUREITEM) + { // destroy + //SendMessageToPC(GetFirstPC()," Destroy"); + SetPlotFlag(oItem,FALSE); + SetDroppableFlag(oItem,FALSE); + DestroyObject(oItem,1.0); + } // destroy + DelayCommand(0.03,KillTheHorse(oHorse,FALSE)); + } // valid + else + { // kill + //SendMessageToPC(GetFirstPC(),"Kill '"+GetName(oHorse)+"'"); + DelayCommand(1.0,ApplyEffectToObject(DURATION_TYPE_INSTANT,eDeath,oHorse)); + } // kill +} // KillTheHorse() + + +void HORSE_SupportTransferInventory(object oFrom,object oTo,location lLoc,int bDestroyFrom=FALSE,int bFirst=TRUE) +{ // PURPOSE: Delay Command transfer to handle moving inventory + object oItem; + object oCopy; + if (bFirst) oItem=GetFirstItemInInventory(oFrom); + else { oItem=GetNextItemInInventory(oFrom); } + if (GetIsObjectValid(oItem)) + { // item found + if (GetIsObjectValid(oTo)) + { // to object + if (GetBaseItemType(oItem)!=BASE_ITEM_CREATUREITEM) + { // no pchide transfers + oCopy=CopyItem(oItem,oTo,TRUE); + } // no pchide transfers + } // to object + else + { // to location + if (GetBaseItemType(oItem)!=BASE_ITEM_CREATUREITEM) + { // no pchide transfers + oCopy=CopyObject(oItem,lLoc); + if (GetItemStackSize(oCopy)!=GetItemStackSize(oItem)) SetItemStackSize(oCopy,GetItemStackSize(oItem)); + if (GetItemCharges(oCopy)!=GetItemCharges(oItem)) SetItemCharges(oCopy,GetItemCharges(oItem)); + } // no pchide transfers + } // to location + DestroyObject(oItem,2.0); + DelayCommand(0.02,HORSE_SupportTransferInventory(oFrom,oTo,lLoc,bDestroyFrom,FALSE)); + } // item found + else + { // transfer gold + if (GetIsObjectValid(oTo)) + { // to object + if (GetGold(oFrom)>0) oCopy=CreateItemOnObject("nw_it_gold001",oTo,GetGold(oFrom)); + } // to object + else + { // to location + if (GetGold(oFrom)>0) oCopy=CreateObject(OBJECT_TYPE_ITEM,"nw_it_gold001",lLoc,GetGold(oFrom)); + } // to location + if (bDestroyFrom) + { // destroy + DestroyObject(oFrom); + } // destroy + if (GetLocalInt(oTo,"bDie")) + { // death effect + oItem=GetItemInSlot(INVENTORY_SLOT_CARMOUR,oTo); + if (GetBaseItemType(oItem)==BASE_ITEM_CREATUREITEM) + { // destroy + SetDroppableFlag(oItem,FALSE); + SetPlotFlag(oItem,FALSE); + DestroyObject(oItem); + } // destroy + DelayCommand(0.1,KillTheHorse(oTo)); + } // death effect + } // transfer gold +} // HORSE_SupportTransferInventory() + + +int HORSE_SupportCountHenchmen(object oPC) +{ // PURPOSE: Return the number of henchmen + int nC=0; + object oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nC+1); + while(GetIsObjectValid(oHench)) + { // count + nC++; + oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nC+1); + } // count + return nC; +} // HORSE_SupportCountHenchmen() + + +void HORSE_SupportTransferPreservedValues(object oRider,object oHorse) +{ // PURPOSE: This will transfer preserved values for the mounted horse to + // oHorse from oRider and then will remove them from oRider + string sS; + float fX3_MOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_MOUNT_MULTIPLE"); + float fX3_DISMOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_DISMOUNT_MULTIPLE"); + if (GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE")>fX3_MOUNT_MULTIPLE) fX3_MOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE"); + if (fX3_MOUNT_MULTIPLE<=0.0) fX3_MOUNT_MULTIPLE=1.0; + if (GetLocalFloat(oRider,"fX3_DISMOUNT_MULTIPLE")>0.0) fX3_DISMOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_DISMOUNT_MULTIPLE"); + if (fX3_DISMOUNT_MULTIPLE>0.0) fX3_MOUNT_MULTIPLE=fX3_DISMOUNT_MULTIPLE; // use dismount multiple instead of mount multiple + if (GetIsObjectValid(oHorse)) + { // transfer values to horse + + } // transfer values to horse + sS=GetSkinString(oRider,"X3_HORSE_POSTDISMOUNT_SCRIPT"); + if (GetStringLength(sS)>0) + { // run post dismount script + SetLocalObject(oRider,"oX3_TempHorse",oHorse); + ExecuteScript(sS,oRider); + DeleteLocalObject(oRider,"oX3_TempHorse"); + } // run post dismount script + // delete variables + if (GetLocalInt(oRider,"bX3_HORSE_MODIFIERS")&&GetStringLength(sS)<1) + { // remove horse modifiers + DeleteLocalInt(oRider,"bX3_HORSE_MODIFIERS"); + DelayCommand(0.10*fX3_MOUNT_MULTIPLE,HORSE_SupportOriginalSpeed(oRider)); + DelayCommand(0.30*fX3_MOUNT_MULTIPLE,HORSE_SupportAdjustMountedArcheryPenalty(oRider)); + DelayCommand(0.15*fX3_MOUNT_MULTIPLE,HORSE_SupportRemoveMountedSkillDecreases(oRider)); + DelayCommand(0.25*fX3_MOUNT_MULTIPLE,HORSE_SupportRemoveACBonus(oRider)); + DelayCommand(0.25*fX3_MOUNT_MULTIPLE,HORSE_SupportRemoveHPBonus(oRider)); + DelayCommand(0.20*fX3_MOUNT_MULTIPLE,HORSE_SupportHandleDamage(oRider,oHorse)); + } // remove horse modifiers + //HORSE_SupportMountCleanVariables(oRider); +} // HORSE_SupportTransferPreservedValues() + + +void HORSE_SupportDismountWrapper(int bAnimate,int bSetOwner) +{ // Wrap + object oHorse=HorseDismount(bAnimate,bSetOwner); + object oNPC=OBJECT_SELF; + if (bSetOwner) SetLocalObject(oNPC,"oAssignedHorse",oHorse); +} // HORSE_SupportDismountWrapper() + + +void HORSE_SupportRestoreFromPreload(object oPC,int nApp,int nTail) +{ // PURPOSE: Restore to previous appearance before preload + SetCreatureAppearanceType(oPC,nApp); + SetCreatureTailType(nTail,oPC); +} // HORSE_SupportRestoreFromPreload() + + +void HORSE_SupportSetMountingSentinel(object oRider,object oHorse,float fTimeout=6.0, int nCount=0) +{ // PURPOSE: This will set a mounting process that will make sure the PC or NPC + // is returned to commandable. There may be a slight delay but, this will + // insure recovery from aborted mounts that could result in PC being stuck as + // not commandable. + if (!HorseGetIsMounted(oRider)&&!IsInConversation(oRider)&&!GetIsInCombat(oRider)&&!HorseGetIsDisabled(oRider)) + { // keep waiting + float fFreq=1.0; // frequency in seconds + if (nCount0) return TRUE; + return FALSE; +} // HORSE_SupportGetHenchmanExistsInDatabase() + + +void HORSE_SupportRestoreHenchmanFromDatabase(object oPC,string sDatabase,int nN=1) +{ // PURPOSE: This will reload and assign the henchman to the PC + object oHenchman=OBJECT_INVALID; + string sN=IntToString(nN); + string sTag=GetCampaignString(sDatabase,"sX3_HTAG_"+sN,oPC); + string sResRef=GetCampaignString(sDatabase,"sX3_HRR_"+sN,oPC); + int nNN=1; + object oOb; + oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nNN); + while(!GetIsObjectValid(oHenchman)&&GetIsObjectValid(oOb)) + { // see if henchman is already in play + if (sResRef==GetResRef(oOb)&&GetTag(oOb)==sTag) oHenchman=oOb; + nNN++; + oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nNN); + } // see if henchman is already in play + if (!GetIsObjectValid(oHenchman)) + { // create henchman + oHenchman=CreateObject(OBJECT_TYPE_CREATURE,sResRef,GetLocation(oPC),FALSE,sTag); + AssignCommand(oHenchman,SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE)); + DelayCommand(1.0,AddHenchman(oPC,oHenchman)); + } // create henchman + // load and set henchman + SetCreatureAppearanceType(oHenchman,GetCampaignInt(sDatabase,"nX3_CAPP_"+sN,oPC)); + SetPhenoType(GetCampaignInt(sDatabase,"nX3_CPHE_"+sN,oPC),oHenchman); + SetCreatureTailType(GetCampaignInt(sDatabase,"nX3_CTAIL_"+sN,oPC),oHenchman); + SetSkinString(oHenchman,"sX3_HorseResRef",GetCampaignString(sDatabase,"sX3_RR_"+sN,oPC)); + SetSkinString(oHenchman,"sX3_HorseMountTag",GetCampaignString(sDatabase,"sX3_TAG_"+sN,oPC)); + SetLocalString(oHenchman,"sX3_HorseMountScript",GetCampaignString(sDatabase,"sX3_SCR_"+sN,oPC)); + SetLocalString(oHenchman,"X3_HORSE_PREMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPRM_"+sN,oPC)); + SetLocalString(oHenchman,"X3_HORSE_POSTMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPOM_"+sN,oPC)); + SetSkinString(oHenchman,"X3_HORSE_PREDISMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPRDM_"+sN,oPC)); + SetSkinString(oHenchman,"X3_HORSE_POSTDISMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPODM_"+sN,oPC)); + //SetSkinInt(oHenchman,"X3_HORSE_APPEARANCE",GetCampaignInt(sDatabase,"nX3_HAP_"+sN,oPC)); + SetLocalInt(oHenchman,"X3_HORSE_TAIL",GetCampaignInt(sDatabase,"nX3_HTL_"+sN,oPC)); + SetLocalInt(oHenchman,"X3_HORSE_NULL_APPEARANCE",GetCampaignInt(sDatabase,"nX3_HNAP_"+sN,oPC)); + SetSkinInt(oHenchman,"nX3_HorseRiderAppearance",GetCampaignInt(sDatabase,"nX3_APP_"+sN,oPC)); + SetSkinInt(oHenchman,"nX3_HorseRiderPhenotype",GetCampaignInt(sDatabase,"nX3_PHEN_"+sN,oPC)); + SetSkinInt(oHenchman,"nX3_StoredFootstep",GetCampaignInt(sDatabase,"nX3_FOOT_"+sN,oPC)); + SetLocalInt(oHenchman,"X3_NO_MOUNT_ANIMATE",GetCampaignInt(sDatabase,"nX3_NOAN_"+sN,oPC)); + SetLocalInt(oHenchman,"nX3_HorsePortrait",GetCampaignInt(sDatabase,"nX3_HPOR_"+sN,oPC)); + SetSkinInt(oHenchman,"nX3_HorseHP",GetCampaignInt(sDatabase,"nX3_HHP_"+sN,oPC)); + SetLocalInt(oHenchman,"nX3_RiderHP",GetCampaignInt(sDatabase,"nX3_RHP_"+sN,oPC)); + SetSkinInt(oHenchman,"bX3_IS_MOUNTED",GetCampaignInt(sDatabase,"bX3_ISM_"+sN,oPC)); + SetSkinInt(oHenchman,"nX3_HorseRiderTail",GetCampaignInt(sDatabase,"nX3_TAL_"+sN,oPC)); + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")) + { // saddlebags enabled + SetLocalInt(oHenchman,"bX3_HAS_SADDLEBAGS",GetCampaignInt(sDatabase,"bX3_HSB_"+sN,oPC)); + } // saddlebags enabled +} // HORSE_SupportRestoreHenchmanFromDatabase() + + +void HORSE_SupportDeleteMountedPCFromDatabase(object oPC,string sDatabase) +{ // PURPOSE: This will remove the info about this PC being mounted + DeleteCampaignVariable(sDatabase,"sX3_RR",oPC); + DeleteCampaignVariable(sDatabase,"sX3_TAG",oPC); + DeleteCampaignVariable(sDatabase,"sX3_SCR",oPC); + DeleteCampaignVariable(sDatabase,"nX3_HAP",oPC); + DeleteCampaignVariable(sDatabase,"nX3_HTL",oPC); + DeleteCampaignVariable(sDatabase,"nX3_HNAP",oPC); + DeleteCampaignVariable(sDatabase,"nX3_HAP",oPC); + DeleteCampaignVariable(sDatabase,"nX3_APP",oPC); + DeleteCampaignVariable(sDatabase,"nX3_PHEN",oPC); + DeleteCampaignVariable(sDatabase,"nX3_FOOT",oPC); + DeleteCampaignVariable(sDatabase,"nX3_NOAN",oPC); + DeleteCampaignVariable(sDatabase,"nX3_CAPP",oPC); + DeleteCampaignVariable(sDatabase,"nX3_CPHE",oPC); + DeleteCampaignVariable(sDatabase,"nX3_CTAIL",oPC); + DeleteCampaignVariable(sDatabase,"sX3_SPRM",oPC); + DeleteCampaignVariable(sDatabase,"sX3_SPOM",oPC); + DeleteCampaignVariable(sDatabase,"sX3_SPRDM",oPC); + DeleteCampaignVariable(sDatabase,"sX3_SPODM",oPC); + DeleteCampaignVariable(sDatabase,"bX3_MOUNTED",oPC); + DeleteCampaignVariable(sDatabase,"nX3_PALUS",oPC); + DeleteCampaignVariable(sDatabase,"nX3_HPOR",oPC); + DeleteCampaignVariable(sDatabase,"nX3_HHP",oPC); + DeleteCampaignVariable(sDatabase,"nX3_RHP",oPC); + DeleteCampaignVariable(sDatabase,"bX3_ISM",oPC); + DeleteCampaignVariable(sDatabase,"nX3_TAL",oPC); + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")) + { // saddlebags enabled + DeleteCampaignVariable(sDatabase,"bX3_HSB",oPC); + } // saddlebags enabled +} // HORSE_SupportDeleteMountedPCFromDatabase() + + +void HORSE_SupportStoreMountedPCInDatabase(object oPC,string sDatabase) +{ // PURPOSE: This will store the PCs information about being mounted + SetCampaignString(sDatabase,"sX3_RR",GetSkinString(oPC,"sX3_HorseResRef"),oPC); + SetCampaignString(sDatabase,"sX3_TAG",GetSkinString(oPC,"sX3_HorseMountTag"),oPC); + SetCampaignString(sDatabase,"sX3_SCR",GetLocalString(oPC,"sX3_HorseMountScript"),oPC); + //SetCampaignInt(sDatabase,"nX3_HAP",GetSkinInt(oPC,"X3_HORSE_APPEARANCE"),oPC); + SetCampaignInt(sDatabase,"nX3_HTL",GetLocalInt(oPC,"X3_HORSE_TAIL"),oPC); + SetCampaignInt(sDatabase,"nX3_HNAP",GetLocalInt(oPC,"X3_HORSE_NULL_APPEARANCE"),oPC); + SetCampaignInt(sDatabase,"nX3_APP",GetSkinInt(oPC,"nX3_HorseRiderAppearance"),oPC); + SetCampaignInt(sDatabase,"nX3_PHEN",GetSkinInt(oPC,"nX3_HorseRiderPhenotype"),oPC); + SetCampaignInt(sDatabase,"nX3_FOOT",GetSkinInt(oPC,"nX3_StoredFootstep"),oPC); + SetCampaignInt(sDatabase,"nX3_NOAN",GetLocalInt(oPC,"X3_NO_MOUNT_ANIMATE"),oPC); + SetCampaignInt(sDatabase,"nX3_CAPP",GetAppearanceType(oPC),oPC); + SetCampaignInt(sDatabase,"nX3_CPHE",GetPhenoType(oPC),oPC); + SetCampaignInt(sDatabase,"nX3_CTAIL",GetCreatureTailType(oPC),oPC); + SetCampaignString(sDatabase,"sX3_SPRM",GetLocalString(oPC,"X3_HORSE_PREMOUNT_SCRIPT"),oPC); + SetCampaignString(sDatabase,"sX3_SPOM",GetLocalString(oPC,"X3_HORSE_POSTMOUNT_SCRIPT"),oPC); + SetCampaignString(sDatabase,"sX3_SPRDM",GetSkinString(oPC,"X3_HORSE_PREDISMOUNT_SCRIPT"),oPC); + SetCampaignString(sDatabase,"sX3_SPODM",GetSkinString(oPC,"X3_HORSE_POSTDISMOUNT_SCRIPT"),oPC); + SetCampaignInt(sDatabase,"nX3_HPOR",GetLocalInt(oPC,"nX3_HorsePortrait"),oPC); + SetCampaignInt(sDatabase,"nX3_RHP",GetLocalInt(oPC,"nX3_RiderHP"),oPC); + SetCampaignInt(sDatabase,"nX3_HHP",GetSkinInt(oPC,"nX3_HorseHP"),oPC); + SetCampaignInt(sDatabase,"bX3_MOUNTED",TRUE,oPC); + SetCampaignInt(sDatabase,"bX3_ISM",GetSkinInt(oPC,"bX3_IS_MOUNTED"),oPC); + SetCampaignInt(sDatabase,"nX3_PALUS",GetLocalInt(oPC,"nX3_PALADIN_UNSUMMON"),oPC); + SetCampaignInt(sDatabase,"nX3_TAL",GetSkinInt(oPC,"nX3_HorseRiderTail"),oPC); + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")) + { // saddlebags enabled + SetCampaignInt(sDatabase,"bX3_HSB",GetLocalInt(oPC,"bX3_HAS_SADDLEBAGS"),oPC); + } // saddlebags enabled +} // HORSE_SupportStoreMountedPCInDatabase() + + +void HORSE_SupportReloadMountedPCFromDatabase(object oPC,string sDatabase) +{ // PURPOSE: This will restore the mounted information about the PC + SetCreatureAppearanceType(oPC,GetCampaignInt(sDatabase,"nX3_CAPP",oPC)); + SetPhenoType(GetCampaignInt(sDatabase,"nX3_CPHE",oPC),oPC); + SetCreatureTailType(GetCampaignInt(sDatabase,"nX3_CTAIL",oPC),oPC); + SetSkinString(oPC,"sX3_HorseResRef",GetCampaignString(sDatabase,"sX3_RR",oPC)); + SetSkinString(oPC,"sX3_HorseMountTag",GetCampaignString(sDatabase,"sX3_TAG",oPC)); + SetLocalString(oPC,"sX3_HorseMountScript",GetCampaignString(sDatabase,"sX3_SCR",oPC)); + SetLocalString(oPC,"X3_HORSE_PREMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPRM",oPC)); + SetLocalString(oPC,"X3_HORSE_POSTMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPOM",oPC)); + SetSkinString(oPC,"X3_HORSE_PREDISMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPRDM",oPC)); + SetSkinString(oPC,"X3_HORSE_POSTDISMOUNT_SCRIPT",GetCampaignString(sDatabase,"sX3_SPODM",oPC)); + //SetSkinInt(oPC,"X3_HORSE_APPEARANCE",GetCampaignInt(sDatabase,"nX3_HAP",oPC)); + SetLocalInt(oPC,"X3_HORSE_TAIL",GetCampaignInt(sDatabase,"nX3_HTL",oPC)); + SetLocalInt(oPC,"X3_HORSE_NULL_APPEARANCE",GetCampaignInt(sDatabase,"nX3_HNAP",oPC)); + SetSkinInt(oPC,"nX3_HorseRiderAppearance",GetCampaignInt(sDatabase,"nX3_APP",oPC)); + SetSkinInt(oPC,"nX3_HorseRiderPhenotype",GetCampaignInt(sDatabase,"nX3_PHEN",oPC)); + SetSkinInt(oPC,"nX3_StoredFootstep",GetCampaignInt(sDatabase,"nX3_FOOT",oPC)); + SetLocalInt(oPC,"X3_NO_MOUNT_ANIMATE",GetCampaignInt(sDatabase,"nX3_NOAN",oPC)); + SetLocalInt(oPC,"nX3_PALADIN_UNSUMMON",GetCampaignInt(sDatabase,"nX3_PALUS",oPC)); + SetLocalInt(oPC,"nX3_HorsePortrait",GetCampaignInt(sDatabase,"nX3_HPOR",oPC)); + SetLocalInt(oPC,"nX3_RiderHP",GetCampaignInt(sDatabase,"nX3_RHP",oPC)); + SetSkinInt(oPC,"nX3_HorseHP",GetCampaignInt(sDatabase,"nX3_HHP",oPC)); + SetSkinInt(oPC,"bX3_IS_MOUNTED",GetCampaignInt(sDatabase,"bX3_ISM",oPC)); + SetSkinInt(oPC,"nX3_HorseRiderTail",GetCampaignInt(sDatabase,"nX3_TAL",oPC)); + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")) + { // saddlebags enabled + SetLocalInt(oPC,"bX3_HAS_SADDLEBAGS",GetCampaignInt(sDatabase,"bX3_HSB",oPC)); + } // saddlebags enabled + if (HorseGetIsMounted(oPC)&&GetStringLeft(GetSkinString(oPC,"sX3_HorseResRef"),GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) AssignCommand(oPC,HORSE_SupportMonitorPaladinUnsummon(oPC)); +} // HORSE_SupportReloadMountedPCFromDatabase() + + +//////////////////////////////////////////////////////////////////////////////// +// FUNCTIONS +//----------------------------- +// This is where the actual functions defined in the prototype section are +// completed. +//////////////////////////////////////////////////////////////////////////////// + + +void HorseReloadFromDatabase(object oPC,string sDatabase) +{ // PURPOSE: Reload status from database and set it the same + int nN; + // Reload settings for oPC + HORSE_SupportReloadMountedPCFromDatabase(oPC,sDatabase); + nN=1; + while(HORSE_SupportGetHenchmanExistsInDatabase(oPC,sDatabase,nN)) + { // restore henchmen + HORSE_SupportRestoreHenchmanFromDatabase(oPC,sDatabase,nN); + nN++; + } // restore henchmen +} // HorseReloadFromDatabase() + + +void HorseSaveToDatabase(object oPC,string sDatabase) +{ // PURPOSE: Save Status to Database + int nN=1; + int nMax; + object oHenchman; + if (HorseGetIsMounted(oPC)) + { // Store mounted info + HORSE_SupportStoreMountedPCInDatabase(oPC,sDatabase); + } // Store mounted info + else + { // Delete mounted info + HORSE_SupportDeleteMountedPCFromDatabase(oPC,sDatabase); + } // Delete mounted info + while(HORSE_SupportGetHenchmanExistsInDatabase(oPC,sDatabase,nN)) + { // count + nMax++; + nN++; + } // count + nN=1; + while(nN<=nMax) + { // remove old + HORSE_SupportDeleteFromDatabase(oPC,sDatabase,nN); + nN++; + } // remove old + nN=1; + oHenchman=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nN); + while(GetIsObjectValid(oHenchman)) + { // traverse henchmen + HORSE_SupportSaveToDatabase(oPC,oHenchman,sDatabase,nN); + nN++; + oHenchman=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nN); + } // traverse henchmen +} // HorseSaveToDatabase() + + +object HorseCreateHorse(string sResRef,location lLoc,object oOwner=OBJECT_INVALID,string sTag="",int nAppearance=-1,int nTail=-1,int nFootstep=-1,string sScript="") +{ // PURPOSE: Spawn a horse with the specified information + object oHorse=OBJECT_INVALID; + int nHenchmen=HORSE_SupportCountHenchmen(oOwner); + int nMax=GetLocalInt(GetModule(),"X3_HORSE_MAX_HENCHMEN"); + int bIncHenchmen=GetLocalInt(GetModule(),"X3_HORSE_NO_HENCHMAN_INCREASE"); + int nCurr=GetMaxHenchmen(); + if (GetStringLength(sResRef)>0) + { // resref specified + oHorse=CreateObject(OBJECT_TYPE_CREATURE,sResRef,lLoc,FALSE,sTag); + if (GetIsObjectValid(oHorse)) + { // horse successfully created + SetLocalInt(oHorse,"bX3_IS_MOUNT",TRUE); + SetLocalString(oHorse,"sX3_OriginalName",GetName(oHorse)); + if (GetIsObjectValid(oOwner)&&GetObjectType(oOwner)==OBJECT_TYPE_CREATURE) + { // assign owner + SetLocalObject(oHorse,"oX3_HorseOwner",oOwner); + if (nHenchmen==nCurr) + { // see if increase possible + if (!bIncHenchmen) + { // increase is permissable + if (nMax==0||nMax>0) + { // do the increase + SetMaxHenchmen(nCurr+1); + } // do the increase + } // increase is permissable + } // see if increase possible + AddHenchman(oOwner,oHorse); + //AssignCommand(oHorse,SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE)); + SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE,oHorse); + if (GetMaster(oHorse)==oOwner) SetName(oHorse,GetName(oOwner)+"'s "+GetName(oHorse)); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oOwner)) SetLocalInt(oOwner,"bX3_STORE_MOUNT_INFO",TRUE); + } // assign owner + if (nAppearance>-1) + { // set appearance + SetCreatureAppearanceType(oHorse,nAppearance); + } // set appearance + if (nTail>-1) + { // set tail + SetCreatureTailType(nTail,oHorse); + } // set tail + if (nFootstep>-1) + { // footstep + SetFootstepType(nFootstep,oHorse); + } // footstep + if (GetStringLength(sScript)>0) + { // post spawn script + SetLocalString(oHorse,"sX3_HORSE_CREATED_SCRIPT",sScript); + ExecuteScript(sScript,oHorse); + } // post spawn script + } // horse successfully created + } // resref specified + return oHorse; +} // HorseCreateHorse() + + +int HorseGetIsMounted(object oTarget) +{ // PURPOSE: Return whether oTarget is mounted + if (GetObjectType(oTarget)==OBJECT_TYPE_CREATURE) + { // valid parameter + if (GetSkinInt(oTarget,"bX3_IS_MOUNTED")) return TRUE; + } // valid parameter + return FALSE; +} // HorseGetIsMounted() + + +int HorseGetCanBeMounted(object oTarget,object oRider=OBJECT_INVALID,int bAssignMount=FALSE) +{ // PURPOSE: This will return whether oTarget can be mounted + int nAppearance; + string sS; + object oOb; + if (GetObjectType(oTarget)==OBJECT_TYPE_CREATURE) + { // valid oTarget type + if (GetIsDead(oTarget)) return FALSE; + if (GetIsPC(oTarget)) return FALSE; + if (GetIsObjectValid(oRider)&&GetObjectType(oRider)!=OBJECT_TYPE_CREATURE) return FALSE; + if (GetIsObjectValid(oRider)&&GetIsEnemy(oRider,oTarget)) return FALSE; + if (GetIsDMPossessed(oTarget)) return FALSE; + if (HorseGetIsMounted(oRider)) return FALSE; + //if (GetIsObjectValid(oRider)&&GetCreatureTailType(oRider)!=CREATURE_TAIL_TYPE_NONE) return FALSE; + nAppearance=GetAppearanceType(oRider); + if (GetIsPC(oRider)&&nAppearance>6&&GetLocalInt(oRider,"X3_CUSTOM_RACE_APPEARANCE")!=nAppearance&&!Horse_SupportRaceAppearance(nAppearance)) return FALSE; // PC is shape shifted + if (GetIsObjectValid(oRider)&&GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // Rider is a creature + sS=HORSE_SupportRaceRestrictString(oRider); + if (GetLocalInt(oTarget,sS)) return FALSE; // race restricted + } // Rider is a creature + oOb=GetMaster(oTarget); + if (GetIsObjectValid(oOb)&&GetIsObjectValid(oRider)) + { // has master make sure is part of party + if (oOb!=oRider&&oOb!=GetMaster(oRider)&&GetMaster(oOb)!=GetMaster(oRider)) + { // not part of party + return FALSE; + } // not part of party + } // has master make sure is part of party + if (GetLocalInt(oTarget,"X3_HORSE_NOT_RIDEABLE_OWNER")) + { // not rideable due to owner + return FALSE; + } // not rideable due to owner + if (GetLocalInt(GetArea(oTarget),"X3_NO_MOUNTING")&&!bAssignMount) + { // no mounting allowed in this area + return FALSE; + } // no mounting allowed in this area + sS=GetResRef(oTarget); + if (GetStringLeft(sS,GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX&&GetIsObjectValid(oRider)) + { // paladin mount + if (HorseGetOwner(oTarget)!=oRider) return FALSE; + } // paladin mount + if (!HorseGetIsAMount(oTarget)) return FALSE; + return TRUE; + } // valid oTarget type + return FALSE; +} // HorseGetCanBeMounted() + + +string HorseGetMountFailureMessage(object oTarget,object oRider=OBJECT_INVALID) +{ // PURPOSE: This will return the error message + int nAppearance; + string sS; + object oOb; + if (GetObjectType(oTarget)==OBJECT_TYPE_CREATURE) + { // valid oTarget type + if (GetIsDead(oTarget)) return StringToRGBString(GetStringByStrRef(111993),STRING_COLOR_ROSE); + if (GetIsPC(oTarget)) return StringToRGBString(GetStringByStrRef(111994),STRING_COLOR_ROSE); + if (GetIsObjectValid(oRider)&&GetObjectType(oRider)!=OBJECT_TYPE_CREATURE) return StringToRGBString(GetStringByStrRef(111995),STRING_COLOR_ROSE); + if (GetIsObjectValid(oRider)&&GetIsEnemy(oRider,oTarget)) return StringToRGBString(GetStringByStrRef(111996),STRING_COLOR_ROSE); + if (GetIsDMPossessed(oTarget)) return StringToRGBString(GetStringByStrRef(111997),STRING_COLOR_ROSE); + if (HorseGetIsMounted(oRider)) return StringToRGBString(GetStringByStrRef(111998),STRING_COLOR_ROSE); + //if (GetIsObjectValid(oRider)&&GetCreatureTailType(oRider)!=CREATURE_TAIL_TYPE_NONE) return StringToRGBString(GetStringByStrRef(111998),STRING_COLOR_ROSE); + nAppearance=GetAppearanceType(oRider); + if (GetIsPC(oRider)&&nAppearance>6&&GetLocalInt(oRider,"X3_CUSTOM_RACE_APPEARANCE")!=nAppearance&&!Horse_SupportRaceAppearance(nAppearance)) return StringToRGBString(GetStringByStrRef(111999),STRING_COLOR_ROSE); + if (GetIsObjectValid(oRider)&&GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // Rider is a creature + sS=HORSE_SupportRaceRestrictString(oRider); + if (GetLocalInt(oTarget,sS)) return StringToRGBString(GetStringByStrRef(112000),STRING_COLOR_PINK); + } // Rider is a creature + oOb=GetMaster(oTarget); + if (GetIsObjectValid(oOb)&&GetIsObjectValid(oRider)) + { // has master make sure is part of party + if (oOb!=oRider&&GetMaster(oRider)!=oOb) + { // not part of party + return StringToRGBString(GetStringByStrRef(112001),STRING_COLOR_ROSE); + } // not part of party + } // has master make sure is part of party + if (GetLocalInt(oTarget,"X3_HORSE_NOT_RIDEABLE_OWNER")) + { // not rideable due to owner + return StringToRGBString(GetStringByStrRef(112002),STRING_COLOR_PINK); + } // not rideable due to owner + if (GetLocalInt(GetArea(oTarget),"X3_NO_MOUNTING")) + { // no mounting allowed in this area + return StringToRGBString(GetStringByStrRef(112003),STRING_COLOR_PINK); + } // no mounting allowed in this area + sS=GetResRef(oTarget); + if (GetStringLeft(sS,GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX&&GetIsObjectValid(oRider)) + { // paladin mount + if (HorseGetOwner(oTarget)!=oRider) return StringToRGBString(GetStringByStrRef(112004),STRING_COLOR_PINK); + } // paladin mount + if (!HorseGetIsAMount(oTarget)) return StringToRGBString(GetStringByStrRef(112005),STRING_COLOR_PINK); + } // valid oTarget type + else + { + // The target is not a mount (as its not a creature). + return StringToRGBString(GetStringByStrRef(112005),STRING_COLOR_ROSE); + } + return ""; +} // HorseGetMountFailureMessage() + + +void HorseSetOwner(object oHorse,object oOwner,int bAssign=FALSE) +{ // PURPOSE: Set oHorse to be owned by oOwner + object oPreviousOwner; + string sName; + int nHenchmen=HORSE_SupportCountHenchmen(oOwner); + int nMax=GetLocalInt(GetModule(),"X3_HORSE_MAX_HENCHMEN"); + int bIncHenchmen=GetLocalInt(GetModule(),"X3_HORSE_NO_HENCHMAN_INCREASE"); + int nCurr=GetMaxHenchmen(); + if (GetObjectType(oHorse)==OBJECT_TYPE_CREATURE&&GetObjectType(oOwner)==OBJECT_TYPE_CREATURE) + { // valid parameters + //oPreviousOwner=GetMaster(oHorse); + //if (oPreviousOwner==oOwner) return; // already is the owner + if ((HorseGetCanBeMounted(oHorse,oOwner,TRUE)||GetIsPC(oOwner))&&!HorseGetIsAMount(oOwner)) + { // horse can be mounted + oPreviousOwner=GetMaster(oHorse); + if (oPreviousOwner!=oOwner) + { // new owner + if (GetObjectType(oPreviousOwner)==OBJECT_TYPE_CREATURE) + { // remove as henchman from previous owner + RemoveHenchman(oPreviousOwner,oHorse); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oPreviousOwner)) SetLocalInt(oPreviousOwner,"bX3_STORE_MOUNT_INFO",TRUE); + } // remove as henchman from previous owner + if (nHenchmen==nCurr) + { // see if increase possible + if (!bIncHenchmen) + { // increase is permissable + if (nMax==0||nMax>0) + { // do the increase + SetMaxHenchmen(nCurr+1); + } // do the increase + } // increase is permissable + } // see if increase possible + AssignCommand(oHorse,ClearAllActions()); + AddHenchman(oOwner,oHorse); + SetLocalObject(oHorse,"oX3_HorseOwner",oOwner); + //AssignCommand(oHorse,SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE)); + DelayCommand(1.0,SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE,oHorse)); + if (bAssign) SetLocalObject(oOwner,"oAssignedHorse",oHorse); + } // new owner + else + { // make sure variables on oHorse are correct + SetLocalObject(oHorse,"oX3_HorseOwner",oOwner); + //AssignCommand(oHorse,SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE)); + SetAssociateState(NW_ASC_DISTANCE_6_METERS,TRUE,oHorse); + if (bAssign) SetLocalObject(oOwner,"oAssignedHorse",oHorse); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oOwner)) SetLocalInt(oOwner,"bX3_STORE_MOUNT_INFO",TRUE); + } // make sure variables on oHorse are correct + sName=GetLocalString(oHorse,"sX3_OriginalName"); + if (GetStringLength(sName)<1) + { // define original name + sName=GetName(oHorse); + SetLocalString(oHorse,"sX3_OriginalName",sName); + } // define original name + if (GetMaster(oHorse)==oOwner) + { // was set okay + if (GetStringLowerCase(GetStringRight(GetName(oOwner),1))=="s"||GetStringLowerCase(GetStringRight(GetName(oOwner),1))=="z") + SetName(oHorse,GetName(oOwner)+"' "+sName); + else { SetName(oHorse,GetName(oOwner)+"'s "+sName); } + } // was set okay + } // horse can be mounted + else + { // not valid + if (GetIsPC(oOwner)) + { // PC + PrintString("X3 HORSE ERROR: Attempt made to set "+GetName(oOwner)+" as owner of "+GetName(oHorse)+" is an invalid assignment."); + } // PC + else + { // error + PrintString("X3 HORSE ERROR: Attempt made to set "+GetName(oOwner)+" as owner of "+GetName(oHorse)+" is an invalid assignment."); + } // error + } // not valid + } // valid parameters +} // HorseSetOwner() + + +void HorseRemoveOwner(object oHorse) +{ // PURPOSE: Remove the owner from oHorse + object oOwner; + string sString; + if (GetObjectType(oHorse)==OBJECT_TYPE_CREATURE) + { // valid parameter + oOwner=GetLocalObject(oHorse,"oX3_HorseOwner"); + if (GetObjectType(oOwner)==OBJECT_TYPE_CREATURE) + { // owner found + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oOwner)) SetLocalInt(oOwner,"bX3_STORE_MOUNT_INFO",TRUE); + sString=GetResRef(oHorse); + // do not allow removing paladin horses from owner + if (GetStringLeft(sString,GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) return; + if (GetMaster(oHorse)==oOwner) RemoveHenchman(oOwner,oHorse); + DeleteLocalObject(oHorse,"oX3_HorseOwner"); + if (oHorse==GetLocalObject(oOwner,"oAssignedHorse")) + { + DeleteLocalObject(oOwner,"oAssignedHorse"); + HORSE_Support_AssignRemainingMount(oOwner); + } + } // owner found + } // valid parameter + sString=GetLocalString(oHorse,"sX3_OriginalName"); + if (GetStringLength(sString)>0) SetName(oHorse,sString); +} // HorseRemoveOwner() + + +object HorseGetOwner(object oHorse) +{ // PURPOSE: Return who the owner of oHorse is or return OBJECT_INVALID + object oOwner; + if (GetObjectType(oHorse)==OBJECT_TYPE_CREATURE) + { // valid parameter + oOwner=GetLocalObject(oHorse,"oX3_HorseOwner"); + if (GetObjectType(oOwner)==OBJECT_TYPE_CREATURE) return oOwner; + } // valid parameter + return OBJECT_INVALID; +} // HorseGetOwner() + + +void HorseAddHorseMenu(object oPC) +{ // PURPOSE: Add Horse Menu to the PC + object oSkin=SKIN_SupportGetSkin(oPC); + itemproperty iProp; + if (GetIsPC(oPC)) + { // valid parameter + iProp=ItemPropertyBonusFeat(IP_CONST_HORSE_MENU); + AddItemProperty(DURATION_TYPE_PERMANENT,iProp,oSkin); + } // valid parameter +} // HorseAddHorseMenu() + + +void HorseSetPhenotype(object oRider,int bJoust=FALSE) +{ // PURPOSE: To set the proper phenotype for oRider when mounted + int nCurrent=GetPhenoType(oRider); + int nPheno=-1; + if (bJoust) + { // jousting + if (GetLocalInt(oRider,"X3_CUSTOM_RACE_JOUST_PHENO")>0) nPheno=GetLocalInt(oRider,"X3_CUSTOM_RACE_JOUST_PHENO"); + else if (nCurrent==PHENOTYPE_NORMAL||nCurrent==HORSE_PHENOTYPE_MOUNTED_N) nPheno=HORSE_PHENOTYPE_JOUSTING_N; + else if (nCurrent==PHENOTYPE_BIG||nCurrent==HORSE_PHENOTYPE_MOUNTED_L) nPheno=HORSE_PHENOTYPE_JOUSTING_L; + } // jousting + else + { // not jousting + if (GetLocalInt(oRider,"X3_CUSTOM_RACE_MOUNTED_PHENO")>0) nPheno=GetLocalInt(oRider,"X3_CUSTOM_RACE_MOUNTED_PHENO"); + else if (nCurrent==PHENOTYPE_NORMAL||nCurrent==HORSE_PHENOTYPE_JOUSTING_N) nPheno=HORSE_PHENOTYPE_MOUNTED_N; + else if (nCurrent==PHENOTYPE_BIG||nCurrent==HORSE_PHENOTYPE_JOUSTING_L) nPheno=HORSE_PHENOTYPE_MOUNTED_L; + } // not jousting + if (nPheno!=-1) SetPhenoType(nPheno,oRider); +} // HorseSetPhenotype() + + +int HorseGetIsDisabled(object oCreature) +{ // PURPOSE: blocked path, death, entanglement, confusion, paralysis, stun, petrification, fear, turned, sleep, knockdown detection by Azbest + int bDisabled=FALSE; + if (GetIsDead(oCreature)||GetLocalInt(oCreature,"bPathIsBlocked")) + { + bDisabled=TRUE; + } + else + { + effect eEffect=GetFirstEffect(oCreature); + while(GetIsEffectValid(eEffect)&&!bDisabled) + { // traverse effects + if ((GetEffectType(eEffect)==EFFECT_TYPE_INVALIDEFFECT&&(GetEffectDurationType(eEffect)==DURATION_TYPE_TEMPORARY||GetEffectDurationType(eEffect)==DURATION_TYPE_PERMANENT))|| + GetEffectType(eEffect)==EFFECT_TYPE_ENTANGLE|| + GetEffectType(eEffect)==EFFECT_TYPE_CONFUSED|| + GetEffectType(eEffect)==EFFECT_TYPE_PARALYZE|| + GetEffectType(eEffect)==EFFECT_TYPE_STUNNED|| + GetEffectType(eEffect)==EFFECT_TYPE_PETRIFY|| + GetEffectType(eEffect)==EFFECT_TYPE_FRIGHTENED|| + GetEffectType(eEffect)==EFFECT_TYPE_TURNED|| + GetEffectType(eEffect)==EFFECT_TYPE_SLEEP) + { // match + bDisabled=TRUE; + } // match + eEffect=GetNextEffect(oCreature); + } // traverse effects + } + return bDisabled; +} // HorseGetIsDisabled() + + +void HorseMount(object oHorse,int bAnimate=TRUE,int bInstant=FALSE,int nState=0) +{ // PURPOSE: Handle the mounting of oHorse + // 0 = Initial state + // 1 = move to horse + // 2 = adjust for animation + // 3 = animate + // 4 = store horse and premount info + // 5 = set appearances + // 6 = destroy oHorse + object oRider=OBJECT_SELF; + float fMonitorSpeed=0.4f; + float fDenominator=3.0; // fraction denominator used to calculate synchronised timing of the process that runs concurently with animation + string sS; + string sTag; + string sResRef; + int nApp,nTail,nPheno,nN; + location lLoc; + float fF; + object oOb; + int nStoredState=GetLocalInt(oRider,"nX3_StoredMountState"); + int bPostMount=FALSE; + float fTimeDelay; // used to hold delay common references and reduce some repeated math + int bAct=GetLocalInt(GetModule(),"X3_HORSE_ACT_VS_DELAY"); + float fX3_MOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_MOUNT_MULTIPLE"); + float fX3_TIMEOUT_TO_MOUNT=GetLocalFloat(GetModule(),"fX3_TIMEOUT_TO_MOUNT"); + if (GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE")>fX3_MOUNT_MULTIPLE) fX3_MOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE"); + if (fX3_MOUNT_MULTIPLE<=0.0) fX3_MOUNT_MULTIPLE=1.0; + if (GetLocalFloat(oRider,"fX3_TIMEOUT_TO_MOUNT")!=0.0) fX3_TIMEOUT_TO_MOUNT=GetLocalFloat(oRider,"fX3_TIMEOUT_TO_MOUNT"); + if (fX3_TIMEOUT_TO_MOUNT<6.0) fX3_TIMEOUT_TO_MOUNT=18.0; + if (GetStringLength(GetLocalString(oHorse,"X3_HORSE_POSTMOUNT_SCRIPT"))>0) bPostMount=TRUE; + if (HorseGetIsDisabled()) return; // drop out if disabled + if (GetObjectType(oHorse)!=OBJECT_TYPE_CREATURE) return; + if (GetIsInCombat(oRider)||GetIsInCombat(oHorse)||IsInConversation(oRider)) return; + if (nState!=0&&nStoredState>nState) return; // abort recursion of this state + switch(nState) + { // main mounting switch + case 0: + { // ----- 0 - Initial State + DeleteLocalInt(oRider,"nX3_StoredMountState"); + if (!HorseGetCanBeMounted(oHorse,oRider)) + { // not mountable + if (GetIsPC(oRider)) + { // provide error message + FloatingTextStrRefOnCreature(111983,oRider,FALSE); + } // provide error message + return; + } // not mountable + if (GetDistanceBetween(oHorse,oRider)>20.0) + { // too far + ClearAllActions(); + ActionMoveToObject(oHorse,TRUE,15.0); + return; + } // too far + sS=GetLocalString(oHorse,"X3_HORSE_PREMOUNT_SCRIPT"); + if (GetStringLength(sS)>0) + { // premount script + SetLocalObject(oRider,"oX3_TempHorse",oHorse); + ExecuteScript(sS,oRider); + if (GetLocalInt(oHorse,"X3_HORSE_NOMOUNT")) + { // no mount set + DeleteLocalInt(oHorse,"X3_HORSE_NOMOUNT"); + return; + } // no mount set + } // premount script + SetLocalInt(oHorse,"X3_DOING_HORSE_ACTION",TRUE); + SetLocalInt(oRider,"X3_DOING_HORSE_ACTION",TRUE); + if (bAct) + { // make sure horse action can be cancelled if actions cancelled + fTimeDelay=8.0*fX3_MOUNT_MULTIPLE; + DelayCommand(fTimeDelay,DeleteLocalInt(oHorse,"X3_DOING_HORSE_ACTION")); + DelayCommand(fTimeDelay,DeleteLocalInt(oRider,"X3_DOING_HORSE_ACTION")); + } // make sure horse action can be cancelled if actions cancelled + SetSkinInt(oRider,"nX3_StoredFootstep",GetFootstepType(oRider)); + SetSkinInt(oRider,"nX3_HorseRiderPhenotype",GetPhenoType(oRider)); + SetLocalInt(oRider,"X3_HORSE_TAIL",HorseGetMountTail(oHorse)); + SetSkinInt(oRider,"nX3_HorseRiderAppearance",GetAppearanceType(oRider)); + SetSkinInt(oRider,"nX3_HorseTail",GetCreatureTailType(oHorse)); + SetSkinInt(oRider,"nX3_HorseAppearance",GetAppearanceType(oHorse)); + SetLocalInt(oRider,"nX3_HorsePortrait",GetPortraitId(oHorse)); + SetLocalInt(oRider,"nX3_RiderHP",GetCurrentHitPoints(oRider)); + SetSkinInt(oRider,"nX3_HorseHP",GetCurrentHitPoints(oHorse)); + SetSkinInt(oRider,"nX3_HorseRiderTail",GetCreatureTailType(oRider)); + SetLocalInt(oRider,"bX3_IS_RIDER",TRUE); + if (!GetIsPC(oRider)) + { // not a PC - set associate state + SetAssociateState(NW_ASC_IS_BUSY,TRUE,oRider); + } // not a PC - set associate state + AssignCommand(oHorse,ClearAllActions(TRUE)); + fTimeDelay=(fX3_TIMEOUT_TO_MOUNT+HORSE_MOUNT_DURATION)*fX3_MOUNT_MULTIPLE; + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneImmobilize(),oHorse,fTimeDelay); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneGhost(),oHorse,fTimeDelay); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")&&!bAct) AssignCommand(GetModule(),DelayCommand(2.0,HORSE_SupportSetMountingSentinel(oRider,oHorse,fX3_TIMEOUT_TO_MOUNT))); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")&&!bAct) SetCommandable(FALSE,oHorse); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")&&!bAct) SetCommandable(FALSE,oRider); + fTimeDelay=fMonitorSpeed*fX3_MOUNT_MULTIPLE; + SetLocalFloat(oRider,"X3_TOTAL_MOUNT_ANIMATION_DELAY",fDenominator*fTimeDelay); // sets a small time delay so that the following code doesnt happen at once + if (!bAnimate&&bInstant) + { // rapid mount + DelayCommand(fTimeDelay,HorseMount(oHorse,bAnimate,bInstant,4)); + } // rapid mount + else + { // move + if (!bAct) + { // not actions + DelayCommand(fTimeDelay,HorseMount(oHorse,bAnimate,bInstant,1)); + } // not actions + else + { // use actions - interruptable + ClearAllActions(); + ActionMoveToObject(oHorse,TRUE,1.5); + ActionDoCommand(HorseMount(oHorse,bAnimate,bInstant,1)); + } // use actions - interruptable + } // move + break; + } // ----- 0 - Initial State + case 1: + { // ----- 1 - Move To Horse + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")&&GetCommandable(oHorse)&&!bAct) SetCommandable(FALSE,oHorse); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")&&GetCommandable(oRider)&&!bAct) SetCommandable(FALSE,oRider); + oOb=GetLocalObject(oHorse,"oX3_TempRider"); + if (oOb!=oRider&&GetIsObjectValid(oOb)) + { // someone else is mounting that + if (GetIsPC(oRider)) + { // someone else mounting + FloatingTextStringOnCreature(GetName(oOb)+GetStringByStrRef(111984),oRider,FALSE); + } // someone else mounting + DeleteLocalInt(oRider,"X3_DOING_HORSE_ACTION"); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")&&!bAct) SetCommandable(TRUE,oRider); + return; + } // someone else is mounting that + SetLocalObject(oHorse,"oX3_TempRider",oRider); + lLoc=HORSE_SupportGetMountLocation(oHorse,oRider,-90.0); + if (GetLocalInt(GetArea(oRider),"bX3_MOUNT_NO_ZAXIS")||GetLocalInt(GetModule(),"bX3_MOUNT_NO_ZAXIS")||GetLocalInt(oRider,"bX3_MOUNT_NO_ZAXIS")) + { // use vector without z axis - thanks Azbest + vector vLoc=GetPositionFromLocation(lLoc); + vector vRider=GetPosition(oRider); + vLoc-=Vector(0.0,0.0,vLoc.z); + vRider-=Vector(0.0,0.0,vRider.z); + fF=VectorMagnitude(vLoc-vRider); + } // use vector without z axis - thanks Azbest + else + { // use location including Z axis + fF=GetDistanceBetweenLocations(GetLocation(oRider),lLoc); + } // use location including Z axis + fMonitorSpeed=GetLocalFloat(GetModule(),"fX3_FREQUENCY"); + if (fMonitorSpeed<0.5||fMonitorSpeed>9.0) fMonitorSpeed=1.0; // frequency + fTimeDelay=fMonitorSpeed*fX3_MOUNT_MULTIPLE; + if (fX3_TIMEOUT_TO_MOUNT0.1&&GetLocalInt(oRider,"nX3_MovingMount")<=FloatToInt(fX3_TIMEOUT_TO_MOUNT/fTimeDelay)) + { // keep moving + nN=GetLocalInt(oRider,"nX3_MovingMount"); + nN++; // used to support timing out if cannot reach destination + SetLocalInt(oRider,"nX3_MovingMount",nN); + if (nN>FloatToInt(fX3_TIMEOUT_TO_MOUNT/fTimeDelay)) + { // timed out + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + ClearAllActions(); + ActionWait(X3_ACTION_DELAY*fX3_MOUNT_MULTIPLE); + ActionJumpToLocation(lLoc); + SetCommandable(FALSE,oRider); + } + bAnimate=FALSE; + } // timed out + else + { // keep trying + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + ClearAllActions(); + ActionMoveToLocation(lLoc,TRUE); + SetCommandable(FALSE,oRider); + } + SetLocalInt(oRider,"nX3_StoredMountState",1); + float fLastHorseDist=GetDistanceBetween(oRider,oHorse); + if (fLastHorseDist==GetLocalFloat(oRider,"fLastHorseDist")) + { + if (GetLocalInt(GetArea(oRider),"X3_ABORT_WHEN_STUCK")||GetLocalInt(oHorse,"X3_ABORT_WHEN_STUCK")) + { // break next call if we are found twice within the same distance from horse, which means we are stuck + SetLocalInt(oRider,"bPathIsBlocked",TRUE); + SetLocalInt(oRider,"nX3_StoredMountState",2); + } // break next call if we are found twice within the same distance from horse, which means we are stuck + else + { // if we are stuck, we want to mount + // no need to set bPathIsBlocked flag, because forced mount continues the proper chain of commands + SetLocalInt(oRider,"nX3_MovingMount",FloatToInt(fX3_TIMEOUT_TO_MOUNT/fTimeDelay)+1); + } // if we are stuck, we want to mount + } + SetLocalFloat(oRider,"fLastHorseDist",fLastHorseDist); + } // keep trying + DelayCommand(fTimeDelay,HorseMount(oHorse,bAnimate,bInstant,1)); + } // keep moving + else + { // next state + DeleteLocalInt(oRider,"nX3_MovingMount"); + DeleteLocalFloat(oRider,"fLastHorseDist"); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + ClearAllActions(); + SetFacing(GetFacing(oHorse)); + SetCommandable(FALSE,oRider); + } + if (bAnimate&&!GetLocalInt(oHorse,"X3_NO_MOUNT_ANIMATE")) + { // see about animate + DelayCommand(0.1*fX3_MOUNT_MULTIPLE,HorseMount(oHorse,bAnimate,bInstant,2)); + } // see about animate + else + { // no animate + DelayCommand(0.1*fX3_MOUNT_MULTIPLE,HorseMount(oHorse,bAnimate,bInstant,4)); + } // no animate + } // next state + break; + } // ----- 1 - Move to Horse + case 2: + { // ----- 2 - Adjust for Animation + SetLocalInt(oRider,"nX3_StoredMountState",3); + nTail=GetLocalInt(oRider,"X3_HORSE_TAIL"); + SetLocalInt(oHorse,"X3_HORSE_TAIL",nTail); + nApp=HORSE_SupportNullAppearance(oHorse,oRider); + if (nApp>0) SetCreatureAppearanceType(oHorse,nApp); + SetCreatureTailType(nTail,oHorse); + DelayCommand(1.0*fX3_MOUNT_MULTIPLE,HorseMount(oHorse,bAnimate,bInstant,3)); // 1.0 second seems to be long enough to set up for animation + break; + } // ----- 2 - Adjust for Animation + case 3: + { // ----- 3 - Animate + SetLocalInt(oRider,"nX3_StoredMountState",4); + fMonitorSpeed=HORSE_MOUNT_DURATION; + if (GetLocalFloat(oHorse,"X3_HORSE_MOUNT_DURATION")>0.0) fMonitorSpeed=GetLocalFloat(oHorse,"X3_HORSE_MOUNT_DURATION"); + if (fMonitorSpeed<1.0) fMonitorSpeed=1.0; + fMonitorSpeed*=fX3_MOUNT_MULTIPLE; + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneGhost(),oHorse,fMonitorSpeed+0.5); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneGhost(),oRider,fMonitorSpeed+0.5); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + ClearAllActions(); + ActionPlayAnimation(HORSE_ANIMATION_MOUNT,1.0,fMonitorSpeed); + SetCommandable(FALSE,oRider); + } + SetLocalFloat(oRider,"X3_TOTAL_MOUNT_ANIMATION_DELAY",fMonitorSpeed); // "case 4:" and "case 5:" need to happen concurrently and with the same time duration as the animation itself :) + HorseMount(oHorse,bAnimate,bInstant,4); + break; + } // ----- 3 - Animate + case 4: + { // ----- 4 - Store Horse and Premount Info + SetLocalInt(oRider,"nX3_StoredMountState",5); + sResRef=GetResRef(oHorse); + if (GetStringLength(sResRef)<1) SendMessageToPC(oRider,"x3_inc_horse(HorseMount): Error finding horse ResRef in case 4."); + SetSkinString(oRider,"sX3_HorseResRef",sResRef); + SetSkinString(oRider,"sX3_HorseMountTag",GetTag(oHorse)); + SetLocalString(oRider,"sX3_HorseMountScript",GetLocalString(oHorse,"sX3_HORSE_CREATED_SCRIPT")); + SetSkinString(oRider,"X3_HORSE_PREDISMOUNT_SCRIPT",GetLocalString(oHorse,"X3_HORSE_PREDISMOUNT_SCRIPT")); + SetSkinString(oRider,"X3_HORSE_POSTDISMOUNT_SCRIPT",GetLocalString(oHorse,"X3_HORSE_POSTDISMOUNT_SCRIPT")); + SetLocalInt(oRider,"X3_NO_MOUNT_ANIMATE",GetLocalInt(oHorse,"X3_NO_MOUNT_ANIMATE")); + if (GetLocalInt(oHorse,"X3_ABORT_WHEN_STUCK")) SetLocalInt(oRider,"X3_ABORT_WHEN_STUCK",TRUE); + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")) + { // saddlebags support enabled + if (GetLocalInt(oHorse,"bX3_HAS_SADDLEBAGS")&&GetLocalObject(oHorse,"oX3_HorseOwner")!=oRider) if (GetMaster(oHorse)!=oRider) HorseSetOwner(oHorse,oRider,TRUE); + SetLocalInt(oRider,"bX3_HAS_SADDLEBAGS",GetLocalInt(oHorse,"bX3_HAS_SADDLEBAGS")); + if (GetLocalInt(oHorse,"bX3_HAS_SADDLEBAGS")) + { // transfer inventory + HorseStoreInventory(oHorse,oRider); + } // transfer inventory + } // saddlebads support enabled + fMonitorSpeed=GetLocalFloat(oRider,"X3_TOTAL_MOUNT_ANIMATION_DELAY")*(fDenominator-1.0)/fDenominator; + DelayCommand(fMonitorSpeed,HorseMount(oHorse,bAnimate,bInstant,5)); + break; + } // ----- 4 - Store Horse and Premount Info + case 5: + { // ----- 5 - Set Appearance + SetLocalInt(oRider,"nX3_StoredMountState",6); + nApp=GetAppearanceType(oHorse); + nTail=GetLocalInt(oRider,"X3_HORSE_TAIL"); + SetCreatureAppearanceType(oRider,HORSE_SupportGetMountedAppearance(oRider)); + HorseSetPhenotype(oRider); + SetCreatureTailType(nTail,oRider); + nApp=GetLocalInt(oHorse,"X3_HORSE_FOOTSTEP"); + if (nApp>0) SetFootstepType(nApp,oRider); + else { SetFootstepType(HORSE_FOOTSTEP_SOUND,oRider); } + fMonitorSpeed=GetLocalFloat(oRider,"X3_TOTAL_MOUNT_ANIMATION_DELAY")/fDenominator; + if (!bAnimate||bInstant) fTimeDelay=fMonitorSpeed; + else fTimeDelay=0.6; // changing appearance, pheno and tail takes about 0.6 second, hide the horse after that amount of time + DelayCommand(fTimeDelay,ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY),oHorse,fMonitorSpeed*fDenominator)); + DelayCommand(fMonitorSpeed,HorseMount(oHorse,bAnimate,bInstant,6)); + break; + } // ----- 5 - Set Appearance + case 6: + { // ----- 6 - Destroy oHorse + SetLocalInt(oRider,"nX3_StoredMountState",7); + SetSkinInt(oRider,"bX3_IS_MOUNTED",TRUE); + SetLocalObject(oRider,"oX3_Saddlebags",GetLocalObject(oHorse,"oX3_Saddlebags")); + sS=GetLocalString(oHorse,"X3_HORSE_POSTMOUNT_SCRIPT"); + if (bPostMount) + { // run post mount script + SetLocalObject(oRider,"oX3_TempHorse",oHorse); + ExecuteScript(sS,oRider); + DeleteLocalObject(oRider,"oX3_TempHorse"); + DeleteLocalInt(oRider,"bX3_HORSE_MODIFIERS"); + } // run post mount script + else + { // no post mount script + HORSE_SupportIncreaseSpeed(oRider,oHorse); + HORSE_SupportAdjustMountedArcheryPenalty(oRider); + DelayCommand(0.4,HORSE_SupportApplyMountedSkillDecreases(oRider)); + HORSE_SupportApplyACBonus(oRider,oHorse); + HORSE_SupportApplyHPBonus(oRider,oHorse); + } // no post mount script + SetLocalInt(oRider,"bX3_HORSE_MODIFIERS",TRUE); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")) SetLocalInt(oRider,"bX3_STORE_MOUNT_INFO",TRUE); + DeleteLocalFloat(oRider,"X3_TOTAL_MOUNT_ANIMATION_DELAY"); + AssignCommand(oHorse,SetIsDestroyable(TRUE,FALSE,FALSE)); + DestroyObject(oHorse,0.3); + DelayCommand(0.5*fX3_MOUNT_MULTIPLE,DeleteLocalInt(oRider,"X3_DOING_HORSE_ACTION")); + if (!GetIsPC(oRider)) + { // not a PC - set associate state + DelayCommand(0.5*fX3_MOUNT_MULTIPLE,SetAssociateState(NW_ASC_IS_BUSY,FALSE,oRider)); + } // not a PC - set associate state + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) DelayCommand(0.5*fX3_MOUNT_MULTIPLE,SetCommandable(TRUE,oRider)); + break; + } // ----- 6 - Destroy oHorse + default: break; + } // main mounting switch +} // HorseMount() + + +object HorseDismount(int bAnimate=TRUE,int bSetOwner=TRUE) +{ // PURPOSE: Dismount the horse + object oRider=OBJECT_SELF; + object oHorse=OBJECT_INVALID; + object oOb; + string sS,sRR,sT; + int nN,nApp,nTail,nFootstep; + int bPostDismount=FALSE; + location lLoc; + float fX3_MOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_MOUNT_MULTIPLE"); + float fX3_DISMOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_DISMOUNT_MULTIPLE"); + if (GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE")>fX3_MOUNT_MULTIPLE) fX3_MOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE"); + if (fX3_MOUNT_MULTIPLE<=0.0) fX3_MOUNT_MULTIPLE=1.0; + if (GetLocalFloat(oRider,"fX3_DISMOUNT_MULTIPLE")>0.0) fX3_DISMOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_DISMOUNT_MULTIPLE"); + if (fX3_DISMOUNT_MULTIPLE>0.0) fX3_MOUNT_MULTIPLE=fX3_DISMOUNT_MULTIPLE; // use dismount multiple instead of mount multiple + float fDelay=1.0*fX3_MOUNT_MULTIPLE; // base delay for non-animated dismount + if (GetStringLength(GetLocalString(oHorse,"X3_HORSE_POSTDISMOUNT_SCRIPT"))>0) bPostDismount=TRUE; + if (GetIsObjectValid(oRider)) + { // oRider is a valid object + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) SetCommandable(FALSE,oRider); + if (HorseGetIsMounted(oRider)) + { // Mounted + if (!GetIsPC(oRider)) + { // not PC - set associate state busy + SetAssociateState(NW_ASC_IS_BUSY,TRUE,oRider); + } // not PC - set associate state busy + sS=GetSkinString(oRider,"X3_HORSE_PREDISMOUNT_SCRIPT"); + if (GetStringLength(sS)>0) + { // PREDISMOUNT SCRIPT + ExecuteScript(sS,oRider); + if (GetLocalInt(oRider,"X3_HORSE_NOMOUNT")) + { // abort + DeleteLocalInt(oRider,"X3_HORSE_NOMOUNT"); + return OBJECT_INVALID; + } // abort + } // PREDISMOUNT SCRIPT + DeleteSkinString(oRider,"X3_HORSE_PREDISMOUNT_SCRIPT"); + SetLocalInt(oRider,"X3_DOING_HORSE_ACTION",TRUE); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + ClearAllActions(); + SetCommandable(FALSE,oRider); + } + if (bAnimate&&!GetLocalInt(oRider,"X3_NO_MOUNT_ANIMATE")) + { // animated dismount + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + ActionWait(X3_ACTION_DELAY*fX3_MOUNT_MULTIPLE); + ActionPlayAnimation(HORSE_ANIMATION_DISMOUNT,1.0,HORSE_DISMOUNT_DURATION*fX3_MOUNT_MULTIPLE); + SetCommandable(FALSE,oRider); + } + fDelay=(X3_ACTION_DELAY+HORSE_DISMOUNT_DURATION)*fX3_MOUNT_MULTIPLE; // delay for animated dismount + } + sRR=GetSkinString(oRider,"sX3_HorseResRef"); + if (GetStringLength(sRR)>0) + { // create dismounted horse + if (bSetOwner) oOb=oRider; + sT=GetSkinString(oRider,"sX3_HorseMountTag"); + nApp=GetSkinInt(oRider,"nX3_HorseAppearance"); + nTail=GetSkinInt(oRider,"nX3_HorseTail"); // add this to be preserved + if (nApp<7) nApp=-1; + if (nTail<3) nTail=-1; + if (nFootstep<2) nFootstep=-1; + nFootstep=GetFootstepType(oRider); + sS=GetLocalString(oRider,"sX3_HorseMountScript"); + lLoc=HORSE_SupportGetMountLocation(oRider,oRider,-90.0); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneGhost(),oRider,fDelay+1.0*fX3_MOUNT_MULTIPLE); + oHorse=HorseCreateHorse(sRR,GetLocation(oRider),oOb,sT,nApp,nTail,nFootstep,sS); + SetLocalInt(oHorse,"X3_DOING_HORSE_ACTION",TRUE); + if (GetLocalInt(oRider,"X3_ABORT_WHEN_STUCK")) SetLocalInt(oHorse,"X3_ABORT_WHEN_STUCK",TRUE); + if (!GetIsObjectValid(oHorse)) + { // failed to create + SendMessageToPC(oRider,"x3_inc_horse(HorseDismount): Failed to create horse."); + } // failed to create + if (oOb==oRider) SetLocalObject(oRider,"oAssignedHorse",oHorse); + if (GetLocalInt(oRider,"nX3_HorsePortrait")>0) SetPortraitId(oHorse,GetLocalInt(oRider,"nX3_HorsePortrait")); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY),oHorse,fDelay+0.5*fX3_MOUNT_MULTIPLE); + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")&&GetIsObjectValid(oHorse)) + { // saddlebags support enabled + SetLocalInt(oHorse,"bX3_HAS_SADDLEBAGS",GetLocalInt(oRider,"bX3_HAS_SADDLEBAGS")); + if (GetLocalInt(oRider,"bX3_HAS_SADDLEBAGS")) + { // transfer contents + HorseRestoreInventory(oHorse); + } // transfer contents + } // saddlebads support enabled + } // create dismounted horse + else + { // resref not defined + SendMessageToPC(oRider,"x3_inc_horse(HorseDismount): Error resref missing."); + } // resref not defined + DeleteSkinInt(oRider,"bX3_IS_MOUNTED"); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")) SetLocalInt(oRider,"bX3_STORE_MOUNT_INFO",TRUE); + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) + { + SetCommandable(TRUE,oRider); + if (!bAnimate||GetLocalInt(oRider,"X3_NO_MOUNT_ANIMATE")) ActionWait(fDelay); + ActionMoveToLocation(lLoc,FALSE); + SetCommandable(FALSE,oRider); + } + DelayCommand(fDelay-0.8*fX3_MOUNT_MULTIPLE,ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectCutsceneGhost(),oHorse,1.7*fX3_MOUNT_MULTIPLE)); + //DelayCommand(fDelay-0.8*fX3_MOUNT_MULTIPLE+1.7*fX3_MOUNT_MULTIPLE, FloatingTextStringOnCreature("ghost off",oRider)); // this tells us when the ghost effect from the above line wears off + DelayCommand(fDelay-0.7*fX3_MOUNT_MULTIPLE,HORSE_SupportResetUnmountedAppearance(oRider)); // keep in mind: changing mounted appearances takes about 0.6 seconds + DelayCommand(fDelay+0.0*fX3_MOUNT_MULTIPLE,HORSE_SupportTransferPreservedValues(oRider,oHorse)); + DelayCommand(fDelay+0.7*fX3_MOUNT_MULTIPLE,HORSE_SupportCleanVariables(oRider)); + DelayCommand(fDelay+1.0*fX3_MOUNT_MULTIPLE,DeleteLocalInt(oRider,"X3_DOING_HORSE_ACTION")); + DelayCommand(fDelay+1.0*fX3_MOUNT_MULTIPLE,DeleteLocalInt(oHorse,"X3_DOING_HORSE_ACTION")); + if (!GetIsPC(oRider)) + { // not PC - set associate state not busy + DelayCommand(fDelay+1.0*fX3_MOUNT_MULTIPLE,SetAssociateState(NW_ASC_IS_BUSY,FALSE,oRider)); + } // not PC - set associate state not busy + if (!GetLocalInt(GetModule(),"X3_NO_MOUNT_COMMANDABLE")) DelayCommand(fDelay+1.0*fX3_MOUNT_MULTIPLE,SetCommandable(TRUE,oRider)); + } // Mounted + } // oRider is a valid object + return oHorse; +} // HorseDismount() + + +int HorseGetMountTail(object oHorse) +{ // PURPOSE: Determine the tail that is needed to represent this horse + int nTail=GetCreatureTailType(oHorse); + int nApp=GetAppearanceType(oHorse); + if (GetLocalInt(oHorse,"X3_HORSE_TAIL")>0) return GetLocalInt(oHorse,"X3_HORSE_TAIL"); + if (nApp>=HORSE_APPEARANCE_OFFSET&&nApp<=HORSE_APPEARANCE_OFFSET+HORSE_NUMBER_OF_HORSES) + { // default horses + return (nApp-HORSE_APPEARANCE_OFFSET)+HORSE_TAIL_OFFSET; + } // default horses + else if (nApp>=HORSE_NULL_RACE_DWARF&&nApp<=HORSE_NULL_RACE_HUMAN) return nTail; + return CREATURE_TAIL_TYPE_NONE; +} // HorseGetMountTail() + + +void HorseInstantDismount(object oRider) +{ // PURPOSE: Instantly dismount oRider + float fX3_MOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_MOUNT_MULTIPLE"); + float fX3_DISMOUNT_MULTIPLE=GetLocalFloat(GetArea(oRider),"fX3_DISMOUNT_MULTIPLE"); + if (GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE")>fX3_MOUNT_MULTIPLE) fX3_MOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_MOUNT_MULTIPLE"); + if (fX3_MOUNT_MULTIPLE<=0.0) fX3_MOUNT_MULTIPLE=1.0; + if (GetLocalFloat(oRider,"fX3_DISMOUNT_MULTIPLE")>0.0) fX3_DISMOUNT_MULTIPLE=GetLocalFloat(oRider,"fX3_DISMOUNT_MULTIPLE"); + if (fX3_DISMOUNT_MULTIPLE>0.0) fX3_MOUNT_MULTIPLE=fX3_DISMOUNT_MULTIPLE; // use dismount multiple instead of mount multiple + HORSE_SupportResetUnmountedAppearance(oRider); + DeleteLocalInt(oRider,"bX3_HORSE_MODIFIERS"); + DelayCommand(0.1*fX3_MOUNT_MULTIPLE,HORSE_SupportOriginalSpeed(oRider)); + DelayCommand(0.2*fX3_MOUNT_MULTIPLE,HORSE_SupportRemoveMountedSkillDecreases(oRider)); + DelayCommand(0.2*fX3_MOUNT_MULTIPLE,HORSE_SupportAdjustMountedArcheryPenalty(oRider)); + DelayCommand(0.3*fX3_MOUNT_MULTIPLE,HORSE_SupportRemoveACBonus(oRider)); + DelayCommand(0.3*fX3_MOUNT_MULTIPLE,HORSE_SupportRemoveHPBonus(oRider)); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")) SetLocalInt(oRider,"bX3_STORE_MOUNT_INFO",TRUE); + DelayCommand(0.4*fX3_MOUNT_MULTIPLE,HORSE_SupportMountCleanVariables(oRider)); + DeleteSkinInt(oRider,"bX3_IS_MOUNTED"); +} // HorseInstantDismount() + + +void HorseInstantMount(object oRider,int nTail,int bJoust=FALSE,string sResRef="") +{ // PURPOSE: Instantly mount oRider + string sRR=sResRef; + if (GetStringLength(sRR)<1) sRR="x3_horse001"; + SetSkinInt(oRider,"nX3_StoredFootstep",GetFootstepType(oRider)); + SetSkinInt(oRider,"nX3_HorseRiderPhenotype",GetPhenoType(oRider)); + SetSkinInt(oRider,"nX3_HorseRiderAppearance",GetAppearanceType(oRider)); + SetLocalInt(oRider,"nX3_RiderHP",GetCurrentHitPoints(oRider)); + SetSkinInt(oRider,"nX3_HorseRiderTail",GetCreatureTailType(oRider)); + SetCreatureAppearanceType(oRider,HORSE_SupportGetMountedAppearance(oRider)); + HorseSetPhenotype(oRider,bJoust); + SetCreatureTailType(nTail,oRider); + SetFootstepType(HORSE_FOOTSTEP_SOUND,oRider); + SetSkinString(oRider,"sX3_HorseResRef",sRR); + HORSE_SupportIncreaseSpeed(oRider,OBJECT_INVALID); + HORSE_SupportAdjustMountedArcheryPenalty(oRider); + SetSkinInt(oRider,"bX3_IS_MOUNTED",TRUE); + DelayCommand(0.5,HORSE_SupportApplyMountedSkillDecreases(oRider)); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")) SetLocalInt(oRider,"bX3_STORE_MOUNT_INFO",TRUE); + SetLocalInt(oRider,"bX3_HORSE_MODIFIERS",TRUE); +} // HorseInstantMount() + + +object HorseGetPaladinMount(object oRider) +{ // PURPOSE: Return the paladin mount for oRider + string sS; + object oNPC; + int nN=1; + if (GetIsObjectValid(oRider)&&GetObjectType(oRider)==OBJECT_TYPE_CREATURE) + { // valid parameter + sS=GetResRef(oRider); + if (GetStringLeft(sS,GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) return oRider; // oRider IS a paladin mount + sS=GetSkinString(oRider,"sX3_HorseResRef"); + if (GetStringLeft(sS,GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) return oRider; // oRider is riding a paladin mount + oNPC=GetLocalObject(oRider,"oX3PaladinMount"); + if (GetIsObjectValid(oNPC)&&GetStringLeft(GetResRef(oNPC),GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) return oNPC; + oNPC=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oRider,nN); + while(GetIsObjectValid(oNPC)) + { // check henchmen + sS=GetResRef(oNPC); + if (GetStringLeft(sS,GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) return oNPC; + nN++; + oNPC=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oRider,nN); + } // check henchmen + return GetLocalObject(oRider,"oX3PaladinMount"); + } // valid parameter + return OBJECT_INVALID; +} // HorseGetPaladinMount() + + +object HorseSummonPaladinMount(int bPHBDuration=FALSE) +{ // PURPOSE: Summon Paladin Mount + object oSummoner=OBJECT_SELF; + object oMount; + location lLoc; + int nLevel=GetLevelByClass(CLASS_TYPE_PALADIN,oSummoner); + int nDespawnTime; + int nCurrentTime; + int nMountNum=1; + string sResRef=HORSE_PALADIN_PREFIX; + effect eVFX; + oMount=HorseGetPaladinMount(oSummoner); + if (!GetIsObjectValid(oMount)&&nLevel>4&&GetObjectType(oSummoner)==OBJECT_TYPE_CREATURE) + { // okay to summon - only one paladin mount at a time + if ((GetIsPC(oSummoner)||GetIsDM(oSummoner))&&!GetHasFeat(FEAT_HORSE_MENU,oSummoner)) HorseAddHorseMenu(oSummoner); + if (nLevel>7&&nLevel<11) nMountNum=2; + else if (nLevel>10&&nLevel<15) nMountNum=3; + else if (nLevel>14&&nLevel<25) nMountNum=4; + else if (nLevel>24&&nLevel<30) nMountNum=5; + else if (nLevel>29&&nLevel<35) nMountNum=6; + else if (nLevel>34&&nLevel<40) nMountNum=7; + else if (nLevel>39) nMountNum=8; + lLoc=HORSE_SupportGetMountLocation(oSummoner,oSummoner); + oMount=HorseCreateHorse(sResRef+IntToString(nMountNum),lLoc,oSummoner); + if (!GetIsObjectValid(oMount)) oMount=HorseCreateHorse(sResRef+IntToString(nMountNum),GetLocation(oSummoner),oSummoner); + if (GetIsObjectValid(oMount)) + { // oMount created + eVFX=EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVFX,oMount,3.0); + eVFX=EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_2); + if (nMountNum>3) eVFX=EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVFX,GetLocation(oMount)); + if (bPHBDuration) + { // Players Handbook 3.5 edition durations + nCurrentTime=HORSE_SupportAbsoluteMinute(); + nDespawnTime=(2*nLevel*60)+nCurrentTime; + SetLocalInt(oSummoner,"nX3_PALADIN_UNSUMMON",nDespawnTime); + } // Players Handbook 3.5 edition durations + else + { // 24 hour - popular bioware + nCurrentTime=HORSE_SupportAbsoluteMinute(); + nDespawnTime=nCurrentTime+(60*24); + SetLocalInt(oSummoner,"nX3_PALADIN_UNSUMMON",nDespawnTime); + } // 24 hour - popular bioware + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oSummoner)) SetLocalInt(oSummoner,"bX3_STORE_MOUNT_INFO",TRUE); + SetLocalObject(oSummoner,"oX3PaladinMount",oMount); + } // oMount created + } // okay to summon - only one paladin mount at a time + else { oMount=OBJECT_INVALID; } + return oMount; +} // HorseSummonPaladinMount() + +object HorseSummonPhantomSteed(int nCasterLvl, int nDuration) +{ // PURPOSE: Summon Phantom Steed + object oSummoner=OBJECT_SELF; + object oMount; + location lLoc; + int nDespawnTime; + int nCurrentTime; + int nMountNum=1; + string sResRef=HORSE_PALADIN_PREFIX; + effect eVFX; + oMount=HorseGetPaladinMount(oSummoner); + if (!GetIsObjectValid(oMount) && GetObjectType(oSummoner) == OBJECT_TYPE_CREATURE) + { // okay to summon - only one mount at a time + if ((GetIsPC(oSummoner) || GetIsDM(oSummoner))&&!GetHasFeat(FEAT_HORSE_MENU,oSummoner)) HorseAddHorseMenu(oSummoner); + if (nCasterLvl < 11) nMountNum = 2; + else if (nCasterLvl > 10 && nCasterLvl < 15) nMountNum = 3; + else if (nCasterLvl > 14 && nCasterLvl < 25) nMountNum = 4; + else if (nCasterLvl > 24 && nCasterLvl < 30) nMountNum = 5; + else if (nCasterLvl > 29 && nCasterLvl < 35) nMountNum = 6; + else if (nCasterLvl > 34 && nCasterLvl < 40) nMountNum = 7; + else if (nCasterLvl > 39) nMountNum = 8; + lLoc=HORSE_SupportGetMountLocation(oSummoner,oSummoner); + oMount=HorseCreateHorse(sResRef+IntToString(nMountNum),lLoc,oSummoner); + if (!GetIsObjectValid(oMount)) oMount=HorseCreateHorse(sResRef+IntToString(nMountNum),GetLocation(oSummoner),oSummoner); + if (GetIsObjectValid(oMount)) + { // oMount created + eVFX=EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVFX,oMount,3.0); + eVFX=EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_2); + if (nMountNum>3) eVFX=EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVFX,GetLocation(oMount)); + nCurrentTime=HORSE_SupportAbsoluteMinute(); + nDespawnTime=(nDuration*60)+nCurrentTime; + SetLocalInt(oSummoner,"nX3_PALADIN_UNSUMMON",nDespawnTime); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oSummoner)) SetLocalInt(oSummoner,"bX3_STORE_MOUNT_INFO",TRUE); + SetLocalObject(oSummoner,"oX3PaladinMount",oMount); + } // oMount created + } // okay to summon - only one paladin mount at a time + else { oMount=OBJECT_INVALID; } + return oMount; +} // HorseSummonPaladinMount() + +void HorseUnsummonPaladinMount() +{ // PURPOSE: Unsummon Paladin Mount + object oPaladin=OBJECT_SELF; + object oMount=HorseGetPaladinMount(oPaladin); + effect eVFX; + if (!GetIsObjectValid(oMount)) oMount=GetLocalObject(oPaladin,"oX3PaladinMount"); + if (GetIsObjectValid(oMount)&&oMount!=oPaladin) + { // Paladin Mount exists + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oPaladin)) SetLocalInt(oPaladin,"bX3_STORE_MOUNT_INFO",TRUE); + if (oMount==oPaladin) + { // Mounted - must dismount first + ClearAllActions(TRUE); + oMount=HorseDismount(FALSE,TRUE); + DelayCommand(3.0,HorseUnsummonPaladinMount()); + } // Mounted - must dismount first + else + { // is the mount + if (GetIsPC(oPaladin)) + { + SendMessageToPCByStrRef(oPaladin,111985); + } + DeleteLocalInt(oPaladin,"nX3_PALADIN_UNSUMMON"); + eVFX=EffectVisualEffect(VFX_IMP_UNSUMMON); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVFX,GetLocation(oMount)); + DestroyObject(oMount); + } // is the mount + } // Paladin Mount exists + else + { // perhaps the command is being called by the mount itself + if (GetStringLeft(GetResRef(oPaladin),GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) + { // is a paladin mount despawning itself + oMount=oPaladin; + eVFX=EffectVisualEffect(VFX_IMP_UNSUMMON); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVFX,GetLocation(oMount)); + DestroyObject(oMount); + } // is a paladin mount despawning itself + } // perhaps the command is being called by the mount itself +} // HorseUnsummonPaladinMount() + + +int HorseGetIsAMount(object oTarget) +{ // PURPOSE: Return TRUE if oTarget is a mountable creature + int nApp; + int nTail; + if (GetSkinInt(oTarget,"bX3_IS_MOUNTED")) return FALSE; + if (GetLocalInt(oTarget,"bX3_IS_MOUNT")) return TRUE; + else if (GetLocalInt(oTarget,"bX3_IS_RIDER")) return FALSE; + else if (GetStringLength(GetLocalString(oTarget,"X3_HORSE_PREMOUNT_SCRIPT"))>0) return TRUE; + else if (GetStringLength(GetLocalString(oTarget,"X3_HORSE_PREDISMOUNT_SCRIPT"))>0) return TRUE; + else if (GetStringLength(GetLocalString(oTarget,"X3_HORSE_POSTDISMOUNT_SCRIPT"))>0) return TRUE; + else if (GetStringLength(GetLocalString(oTarget,"X3_HORSE_POSTMOUNT_SCRIPT"))>0) return TRUE; + else if (GetStringLength(GetLocalString(oTarget,"X3_HORSE_OWNER_TAG"))>0) return TRUE; + else if (GetLocalInt(oTarget,"X3_HORSE_NULL_APPEARANCE")>0) return TRUE; + //else if (GetSkinInt(oTarget,"X3_HORSE_APPEARANCE")>0) return TRUE; + else if (GetLocalInt(oTarget,"X3_HORSE_TAIL")>0) return TRUE; + nApp=GetAppearanceType(oTarget); + nTail=GetCreatureTailType(oTarget); + if (nApp>=HORSE_APPEARANCE_OFFSET&&nApp<=HORSE_APPEARANCE_OFFSET+HORSE_NUMBER_OF_HORSES) return TRUE; + if (nApp==HORSE_NULL_RACE_GNOME||nApp==HORSE_NULL_RACE_ELF||nApp==HORSE_NULL_RACE_GNOME||nApp==HORSE_NULL_RACE_HALFELF|| + nApp==HORSE_NULL_RACE_HALFLING||nApp==HORSE_NULL_RACE_HALFORC||nApp==HORSE_NULL_RACE_HUMAN) + { // might be a tail always based mount + if (nTail>=HORSE_TAIL_OFFSET&&nTail<=HORSE_TAIL_OFFSET+HORSE_NUMBER_OF_HORSES&&nTail!=14) return TRUE; + } // might be a tail always based mount + return FALSE; +} // HorseGetIsAMount() + + +void HorseStoreInventory(object oCreature,object oRider=OBJECT_INVALID) +{ // PURPOSE: To store inventory being carried by oCreature + object oWP=GetWaypointByTag("X3_HORSE_INVENTORY_STORAGE"); + object oOwner=GetMaster(oCreature); + object oOwnersMaster=GetMaster(oOwner); + object oItem; + object oChest; + object oCont; + int nC; + int nN; + string sR; + string sT; + int nST; + int nCH; + string sID=GetTag(oCreature); // ID to uniquely identify for inventory storage + string sDB="X3SADDLEBAG"+GetTag(GetModule()); + if (GetStringLength(GetLocalString(GetModule(),"X3_SADDLEBAG_DATABASE"))>0) sDB=GetLocalString(GetModule(),"X3_SADDLEBAG_DATABASE"); + if (GetStringLength(sID)>6) sID=GetStringLeft(sID,6); + sID=sID+GetStringRight(GetResRef(oCreature),2); + if (oOwner!=oCreature&&GetIsObjectValid(oOwner)) + { // owner specified + if (!GetIsPC(oOwner)) + { // henchman owner + sT=GetTag(oOwner); + if (GetStringLength(sT)>8) sT=GetStringLeft(sT,6)+GetStringRight(sT,2); + sID=sID+sT; + if (GetIsObjectValid(oOwnersMaster)&&oOwner!=oOwnersMaster&&GetIsPC(oOwnersMaster)) + { // PC + sID=sID+GetPCPublicCDKey(oOwnersMaster)+GetStringLeft(GetName(oOwnersMaster),4); + } // PC + } // henchman owner + else + { // PC owner + sID=sID+GetPCPublicCDKey(oOwner)+GetStringLeft(GetName(oOwner),4); + } // PC owner + } // owner specified + if (GetIsObjectValid(oWP)) + { // do not use database + // I am using a placeable that has inventory but, by default does not + // have scripts that produce extra inventory. + oChest=CreateObject(OBJECT_TYPE_PLACEABLE,"x3_plc_jars001",GetLocation(oWP),FALSE,"X3_"+sID); + SetName(oChest,GetName(oCreature)+"'s Inventory"); + SetLocalObject(oCreature,"oX3_Saddlebags",oChest); + HORSE_SupportTransferInventory(oCreature,oChest,GetLocation(oChest)); + } // do not use database + else + { // use database + nC=0; + oItem=GetFirstItemInInventory(oCreature); + while(GetIsObjectValid(oItem)) + { // store inventory + nC++; + sR=GetResRef(oItem); + sT=GetTag(oItem); + nST=GetItemStackSize(oItem); + nCH=GetItemCharges(oItem); + SetCampaignString(sDB,"sR"+sID+IntToString(nC),sR); + SetCampaignString(sDB,"sT"+sID+IntToString(nC),sT); + SetCampaignInt(sDB,"nS"+sID+IntToString(nC),nST); + SetCampaignInt(sDB,"nC"+sID+IntToString(nC),nCH); + DestroyObject(oItem,0.1); + oItem=GetNextItemInInventory(oCreature); + } // store inventory + SetCampaignInt(sDB,"nCO_"+sID,nC); + if (GetIsObjectValid(oRider)) SetLocalString(oRider,"sDB_Inv",sID); + } // use database +} // HorseStoreInventory() + + +void HorseRestoreInventory(object oCreature,int bDrop=FALSE) +{ // PURPOSE: To restore inventory that was stored while mounted + object oWP=GetWaypointByTag("X3_HORSE_INVENTORY_STORAGE"); + object oOwner=GetMaster(oCreature); + object oOwnersMaster=GetMaster(oOwner); + object oItem; + object oChest; + int nC; + int nN; + string sR; + string sT; + int nST; + int nCH; + string sID=GetTag(oCreature);// ID to uniquely identify for inventory storage + string sDB="X3SADDLEBAG"+GetTag(GetModule()); + if (GetStringLength(GetLocalString(GetModule(),"X3_SADDLEBAG_DATABASE"))>0) sDB=GetLocalString(GetModule(),"X3_SADDLEBAG_DATABASE"); + if (GetStringLength(sID)>6) sID=GetStringLeft(sID,6); + sID=sID+GetStringRight(GetResRef(oCreature),2); + if (oOwner!=oCreature&&GetIsObjectValid(oOwner)) + { // owner specified + if (!GetIsPC(oOwner)) + { // henchman owner + sT=GetTag(oOwner); + if (GetStringLength(sT)>8) sT=GetStringLeft(sT,6)+GetStringRight(sT,2); + sID=sID+sT; + if (GetIsObjectValid(oOwnersMaster)&&oOwner!=oOwnersMaster&&GetIsPC(oOwnersMaster)) + { // PC + sID=sID+GetPCPublicCDKey(oOwnersMaster)+GetStringLeft(GetName(oOwnersMaster),4); + } // PC + } // henchman owner + else + { // PC owner + sID=sID+GetPCPublicCDKey(oOwner)+GetStringLeft(GetName(oOwner),4); + } // PC owner + } // owner specified + if (GetIsObjectValid(oWP)) + { // do not use database + oChest=GetObjectByTag("X3_"+sID); + if (!GetIsObjectValid(oChest)) oChest=GetLocalObject(oOwner,"oX3_Saddlebags"); + if (GetIsObjectValid(oChest)) + { // chest found + HORSE_SupportTransferInventory(oChest,oCreature,GetLocation(oCreature),TRUE); + } // chest found + else + { // error + PrintString("ERROR: x3_inc_horse 'HorseRestoreInventory()' Could not find chest 'X3_"+sID+"'!"); + } // error + } // do not use database + else + { // use database + nC=GetCampaignInt(sDB,"nCO_"+sID); + while(nC>0) + { // restore inventory + sR=GetCampaignString(sDB,"sR"+sID+IntToString(nC)); + sT=GetCampaignString(sDB,"sT"+sID+IntToString(nC)); + nST=GetCampaignInt(sDB,"nS"+sID+IntToString(nC)); + nCH=GetCampaignInt(sDB,"nC"+sID+IntToString(nC)); + DeleteCampaignVariable(sDB,"sR"+sID+IntToString(nC)); + DeleteCampaignVariable(sDB,"sT"+sID+IntToString(nC)); + DeleteCampaignVariable(sDB,"nS"+sID+IntToString(nC)); + DeleteCampaignVariable(sDB,"nC"+sID+IntToString(nC)); + if (!bDrop) oItem=CreateItemOnObject(sR,oCreature,nST,sT); + else + { // drop + oItem=CreateObject(OBJECT_TYPE_ITEM,sR,GetLocation(oCreature),FALSE,sT); + if (GetItemStackSize(oItem)!=nST&&nST!=0) SetItemStackSize(oItem,nST); + } // drop + if (nCH>0) SetItemCharges(oItem,nCH); + nC--; + } // restore inventory + DeleteCampaignVariable(sDB,"nCO_"+sID); + } // use database +} // HorseRestoreInventory() + + +void HorseChangeToDefault(object oCreature) +{ // PURPOSE: HorseChangeToDefault + int nRace=GetRacialType(oCreature); + int nDefApp=StringToInt(Get2DAString("racialtypes","Appearance",nRace)); + int nDefPhen; + int nPheno=GetPhenoType(oCreature); + if (nPheno==HORSE_PHENOTYPE_JOUSTING_L||nPheno==HORSE_PHENOTYPE_MOUNTED_L) nDefPhen=PHENOTYPE_BIG; + else { nDefPhen=PHENOTYPE_NORMAL; } + SetCreatureAppearanceType(oCreature,nDefApp); + SetPhenoType(nDefPhen,oCreature); + SetCreatureTailType(CREATURE_TAIL_TYPE_NONE,oCreature); + SetFootstepType(FOOTSTEP_TYPE_NORMAL,oCreature); + DeleteLocalInt(oCreature,"bX3_HORSE_MODIFIERS"); + DelayCommand(0.1,HORSE_SupportOriginalSpeed(oCreature)); + DelayCommand(0.2,HORSE_SupportRemoveMountedSkillDecreases(oCreature)); + DelayCommand(0.2,HORSE_SupportAdjustMountedArcheryPenalty(oCreature)); + DelayCommand(0.4,HORSE_SupportRemoveACBonus(oCreature)); + DelayCommand(0.4,HORSE_SupportRemoveHPBonus(oCreature)); + DelayCommand(1.0,HORSE_SupportCleanVariables(oCreature)); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsPC(oCreature)) SetLocalInt(oCreature,"bX3_STORE_MOUNT_INFO",TRUE); +} // HorseChangeToDefault() + + +void HorseIfNotDefaultAppearanceChange(object oCreature) +{ // PURPOSE: See if not default appearance + int nRace=GetRacialType(oCreature); + int nDefApp=StringToInt(Get2DAString("racialtypes","Appearance",nRace)); + if (GetAppearanceType(oCreature)!=nDefApp) HorseChangeToDefault(oCreature); +} // HorseIfNotDefaultAppearanceChange() + + +object HorseGetMyHorse(object oRider) +{ // PURPOSE: Return active horse + object oRet=GetLocalObject(oRider,"oX3_TempHorse"); + if (GetIsObjectValid(oRet)) return oRet; + return GetLocalObject(oRider,"oAssignedHorse"); +} // HorseGetMyHorse() + + +int HorseGetHasAHorse(object oRider) +{ // PURPOSE: Return if oRider has a horse + int nN=1; + object oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oRider,nN); + while(GetIsObjectValid(oHench)) + { // traverse henchmen + if (HorseGetIsAMount(oHench)) return TRUE; + nN++; + oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oRider,nN); + } // traverse henchmen + return FALSE; +} // HorseGetHasAHorse() + + +object HorseGetHorse(object oRider,int nN=1) +{ // PURPOSE: Return the nNth horse of oRider + int nC=1; + int nH=0; + object oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oRider,nC); + while(GetIsObjectValid(oHench)) + { // traverse henchmen + if (HorseGetIsAMount(oHench)) + { // is a horse + nH++; + if (nH==nN) return oHench; + } // is a horse + nC++; + oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oRider,nC); + } // traverse henchmen + return OBJECT_INVALID; +} // HorseGetHorse() + + +void HorseRestoreHenchmenLocations(object oPC) +{ // PURPOSE: Restore the locations of the henchmen of henchmen + object oHench; + int nN; + int nC; + object oAssoc; + int bNoMounts; + int bNoMounting; + int bRunAgain=FALSE; + float fDelay=0.2; + object oArea; + if (GetLocalInt(GetModule(),"X3_RESTORE_HENCHMEN_LOCATIONS")) + { // restore location of henchmen belonging to henchmen + nN=1; + oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nN); + while(GetIsObjectValid(oHench)) + { // hench + oArea=GetArea(oHench); + bNoMounts=FALSE; + bNoMounting=FALSE; + if (!GetLocalInt(oArea,"X3_MOUNT_OK_EXCEPTION")) + { // find out if horses ok + if (GetLocalInt(GetModule(),"X3_MOUNTS_EXTERNAL_ONLY")&&GetIsAreaInterior(oArea)) bNoMounts=TRUE; + else if (GetLocalInt(GetModule(),"X3_MOUNTS_NO_UNDERGROUND")&&!GetIsAreaAboveGround(oArea)) bNoMounts=TRUE; + } // find out if horses ok + if (GetLocalInt(oArea,"X3_NO_MOUNTING")||GetLocalInt(oArea,"X3_NO_HORSES")||bNoMounts) bNoMounting=TRUE; + nC=1; + oAssoc=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nC); + while(GetIsObjectValid(oAssoc)) + { // check each associate + if (HorseGetIsMounted(oAssoc)&&(bNoMounts||bNoMounting)) + { // dismount + bRunAgain=TRUE; + DelayCommand(fDelay,AssignCommand(oAssoc,HORSE_SupportDismountWrapper(FALSE,TRUE))); + fDelay=fDelay+0.2; + } // dismount + else if (HorseGetIsAMount(oAssoc)&&bNoMounts) + { // no mounts + DelayCommand(fDelay,RemoveHenchman(oHench,oAssoc)); + DelayCommand(fDelay+0.01,SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,oAssoc)); + fDelay=fDelay+0.2; + } // no mounts + else if (GetArea(oAssoc)!=oArea) + { // jump + AssignCommand(oAssoc,ClearAllActions(TRUE)); + AssignCommand(oAssoc,ActionJumpToObject(oHench)); + } // jump + nC++; + oAssoc=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nC); + } // check each associate + nN++; + oHench=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nN); + } // hench + nN=1; + oHench=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC,oPC,nN); + while(GetIsObjectValid(oHench)) + { // test non-henchmen + if (!GetIsObjectValid(GetMaster(oHench))||GetMaster(oHench)==oHench) + { // test associates + oArea=GetArea(oHench); + bNoMounts=FALSE; + bNoMounting=FALSE; + if (!GetLocalInt(oArea,"X3_MOUNT_OK_EXCEPTION")) + { // find out if horses ok + if (GetLocalInt(GetModule(),"X3_MOUNTS_EXTERNAL_ONLY")&&GetIsAreaInterior(oArea)) bNoMounts=TRUE; + else if (GetLocalInt(GetModule(),"X3_MOUNTS_NO_UNDERGROUND")&&!GetIsAreaAboveGround(oArea)) bNoMounts=TRUE; + } // find out if horses ok + if (GetLocalInt(oArea,"X3_NO_MOUNTING")||GetLocalInt(oArea,"X3_NO_HORSES")||bNoMounts) bNoMounting=TRUE; + nC=1; + oAssoc=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nC); + while(GetIsObjectValid(oAssoc)) + { // check each associate + if (HorseGetIsMounted(oAssoc)&&(bNoMounts||bNoMounting)) + { // dismount + bRunAgain=TRUE; + DelayCommand(fDelay,AssignCommand(oAssoc,HORSE_SupportDismountWrapper(FALSE,TRUE))); + fDelay=fDelay+0.2; + } // dismount + else if (HorseGetIsAMount(oAssoc)&&bNoMounts) + { // no mounts + DelayCommand(fDelay,RemoveHenchman(oHench,oAssoc)); + DelayCommand(fDelay+0.01,SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,oAssoc)); + fDelay=fDelay+0.2; + } // no mounts + else if (GetArea(oAssoc)!=oArea) + { // jump + AssignCommand(oAssoc,ClearAllActions(TRUE)); + AssignCommand(oAssoc,ActionJumpToObject(oHench)); + } // jump + nC++; + oAssoc=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nC); + } // check each associate + } // test associates + nN++; + oHench=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC,oPC,nN); + } // test non-henchmen + if (bRunAgain) DelayCommand(5.0+fDelay,HorseRestoreHenchmenLocations(oPC)); + } // restore location of henchmen belonging to henchmen +} // HorseRestoreHenchmenLocations() + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// TRANSITIONS //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +void HorseHitchHorses(object oHitch,object oClicker,location lPreJump) +{ // PURPOSE: Hitch all associates to clicker + object oOb; + object oHench; + int nN=1; + int nNN; + int bNotAllowed=FALSE; + float fX3_MOUNT_MULTIPLE=GetLocalFloat(GetArea(oClicker),"fX3_MOUNT_MULTIPLE"); + float fX3_DISMOUNT_MULTIPLE=GetLocalFloat(GetArea(oClicker),"fX3_DISMOUNT_MULTIPLE"); + if (GetLocalFloat(oClicker,"fX3_MOUNT_MULTIPLE")>fX3_MOUNT_MULTIPLE) fX3_MOUNT_MULTIPLE=GetLocalFloat(oClicker,"fX3_MOUNT_MULTIPLE"); + if (fX3_MOUNT_MULTIPLE<=0.0) fX3_MOUNT_MULTIPLE=1.0; + if (GetLocalFloat(oClicker,"fX3_DISMOUNT_MULTIPLE")>0.0) fX3_DISMOUNT_MULTIPLE=GetLocalFloat(oClicker,"fX3_DISMOUNT_MULTIPLE"); + if (fX3_DISMOUNT_MULTIPLE>0.0) fX3_MOUNT_MULTIPLE=fX3_DISMOUNT_MULTIPLE; // use dismount multiple instead of mount multiple + float fDelay=0.2*fX3_MOUNT_MULTIPLE; + oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oClicker,nN); + while(GetIsObjectValid(oOb)) + { // traverse henchmen + if (HorseGetIsAMount(oOb)&&(!GetLocalInt(oOb,"X3_DOING_HORSE_ACTION"))) + { // is a mount + bNotAllowed=TRUE; + if (GetStringLeft(GetResRef(oOb),GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) SetLocalObject(oClicker,"oX3PaladinMount",oOb); + if (GetIsObjectValid(oHitch)&&GetDistanceBetween(oClicker,oHitch)<15.0) + { // jump to hitch + DelayCommand(0.1,RemoveHenchman(oClicker,oOb)); + AssignCommand(oOb,ClearAllActions(TRUE)); + //AssignCommand(oOb,ActionWait(X3_ACTION_DELAY/2*fX3_MOUNT_MULTIPLE)); + AssignCommand(oOb,ActionJumpToObject(oHitch)); + AssignCommand(oOb,ActionDoCommand(SetFacingPoint(GetPosition(oHitch)))); + SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,oOb); + } // jump to hitch + else + { // stand where you are and make way + DelayCommand(0.1,RemoveHenchman(oClicker,oOb)); + SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,oOb); + AssignCommand(oOb,ClearAllActions(TRUE)); + if (GetDistanceBetween(oOb,oClicker)<3.0) DelayCommand(1.0*fX3_MOUNT_MULTIPLE,AssignCommand(oOb,ActionMoveAwayFromLocation(lPreJump,FALSE,3.0+IntToFloat(Random(15))/10.0))); + else if (GetDistanceBetween(oOb,oClicker)>4.0) DelayCommand(1.0*fX3_MOUNT_MULTIPLE,AssignCommand(oOb,ActionMoveToLocation(GetBehindLocation(oClicker)))); + } // stand where you are and make way + } // is a mount + else + { // check for mounts for this henchman + oHench=oOb; + nNN=1; + oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nNN); + while(GetIsObjectValid(oOb)) + { // traverse henchmen + if (HorseGetIsAMount(oOb)&&(!GetLocalInt(oOb,"X3_DOING_HORSE_ACTION"))) + { // is a mount + bNotAllowed=TRUE; + if (GetStringLeft(GetResRef(oOb),GetStringLength(HORSE_PALADIN_PREFIX))==HORSE_PALADIN_PREFIX) SetLocalObject(oHench,"oX3PaladinMount",oOb); + if (GetIsObjectValid(oHitch)&&GetDistanceBetween(oClicker,oHitch)<15.0) + { // jump to hitch + DelayCommand(0.1,RemoveHenchman(oHench,oOb)); + AssignCommand(oOb,ClearAllActions(TRUE)); + //AssignCommand(oOb,ActionWait(X3_ACTION_DELAY/2*fX3_MOUNT_MULTIPLE)); + AssignCommand(oOb,ActionJumpToObject(oHitch)); + AssignCommand(oOb,ActionDoCommand(SetFacingPoint(GetPosition(oHitch)))); + SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,oOb); + } // jump to hitch + else + { // stand where you are and make way + DelayCommand(0.1,RemoveHenchman(oHench,oOb)); + AssignCommand(oOb,ClearAllActions(TRUE)); + SetAssociateState(NW_ASC_MODE_STAND_GROUND,TRUE,oOb); + fDelay+=0.2*fX3_MOUNT_MULTIPLE; + //if (GetDistanceBetween(oOb,oClicker)<4.0) DelayCommand(fDelay,AssignCommand(oOb,ActionMoveAwayFromLocation(lPreJump,FALSE,4.0+IntToFloat(Random(15))/10.0))); + if (GetDistanceBetween(oOb,OBJECT_SELF)<4.0) DelayCommand(fDelay,AssignCommand(oOb,ActionMoveAwayFromLocation(lPreJump,FALSE,4.0+IntToFloat(Random(15))/10.0))); + //else if (GetDistanceBetween(oOb,oClicker)>5.0) DelayCommand(fDelay,AssignCommand(oOb,ActionMoveToLocation(GetBehindLocation(oClicker)))); + else if (GetDistanceBetween(oOb,OBJECT_SELF)>5.0) DelayCommand(fDelay,AssignCommand(oOb,ActionMoveToObject(OBJECT_SELF,FALSE,4.0+IntToFloat(Random(15))/10.0))); + //SendMessageToPC(oClicker, FloatToString(GetDistanceBetween(oOb,OBJECT_SELF))); + } // stand where you are and make way + } // is a mount + nNN++; + oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nNN); + } // traverse henchmen + } // check for mounts for this henchman + nN++; + oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oClicker,nN); + } // traverse henchmen + if (bNotAllowed) SendMessageToPCByStrRef(oClicker,111990); +} // HorseHitchHorses() + + +void HorseForceJump(object oJumper,object oDestination,float fRange=2.0,int nTimeOut=10) +{ // PURPOSE: Make sure jump + //SendMessageToPC(oJumper,"nw_g0_transition:ForceJump("+IntToString(nTimeOut)+")"); + if (nTimeOut>0&&(GetArea(oJumper)!=GetArea(oDestination)||GetDistanceBetween(oJumper,oDestination)>fRange)) + { // jump + AssignCommand(oJumper,ClearAllActions(TRUE)); + AssignCommand(oJumper,ActionJumpToObject(oDestination)); + DelayCommand(1.0,HorseForceJump(oJumper,oDestination,fRange,nTimeOut-1)); + } // jump +} // HorseForceJump() + + +void HorseMoveAssociates(object oMaster) +{ // PURPOSE: Give the PC some breathing room + int nN,nNN; + float fDelay=0.2; + object oHench,oAssociate,oSummon; + nN=1; + oAssociate=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oMaster,nN); + while(GetIsObjectValid(oAssociate)) + { // move associates + if (HorseGetIsAMount(oAssociate)||HorseGetIsMounted(oAssociate)) + { // only move mounts or mounted associates + if (GetArea(oAssociate)==GetArea(oMaster)&&GetDistanceBetween(oMaster,oAssociate)<3.0) + { // move away + AssignCommand(oAssociate,ClearAllActions()); + DelayCommand(1.0,AssignCommand(oAssociate,ActionMoveAwayFromObject(oMaster,FALSE,3.0+IntToFloat(Random(10))/10.0))); + } // move away + } // only move mounts or mounted associates + else + { // henchmen of henchman + oHench=oAssociate; + nNN=1; + oAssociate=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nNN); + while(GetIsObjectValid(oAssociate)) + { // traverse henchmen + if (HorseGetIsAMount(oAssociate)||HorseGetIsMounted(oAssociate)) + { // only move mounts or mounted associates + if (GetArea(oAssociate)==GetArea(oMaster)&&GetDistanceBetween(oMaster,oAssociate)<3.0) + { // move away + fDelay+=0.2; + AssignCommand(oAssociate,ClearAllActions()); + DelayCommand(fDelay,AssignCommand(oAssociate,ActionMoveAwayFromObject(oMaster,FALSE,3.0+IntToFloat(Random(15))/10.0))); + } // move away + } // only move mounts or mounted associates + nNN++; + oAssociate=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oHench,nNN); + oSummon=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oHench,nNN); + } // traverse henchmen + nNN=1; + oSummon=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oHench,nNN); + while(GetIsObjectValid(oSummon)) + { // traverse summons + if (HorseGetIsAMount(oSummon)||HorseGetIsMounted(oSummon)) + { // only move mounts or mounted associates + if (GetArea(oSummon)==GetArea(oMaster)&&GetDistanceBetween(oMaster,oSummon)<3.0) + { // move away + fDelay+=0.2; + AssignCommand(oSummon,ClearAllActions()); + DelayCommand(fDelay,AssignCommand(oSummon,ActionMoveAwayFromObject(oMaster,FALSE,3.0+IntToFloat(Random(15))/10.0))); + } // move away + } // only move mounts or mounted associates + nNN++; + oSummon=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oHench,nNN); + } // traverse summons + } // henchmen of henchman + nN++; + oAssociate=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oMaster,nN); + } // move associates + nN=1; + oAssociate=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oMaster,nN); + while(GetIsObjectValid(oAssociate)) + { // move associates + if (HorseGetIsAMount(oAssociate)||HorseGetIsMounted(oAssociate)) + { // only move mounts or mounted associates + if (GetArea(oAssociate)==GetArea(oMaster)&&GetDistanceBetween(oMaster,oAssociate)<3.0) + { // move away + AssignCommand(oAssociate,ClearAllActions()); + DelayCommand(1.0,AssignCommand(oAssociate,ActionMoveAwayFromObject(oMaster,FALSE,3.0+IntToFloat(Random(10))/10.0))); + } // move away + } // only move mounts or mounted associates + nN++; + oAssociate=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oMaster,nN); + } // move associates +} // HorseMoveAssociates() + + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////// DEATH HANDLING /////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +void HorseDismountWrapper() +{ // Wrap + object oNPC=OBJECT_SELF; + if (HorseGetIsMounted(oNPC)) + { // make sure dismount + DelayCommand(0.1,HORSE_SupportResetUnmountedAppearance(oNPC)); + DelayCommand(0.8,HORSE_SupportCleanVariables(oNPC)); + DelayCommand(1.0,HORSE_SupportRemoveACBonus(oNPC)); + DelayCommand(1.0,HORSE_SupportRemoveHPBonus(oNPC)); + DelayCommand(1.0,HORSE_SupportRemoveMountedSkillDecreases(oNPC)); + DelayCommand(1.0,HORSE_SupportAdjustMountedArcheryPenalty(oNPC)); + DelayCommand(1.0,HORSE_SupportOriginalSpeed(oNPC)); + DelayCommand(2.0,HorseDismountWrapper()); + } // make sure dismount +} // HorseDismountWrapper() + + +void HorseReassign(object oHorse,object oHench,object oMaster) +{ // PURPOSE: To handle horse reassign + if (GetIsObjectValid(oMaster)&&oMaster!=oHench) + { // reassign + HorseSetOwner(oHorse,oMaster); + DeleteLocalObject(oHench,"oAssignedHorse"); + } // reassign + else + { // free + HorseRemoveOwner(oHorse); + DeleteLocalObject(oHench,"oAssignedHorse"); + } // free +} // HorseReassign() + + +int HorseHandleDeath() +{ // PURPOSE: Handle horses, re-assigning dying henchman's horse, handle mounted henchman's death (dismount, transfer saddlebag content to horse, re-assign horse to PC or free it) + object oDied=OBJECT_SELF; + object oHorse=HorseGetHorse(oDied); + object oMaster=GetMaster(oDied); + if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsObjectValid(GetMaster(oDied))) SetLocalInt(GetMaster(oDied),"bX3_STORE_MOUNT_INFO",TRUE); + if (HorseGetIsAMount(oDied)) + { // check dying horse for saddlebags and eventual re-assigning to master + if (GetLocalInt(GetModule(),"X3_HORSE_ENABLE_SADDLEBAGS")&&GetLocalInt(oDied,"bX3_HAS_SADDLEBAGS")) + { // might have saddle bags + object oOwner=HorseGetOwner(oDied); + if (!GetIsPC(oOwner)&&GetIsObjectValid(oOwner)&&oOwner!=oDied) + { // npc + oOwner=GetMaster(oOwner); + if (GetIsPC(oOwner)) + { // set to PC then drop + HorseSetOwner(oDied,oOwner); + } // set to PC then drop + } // npc + else if (GetIsPC(oOwner)) + { // PC + } // PC + DeleteLocalInt(oDied,"bX3_HAS_SADDLEBAGS"); // if you don't want to remove saddlebags for horse resurrection purpose, make sure that the corpse's inventory content is moved to the resurrected living horse again (if you do that, you can possibly remove the check for saddlebags including the repeated call of the death script) + DelayCommand(0.1,ExecuteScript(GetLocalString(oDied,"sX3_DEATH_SCRIPT"),oDied)); + return TRUE; // no resurrection for horses by default + } // might have saddle bags + return TRUE; // horse should not respawn like other henchmen + } // check dying horse for saddlebags and eventual re-assigning to master + if (GetIsObjectValid(oHorse)) HorseReassign(oHorse,oDied,GetMaster(oDied)); // check henchman's horses for possible re-assigning to master or free them + // * Bob Minors Jan 2008: added support for X3 mounts: + // * If killed while mounted, we are dismounted. This takes about 0.5s so drop out and rerun this script again afterward + if (HorseGetIsMounted(oDied)) + { // check if creature died in mounted state + oHorse=HorseDismount(FALSE,TRUE); + SetLocalObject(oDied,"oAssignedHorse",oHorse); // Kludge + HorseReassign(oHorse,oDied,oMaster); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectKnockdown(),oHorse,1.8); + DelayCommand(2.0,AssignCommand(oHorse,ActionMoveAwayFromObject(oDied,TRUE,3.0))); // drop rider and jump aside + DelayCommand(1.2,ExecuteScript(GetLocalString(oDied,"sX3_DEATH_SCRIPT"),oDied)); // give dismount and potential inventory transfer some space before rerun + //return TRUE; // this return makes the dead mounted henchman non-resurrectable, so its commented out so that they can be resurrected by default + } // check if creature died in mounted state +return FALSE; +} //HorseHandleDeath() + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////// PRELOAD ANIMATIONS ///////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +void HorsePreloadAnimations(object oPC) +{ // PURPOSE: Force a preload of mount animations + int nApp=GetAppearanceType(oPC); + int nTail=GetCreatureTailType(oPC); + //SetCreatureAppearanceType(oPC,HORSE_SupportGetMountedAppearance(oPC)); + if (nTail==0) nTail=14; + HORSE_SupportRestoreFromPreload(oPC,nApp,nTail); +} // HorsePreloadAnimations() + + +//void main(){} diff --git a/src/include/xchst_inc.nss b/src/include/xchst_inc.nss new file mode 100644 index 0000000..cd948df --- /dev/null +++ b/src/include/xchst_inc.nss @@ -0,0 +1,95 @@ +#include "inc_utility" +#include "inc_sql" +#include "prc_inc_chat" + +const string XCHST_PLC = "xchst_plc"; +const string XCHST_CONT = "xchst_cont"; +//const string XCHST_DB = "xchst_db"; +const string XCHST_OWN = "xchst_owner"; +const string XCHST_ID = "xchst_id"; + +// return database ID from oTarget +string GetOwnerID(object oPC) +{ + string sID = GetPCPublicCDKey(oPC, TRUE); + sID += "#" + GetName(oPC); + return GetSubString(sID, 0, 20); +} + +location MoveLocation(location lCurrent,float fDirection,float fDistance,float fOffFacing = 0.0,float fOffZ = 0.0f) +{ + vector vPos = GetPositionFromLocation(lCurrent); + float fFace = GetFacingFromLocation(lCurrent); + float fNewX = vPos.x + (fDistance * cos(fFace + fDirection)); + float fNewY = vPos.y + (fDistance * sin(fFace + fDirection)); + float fNewZ = vPos.z + (fOffZ); + vector vNewPos = Vector(fNewX,fNewY,fNewZ); + location lNewLoc = Location(GetAreaFromLocation(lCurrent),vNewPos,fFace + fOffFacing); + return lNewLoc; +} + +object CreateChest(object oOwner, location lTarget, string sID) +{ + //Create the item container first + object oChest; + if(GetPRCSwitch(PRC_USE_DATABASE)) + oChest = PRC_GetPersistentObject(oOwner, sID, OBJECT_INVALID, XCHST_DB); + else + oChest = RetrieveCampaignObject(XCHST_DB, sID, lTarget); + + if(!GetIsObjectValid(oChest)) + oChest = CreateObject(OBJECT_TYPE_CREATURE, XCHST_CONT, lTarget, FALSE); + + effect eLink = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + eLink = EffectLinkEffects(eLink, EffectCutsceneImmobilize()); + eLink = EffectLinkEffects(eLink, EffectCutsceneGhost()); + eLink = EffectLinkEffects(eLink, EffectAreaOfEffect(185, "", "", "xchst_exit")); + eLink = SupernaturalEffect(eLink); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oChest); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectEthereal()), oChest); + + //now the placeable + object oPLC = CreateObject(OBJECT_TYPE_PLACEABLE, XCHST_PLC, lTarget, FALSE); + effect eVis = SupernaturalEffect(EffectVisualEffect(VFX_DUR_PROT_SHADOW_ARMOR)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eVis, oPLC); + + SetLocalObject(oChest, XCHST_PLC, oPLC); + SetLocalObject(oChest, XCHST_OWN, oOwner); + SetLocalString(oChest, XCHST_ID, sID); + //bind the chest to pc and placeable object + SetLocalObject(oOwner, XCHST_CONT, oChest); + SetLocalObject(oPLC, XCHST_CONT, oChest); + + return oChest; +} + +void SummonChest(object oPC) +{ + location lNewLoc = MoveLocation(GetLocation(oPC), 0.0, 1.5, 0.0, 0.0); + CreateChest(oPC, lNewLoc, GetOwnerID(oPC)); + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), lNewLoc); +} + +void DismissChest(object oChest) +{ + object oPC = GetLocalObject(oChest, XCHST_OWN); + object oPLC = GetLocalObject(oChest, XCHST_PLC); + string sOwnerID = GetLocalString(oChest, XCHST_ID); + + AssignCommand(oChest, ClearAllActions(TRUE)); + RemoveHenchman(oPC, oChest); + DeleteLocalObject(oPC, XCHST_CONT); + + if(GetPRCSwitch(PRC_USE_DATABASE)) + PRC_SetPersistentObject(oPC, sOwnerID, oChest, 0, XCHST_DB); + else + StoreCampaignObject(XCHST_DB, sOwnerID, oChest); + + // if not in single player mode - export character + if(GetPCPublicCDKey(oPC) != "") + ExportSingleCharacter(oPC); + + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_UNSUMMON), GetLocation(oPLC)); + MyDestroyObject(oChest); + MyDestroyObject(oPLC); +} \ No newline at end of file diff --git a/unpack_haks.cmd b/unpack_haks.cmd new file mode 100644 index 0000000..a4401c0 --- /dev/null +++ b/unpack_haks.cmd @@ -0,0 +1 @@ +nasher unpack tophak --verbose --removeDeleted:false \ No newline at end of file diff --git a/unpack_module.cmd b/unpack_module.cmd new file mode 100644 index 0000000..faad5fb --- /dev/null +++ b/unpack_module.cmd @@ -0,0 +1 @@ +nasher unpack default --verbose --removeDeleted:false \ No newline at end of file diff --git a/update_src_from_haks.cmd b/update_src_from_haks.cmd new file mode 100644 index 0000000..dd1d285 --- /dev/null +++ b/update_src_from_haks.cmd @@ -0,0 +1 @@ +nasher unpack tophak --verbose -y \ No newline at end of file diff --git a/update_src_from_module.cmd b/update_src_from_module.cmd new file mode 100644 index 0000000..44d2155 --- /dev/null +++ b/update_src_from_module.cmd @@ -0,0 +1 @@ +nasher unpack default --verbose -y \ No newline at end of file