/*
   ----------------
   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);
    }
}
