Author Topic: Avoiding Bonus Spell Slot Loss When Shifting?  (Read 562 times)

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« on: October 26, 2013, 03:12:30 am »


               Pretty much what the title says.  I have a workaround to avoid spell slot loss due to losing casting stat (that said, is there a way to fire a script when an effect ends, in this case a polymorph?  Would be cleaner) but I'm driving myself crazy trying to figure out bonus spell slots.

1, if I add bonus spell slots to the new polymorph skin, it still loses the slots because the slots vanish for a second and erase the memorized spells

2, if I add bonus spell slots to the character's skin itself it simply doesn't apply while shifted.

3, if I add bonus spell slots to both it doesn't work either because, again, the slots vanish for a moment and erase the memorized spells

Anyone found a good way around these issues?
               
               

               


                     Modifié par MagicalMaster, 26 octobre 2013 - 02:44 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #1 on: October 26, 2013, 12:33:02 pm »


               

MagicalMaster wrote...

(that said, is there a way to fire a script when an effect ends, in this case a polymorph?  Would be cleaner)

yes check this thread or complete solution that does this and much more - promoted in Community Patch thread.

as for the slots from items issue, I dont think there is a way to workaroud it without NWNX, with nwnx however there are several choices:

1) apply effect spell slot bonus instead of itemproperty
2) save and restore memorized slots before/after polymorph
3) edit polymorph so it does not unequip items
1,2 are possible to do with public plugins for both windows and linux, 3 is not public but I think Ive seen this somewhere maybe sinfar
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #2 on: October 26, 2013, 07:59:14 pm »


               I can't use any solution that requires an override or hak (or won't in this case, at least -- Siege of the Heavens single player module with vanilla content) and that complete solution came with overrides.  Are those overrides needed for that solution?

I'll probably fiddle around with that equip/unequip idea.  But I already have a less-elegant but working solution so we'll see how it goes.

And can't use NWNX in this scenario.  Sounds like I might just need to give up on preserving bonus spell slots from gear when shifting.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #3 on: October 26, 2013, 08:15:03 pm »


               

MagicalMaster wrote...

I can't use any solution that requires an override or hak (or won't in this case, at least -- Siege of the Heavens single player module with vanilla content) and that complete solution came with overrides.  Are those overrides needed for that solution?

nope that polymorph.2da is only needed to fix temporary hitpoints replenishing and the package works without it - it uses the equip/unequip

its designed to be part of my unofficial hak in future, so there are extra functionalities you can turn on/off more info in my documentation but if you know scripting you can read what to set inside the scripts (libraries) yourself

if you have more questions, pm me
               
               

               


                     Modifié par ShaDoOoW, 26 octobre 2013 - 07:15 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #4 on: October 26, 2013, 09:07:40 pm »


               Yeah, I already started pulling it apart and learned something interesting things (I was wondering how you retrieved the actual Str/Dex/etc value from an item).  Unfortunately I've already modified my shapeshifting scripts a bit so I won't be able to use your package as is, I'll need to pull relevant things out.

Does your package fix the bug where dying in Shapeshift form effectively removes all benefits from gear from the character until re-equipped or polymorphed again?  Was going to try to simply re-Polymorph and un-Polymorph the player when they come back to life to fix it but I'm not sure if there's a better solution.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #5 on: October 26, 2013, 09:59:13 pm »


               

MagicalMaster wrote...

Does your package fix the bug where dying in Shapeshift form effectively removes all benefits from gear from the character until re-equipped or polymorphed again?  Was going to try to simply re-Polymorph and un-Polymorph the player when they come back to life to fix it but I'm not sure if there's a better solution.

nope

pvp servers i know solves this via reequip all items option for player - but i got beter idea though untested but imo worth the shot

try removing and readding all itemproperties on all items
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #6 on: October 27, 2013, 01:06:19 am »


               Seems more complicated than just repolymorphing them for half a second.
               
               

               
            

Legacy_HipMaestro

  • Hero Member
  • *****
  • Posts: 2849
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #7 on: October 27, 2013, 01:32:49 am »


               

MagicalMaster wrote...
Seems more complicated than just repolymorphing them for half a second.

You may not need to repolymorph them at all, MM.  Consider...

Suppose you checked to see if/when the default polymorph script fires and then substitute your own polymorph script.  I know this has been done in vanilla because HotU contains these "wild magic" areas in one of the modules that produces some odd consequences, strange spell effects that are triggered.  

Then, once the default polymorph script has been neutralized, instead of your custom script calling on one of the default polymorph blueprints, call up a modified one that has adequate WIS and INT levels that would not wipe out the bous slots.  I don't think CHA-based spellcasting has this problem, does it? You may need to also add sufficent spellcaster levels to the blueprints so that the slots would be on the polymorphs, but they would, in essence be replicant versions of the class entering the area.  Not sure how you would handle players wearing equipment that provides bonus slots peripheral to the ability modifier, though.  

Trying to brainstorm a vanilla solution to this problem, folks.  The blind squirrel hoping to find a nut.
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #8 on: October 27, 2013, 01:40:24 am »


               

HipMaestro wrote...

You may not need to repolymorph them at all, MM.  Consider...


The repolymorphing is referring to the bug where items stop giving stats if you die wihle polymorphed, not the bonus spell slots

HipMaestro wrote...

Then, once the default polymorph script has been neutralized, instead of your custom script calling on one of the default polymorph blueprints, call up a modified one that has adequate WIS and INT levels that would not wipe out the bous slots.


Unfortunately, this would require modifying polymorph.2da since you'd need like 90+ wisdom (40+ modifier).

HipMaestro wrote...

I don't think CHA-based spellcasting has this problem, does it?


It does but you don't have to fix the spellbook each time so it's less of a big deal -- you lose the spells but don't have to mess with the spellbook each time.

HipMaestro wrote...

Not sure how you would handle players wearing equipment that provides bonus slots peripheral to the ability modifier, though.


It's the bonus slots that are the big issue '<img'>  There's a number of workarounds for the ability modifier.  Bonus slots are much harder.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #9 on: October 27, 2013, 01:44:14 am »


               polymorph doesnt alter wis, int, cha
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #10 on: October 27, 2013, 03:27:00 am »


               Oh, I see -- I knew Mindflayer increased Int and Cha but I didn't realize it was a scripted bonus rather than actually increasing the value.  So yeah, editing the 2da wouldn't help.
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #11 on: October 27, 2013, 08:05:56 pm »


               Technically the int and wis bonuses from mindflayer are from item properties on the skin. (Inhibiting other int/wis bonuses from being merged).
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #12 on: October 27, 2013, 08:17:44 pm »


               Oh, even better.  This is why I hate shifting in NWN : /
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #13 on: October 28, 2013, 04:20:49 am »


               We, after many attempts resolved the issue (losing item properties after dying while shifted) in Av3.  After digging I can't seem to pin it down (10+ years of development from 100+ developers will do that.)
Genisys however does have some work toward this aim I believe. And in fact we use some of it (not sure if this was the ultimate fix, but I think it is, or lead to it.) If you search the vault for Genisys I bet you can find the relevant scripts.
This is what we have, and sadly I can not tell you if this is all inclusive, or alone the final fix:
//Created by Genisys 5/4/08
//This OnActivateItem script works off the resref name of the items equipped.
//It allows those polymorphed who die to unequip all items and equip them again
//with just the use of this item. (Believe me I know how frustrating that is!)
#include "x2_inc_switches"
void main ()
{
    object oPC;
    oPC = GetItemActivator();
   AssignCommand(oPC, ClearAllActions());
if ((GetItemInSlot(INVENTORY_SLOT_NECK, oPC) != OBJECT_INVALID))
   {
    object oItem1;
    oItem1 = GetItemInSlot(INVENTORY_SLOT_NECK, oPC);
    string aTag;
    aTag = GetResRef(oItem1);
   if(aTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_NECK, oPC)));
   DelayCommand(2.0, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, aTag), INVENTORY_SLOT_NECK)));
   }
   }
 if ((GetItemInSlot(INVENTORY_SLOT_BELT, oPC) != OBJECT_INVALID))
   {
    object oItem2;
    oItem2 = GetItemInSlot(INVENTORY_SLOT_BELT, oPC);
    string bTag;
    bTag = GetResRef(oItem2);
   if(bTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_BELT, oPC)));
   DelayCommand(2.1, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, bTag), INVENTORY_SLOT_BELT)));
   }
   }

    if ((GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC) != OBJECT_INVALID))
   {
    object oItem3;
    oItem3 = GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC);
    string cTag;
    cTag = GetResRef(oItem3);
   if(cTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC)));
   DelayCommand(2.3, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, cTag), INVENTORY_SLOT_BOOTS)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_CHEST, oPC) != OBJECT_INVALID))
   {
    object oItem4;
    oItem4 = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC);
    string dTag;
    dTag = GetResRef(oItem4);
   if(dTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC)));
   DelayCommand(2.4, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, dTag), INVENTORY_SLOT_CHEST)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC) != OBJECT_INVALID))
   {
    object oItem5;
    oItem5 = GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC);
    string eTag;
    eTag = GetResRef(oItem5);
   if(eTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC)));
   DelayCommand(2.5, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, eTag), INVENTORY_SLOT_CLOAK)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_HEAD, oPC) != OBJECT_INVALID))
   {
    object oItem6;
    oItem6 = GetItemInSlot(INVENTORY_SLOT_HEAD, oPC);
    string fTag;
    fTag = GetResRef(oItem6);
   if(fTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_HEAD, oPC)));
   DelayCommand(2.6, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, fTag), INVENTORY_SLOT_HEAD)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC) != OBJECT_INVALID))
   {
    object oItem7;
    oItem7 = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
    string gTag;
    gTag = GetResRef(oItem7);
   if(gTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC)));
   DelayCommand(2.7, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, gTag), INVENTORY_SLOT_LEFTHAND)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC) != OBJECT_INVALID))
   {
    object oItem8;
    oItem8 = GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC);
    string hTag;
    hTag = GetResRef(oItem8);
   if(hTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC)));
   DelayCommand(2.8, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, hTag), INVENTORY_SLOT_LEFTRING)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) != OBJECT_INVALID))
   {
    object oItem9;
    oItem9 = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
    string jTag;
    jTag = GetResRef(oItem9);
   if(jTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC)));
   DelayCommand(3.0, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, jTag), INVENTORY_SLOT_RIGHTHAND)));
   }
   }
    if ((GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC) != OBJECT_INVALID))
   {
    object oItem11;
    oItem11 = GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC);
    string kTag;
    kTag = GetResRef(oItem11);
   if(kTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC)));
   DelayCommand(3.2, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, kTag), INVENTORY_SLOT_RIGHTRING)));
   }
   }
       if ((GetItemInSlot(INVENTORY_SLOT_ARMS, oPC) != OBJECT_INVALID))
   {
    object oItem12;
    oItem12 = GetItemInSlot(INVENTORY_SLOT_ARMS, oPC);
    string lTag;
    lTag = GetResRef(oItem12);
   if(lTag != "")
   {
   AssignCommand(oPC, ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_ARMS, oPC)));
   DelayCommand(3.4, AssignCommand(oPC, ActionEquipItem(GetItemPossessedBy(oPC, lTag), INVENTORY_SLOT_ARMS)));
   }
   }
//The End WOOSH!
}
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Avoiding Bonus Spell Slot Loss When Shifting?
« Reply #14 on: October 28, 2013, 04:21:50 am »


               //:://////////////////////////////
//:: Created By: Iznoghoud
//:: Last modified: January 19 2004
/*
What this script changes:
- Melee Weapon properties now carry over to the unarmed forms' claws and bite
attacks.
1) Now, items with an AC bonus (or penalty) carry over to the shifted form as
the correct type. This means if you wear an amulet of natural armor +4, and a
cloak of protection +5, and you shift to a form that gets all item properties
carried over, you will have the +4 natural armor bonus from the ammy, as well as
the +5 deflection bonus from the cloak. No longer will the highest one override
all the other AC bonuses even if they are a different type.
2) Other "stackable" item properties, like ability bonuses, skill bonuses and
saving throw bonuses, now correctly add up in shifted form. This means if you
have a ring that gives +2 strength, and a ring with +3 strength, and you shift
into a drow warrior, you get +5 strength in shifted form, where you used to get
only +3. (the highest)

This file contains the code that handles stacking item properties for the improved
Shifter and Druid wildshape scripts.
1 February 2004:
Added an option to allow or disallow AC stacking of different types.
*/
//:://////////////////////////////
//******** Begin Options *********
//***************** GENERAL OPTIONS *********************
// Set this to TRUE to allow differing types of AC bonuses on items to stack.
// (ie armor, deflection, natural) Warning: This can give shifters who multiclass
// with monk a godly AC depending on your module.
// With FALSE, AC will transfer as it did with the default Bioware shifter script.
const int GW_ALLOW_AC_STACKING = TRUE;
//***************** FOR SHIFTER SHAPES ******************
// Set this to TRUE to merge properties of boots/rings/ammy/cloak/bracers regardless
// of what polymorph.2da indicates.
// FALSE uses the polymorph.2da to decide whether to copy
const int GW_ALWAYS_COPY_ITEM_PROPS = TRUE;//FALSE;
// Set this to TRUE to merge armor/helmet properties regardless of what polymorph.2da indicates.
// FALSE uses the polymorph.2da to decide whether to copy
const int GW_ALWAYS_COPY_ARMOR_PROPS = TRUE;
// - Set this to 1 to copy over weapon properties to claw/bite attacks.
// - Set this to 2 to copy over glove properties to claw/bite attacks.
// - Set this to 3 to copy over from either weapon or gloves depending on whether a
//   weapon was worn at the time of shifting.
// - Set this to any other value ( eg 0 ) to not copy over anything to claw/bite attacks.
const int GW_COPY_WEAPON_PROPS_TO_UNARMED = 3;

//***************** FOR DRUID SHAPES ********************
// These options do nothing if you have not imported the improved Druid wild-
// and elemental shape scripts
// Set this to TRUE to merge properties of boots/rings/ammy/cloak/bracers regardless
// of what polymorph.2da indicates.
// FALSE uses the polymorph.2da to decide whether to copy
const int WS_ALWAYS_COPY_ITEM_PROPS = TRUE;//FALSE;
// Set this to TRUE to merge armor/helmet properties regardless of what polymorph.2da indicates.
// FALSE uses the polymorph.2da to decide whether to copy
const int WS_ALWAYS_COPY_ARMOR_PROPS = TRUE;
// - Set this to 1 to copy over weapon properties to claw/bite attacks.
// - Set this to 2 to copy over glove properties to claw/bite attacks.
// - Set this to 3 to copy over from either weapon or gloves depending on whether a
//   weapon was worn at the time of shifting.
// - Set this to any other value ( eg 0 ) to not copy over anything to claw/bite attacks.
const int WS_COPY_WEAPON_PROPS_TO_UNARMED = 3;
//******** End Options ***********
// Includes for various shifter and item related functions
#include "x2_inc_itemprop"
#include "x2_inc_shifter"
// **** Begin Function prototypes ****
// Copies oOld's Properties to oNew, but only properties that do not stack
// with properties of the same type. If oOld is a weapon, then bWeapon must be TRUE.
void WildshapeCopyNonStackProperties(object oOld, object oNew, int bWeapon = FALSE);
// Returns TRUE if ip is an item property that will stack with other properties
// of the same type: Ability, AC, Saves, Skills.
int GetIsStackingProperty(itemproperty ip);
// Returns the AC bonus type of oItem: AC_*_BONUS
int GetItemACType(object oItem);
// Looks for Stackable Properties on oItem, and sets local variables to count the total bonus.
// Also links any found AC bonuses/penalties to ePoly.
effect ExamineStackableProperties (object oPC, effect ePoly, object oItem );
// if bItems is TRUE, Adds the stackable properties on all the objects given to ePoly.
// if bArmor is TRUE, Adds the stackable properties on armor and helmet to ePoly.
effect AddStackablePropertiesToPoly ( object oPC, effect ePoly, int bWeapon, int bItems, int bArmor, object oArmorOld, object oRing1Old,
                                      object oRing2Old, object oAmuletOld, object oCloakOld, object oBracerOld,
                                      object oBootsOld, object oBeltOld, object oHelmetOld, object oShield, object oWeapon, object oHideOld);
// Returns the spell that applied a Polymorph Effect currently on the player.
// -1 if it was no spell, -2 if no polymorph effect found.
int ScanForPolymorphEffect(object oPC);
// Converts a number from iprp_damagetype.2da to the corresponding
// DAMAGE_TYPE_* constants.
int ConvertNumToDamTypeConstant ( int iItemDamType );
// Converts a number from iprp_immuncost.2da to the corresponding percentage of immunity
int ConvertNumToImmunePercentage ( int iImmuneCost );
// Special function to copy over weapon properties, which deals with copying
// over ranged weapons correctly.
void WildshapeCopyWeaponProperties(object oPC, object oOld, object oNew);
// Returns TRUE if oItem is a creature claw or bite.
int GetIsCreatureWeapon( object oItem );
// **** End Function prototypes ****
// **** Begin Functions, added by Iznoghoud ****
// Copies oOld's Properties to oNew, but only properties that do not stack
// with properties of the same type. If oOld is a weapon, then bWeapon must be TRUE.
void WildshapeCopyNonStackProperties(object oOld, object oNew, int bWeapon = FALSE) {
    if (GetIsObjectValid(oOld) && GetIsObjectValid(oNew))
    {
        itemproperty ip = GetFirstItemProperty(oOld);
        while (GetIsItemPropertyValid(ip)) // Loop through all the item properties.
        {
            if (bWeapon) // If a weapon, then we must make sure not to transfer between ranged and non-ranged weapons!
            {
                if (GetWeaponRanged(oOld) == GetWeaponRanged(oNew) )
                {
                    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew);
                }
            }
            else
            {
                // If not a stacking property, copy over the property.
                // Dont copy on hit cast spell property unless the target is a claw/bite.
                if ( !GetIsStackingProperty(ip) && ( !(GetItemPropertyType(ip) == ITEM_PROPERTY_ONHITCASTSPELL) || GetIsCreatureWeapon(oNew) ) )
                    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew);
            }
            ip = GetNextItemProperty(oOld); // Get next property
        }
    }
}
// Returns TRUE if ip is an item property that will stack with other properties
// of the same type: Ability, AC, Saves, Skills.
int GetIsStackingProperty(itemproperty ip) {
    int iType = GetItemPropertyType(ip);
    if ( iType == 0 || ( GW_ALLOW_AC_STACKING && (iType == 1) ) ||     // Bonus to Ability, AC
         iType == 27 || ( GW_ALLOW_AC_STACKING && (iType == 28) ) ||   // Penalty to Ability, AC
           iType == 40 || iType == 41 || // Bonus to saves (against element/universal, or fort/reflex/will)
           iType == 49 || iType == 50 || // Penalty to saves (against element/universal, or fort/reflex/will)
           iType == 52 || iType == 29 || // Skill Bonus, Penalty
           iType == 51 ||                // Regeneration
           iType == 20 || iType == 24    // Damage Immunity and Vulnerability
       )
        return TRUE;
    else
        return FALSE;
}
// Returns the AC bonus type of oItem: AC_*_BONUS
int GetItemACType(object oItem) {
    switch(GetBaseItemType(oItem)) {
    case BASE_ITEM_ARMOR: // These item types always get an armor ac bonus
    case BASE_ITEM_BRACER:
        return AC_ARMOUR_ENCHANTMENT_BONUS;
        break;
    case BASE_ITEM_BELT: // These item types always get a deflection ac bonus.
    case BASE_ITEM_CLOAK:
    case BASE_ITEM_GLOVES: // Note that gloves and bracers equip in the same inventory slot,
    case BASE_ITEM_HELMET: // but do not give the same AC bonus type!!!
    case BASE_ITEM_RING:
    case BASE_ITEM_TORCH:
        return AC_DEFLECTION_BONUS;
        break;
    case BASE_ITEM_BOOTS: // Only boots give a dodge ac bonus
        return AC_DODGE_BONUS;
        break;
    case BASE_ITEM_AMULET: // Only amulets give a natural AC bonus
        return AC_NATURAL_BONUS;
        break;
    case BASE_ITEM_LARGESHIELD: // Shields give a shield AC bonus
    case BASE_ITEM_SMALLSHIELD:
    case BASE_ITEM_TOWERSHIELD:
        return AC_SHIELD_ENCHANTMENT_BONUS;
        break;
    default: // It was a weapon, or a non default item, safest to default to deflection
        return AC_DEFLECTION_BONUS;
        break;
    };
    return AC_DEFLECTION_BONUS; // This one would seem unneccesary but it won't compile otherwise.
}
// Looks for Stackable Properties on oItem, and sets local variables to count the total bonus.
// Also links any found AC bonuses/penalties to ePoly.
effect ExamineStackableProperties ( object oPC, effect ePoly, object oItem )
{
    if ( !GetIsObjectValid(oItem) ) // If not valid, dont do any unneccesary work.
        return ePoly;
    itemproperty ip = GetFirstItemProperty(oItem);
    int iSubType;
    effect eTemp;
    while ( GetIsItemPropertyValid(ip) ) // Loop through all the item properties
    {
        if ( GetIsStackingProperty(ip) ) // See if it's a stacking property
        {
            iSubType = GetItemPropertySubType(ip); // Get the item property subtype for later use.
                                            // This contains whether a bonus is str, dex,
                                            // concentration skill, universal saving throws, etc.
            switch ( GetItemPropertyType(ip) ) // Which type of property is it?
            {
                // In the case of AC modifiers, add it directly to the Polymorphing effect.
                // For the other cases, set local variables on the player to
                // make a sum of all the bonuses/penalties. We use local
                // variables here because there are no arrays in NWScript, and
                // declaring a variable for every skill, ability type and saving
                // throw type in here is a little overboard.
                case 0: // Ability Bonus
                    SetLocalInt(oPC, "ws_ability_" + IntToString(iSubType), GetLocalInt(oPC, "ws_ability_" + IntToString(iSubType)) + GetItemPropertyCostTableValue(ip) );
                    break;
                case 1: // AC Bonus
                    ePoly = EffectLinkEffects(EffectACIncrease(GetItemPropertyCostTableValue(ip),GetItemACType(oItem)), ePoly);
                    break;
                case 27: // Ability Penalty
                    SetLocalInt(oPC, "ws_ability_" + IntToString(iSubType), GetLocalInt(oPC, "ws_ability_" + IntToString(iSubType)) - GetItemPropertyCostTableValue(ip) );
                    break;
                case 28: // AC penalty
                    ePoly = EffectLinkEffects(EffectACDecrease(GetItemPropertyCostTableValue(ip)), ePoly);
                    break;
                case 52: // Skill Bonus
                    SetLocalInt(oPC, "ws_skill_" + IntToString(iSubType), GetLocalInt(oPC, "ws_skill_" + IntToString(iSubType)) + GetItemPropertyCostTableValue(ip) );
                    break;
                case 29: // Skill Penalty
                    SetLocalInt(oPC, "ws_skill_" + IntToString(iSubType), GetLocalInt(oPC, "ws_skill_" + IntToString(iSubType)) - GetItemPropertyCostTableValue(ip) );
                    break;
                case 40: // Saving Throw Bonus vs Element(or universal)
                    SetLocalInt(oPC, "ws_save_elem_" + IntToString(iSubType), GetLocalInt(oPC, "ws_save_elem_" + IntToString(iSubType)) + GetItemPropertyCostTableValue(ip) );
                    break;
                case 41: // Saving Throw Bonus specific (fort/reflex/will)
                    SetLocalInt(oPC, "ws_save_spec_" + IntToString(iSubType), GetLocalInt(oPC, "ws_save_spec_" + IntToString(iSubType)) + GetItemPropertyCostTableValue(ip) );
                    break;
                case 49: // Saving Throw Penalty vs Element(or universal)
                    SetLocalInt(oPC, "ws_save_elem_" + IntToString(iSubType), GetLocalInt(oPC, "ws_save_elem_" + IntToString(iSubType)) - GetItemPropertyCostTableValue(ip) );
                    break;
                case 50: // Saving Throw Penalty specific (fort/reflex/will)
                    SetLocalInt(oPC, "ws_save_spec_" + IntToString(iSubType), GetLocalInt(oPC, "ws_save_spec_" + IntToString(iSubType)) - GetItemPropertyCostTableValue(ip) );
                    break;
                case 51: // Regeneration
                    SetLocalInt(oPC, "ws_regen", GetLocalInt(OBJECT_SELF, "ws_regen") + GetItemPropertyCostTableValue(ip) );
                    break;
                case 20: // Damage Immunity
                    SetLocalInt(oPC, "ws_dam_immun_" + IntToString(iSubType), GetLocalInt(oPC, "ws_dam_immun_" + IntToString(iSubType)) + ConvertNumToImmunePercentage(GetItemPropertyCostTableValue(ip)) );
                    break;
                case 24: // Damage Vulnerability
                    SetLocalInt(oPC, "ws_dam_immun_" + IntToString(iSubType), GetLocalInt(oPC, "ws_dam_immun_" + IntToString(iSubType)) - ConvertNumToImmunePercentage(GetItemPropertyCostTableValue(ip)) );
                    break;
            };
        }
        ip = GetNextItemProperty(oItem);
    }
    return ePoly;
}
// if bItems is TRUE, Adds all the stackable properties on all the objects given to ePoly.
// if bItems is FALSE, Adds only the stackable properties on armor and helmet to ePoly.
effect AddStackablePropertiesToPoly ( object oPC, effect ePoly, int bWeapon, int bItems, int bArmor, object oArmorOld, object oRing1Old,
                                      object oRing2Old, object oAmuletOld, object oCloakOld, object oBracerOld,
                                      object oBootsOld, object oBeltOld, object oHelmetOld, object oShield, object oWeapon, object oHideOld)
{
    if (bArmor ) // Armor properties get carried over
    {
        ePoly = ExamineStackableProperties ( oPC, ePoly, oArmorOld );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oHelmetOld );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oShield );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oHideOld );
    }
    if ( bItems ) // Item properties get carried over
    {
        ePoly = ExamineStackableProperties ( oPC, ePoly, oRing1Old );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oRing2Old );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oAmuletOld );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oCloakOld );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oBootsOld );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oBeltOld );
        ePoly = ExamineStackableProperties ( oPC, ePoly, oBracerOld );
    }
    // AC bonuses are attached to ePoly inside ExamineStackableProperties
    int i; // This will loop over all the different ability subtypes (eg str, dex, con, etc)
    int j; // This will contain the sum of the stackable bonus type we're looking at
    for ( i = 0; i <= 5; i++ ) // **** Handle Ability Bonuses ****
    {
        j = GetLocalInt(oPC, "ws_ability_" + IntToString(i));
        // Add the sum of this ability bonus to the polymorph effect.
        if ( j > 0 ) // Sum was Positive
            ePoly = EffectLinkEffects(EffectAbilityIncrease(i, j), ePoly);
        else if ( j < 0 ) // Sum was Negative
            ePoly = EffectLinkEffects(EffectAbilityDecrease(i, -j), ePoly);
        DeleteLocalInt(oPC, "ws_ability_" + IntToString(i));
    }
    for ( i = 0; i <= 26; i++ ) // **** Handle Skill Bonuses ****
    {
        j = GetLocalInt(oPC, "ws_skill_" + IntToString(i));
        // Add the sum of this skill bonus to the polymorph effect.
        if ( j > 0 ) // Sum was Positive
            ePoly = EffectLinkEffects(EffectSkillIncrease(i, j), ePoly);
        else if ( j < 0 ) // Sum was Negative
            ePoly = EffectLinkEffects(EffectSkillDecrease(i, -j), ePoly);
        DeleteLocalInt(oPC, "ws_skill_" + IntToString(i));
    }
    for ( i = 0; i <= 21; i++ ) // **** Handle Saving Throw vs element Bonuses ****
    {
        j = GetLocalInt(oPC, "ws_save_elem_" + IntToString(i));
        // Add the sum of this saving throw bonus to the polymorph effect.
        if ( j > 0 ) // Sum was Positive
            ePoly = EffectLinkEffects(EffectSavingThrowIncrease(SAVING_THROW_ALL, j, i), ePoly);
        else if ( j < 0 ) // Sum was Negative
            ePoly = EffectLinkEffects(EffectSavingThrowDecrease(SAVING_THROW_ALL, -j, i), ePoly);
        DeleteLocalInt(oPC, "ws_save_elem_" + IntToString(i));
    }
    for ( i = 0; i <= 3; i++ ) // **** Handle Saving Throw specific Bonuses ****
    {
        j = GetLocalInt(oPC, "ws_save_spec_" + IntToString(i));
        // Add the sum of this saving throw bonus to the polymorph effect.
        if ( j > 0 ) // Sum was Positive
            ePoly = EffectLinkEffects(EffectSavingThrowIncrease(i, j), ePoly);
        else if ( j < 0 ) // Sum was Negative
            ePoly = EffectLinkEffects(EffectSavingThrowDecrease(i, -j), ePoly);
        DeleteLocalInt(oPC, "ws_save_spec_" + IntToString(i));
    }
    j = GetLocalInt(oPC, "ws_regen");
    if ( j > 0 )
    {
        ePoly = EffectLinkEffects(EffectRegenerate(j, 6.0), ePoly);
        DeleteLocalInt(oPC, "ws_regen" );
    }
    for ( i = 0; i <= 13; i++ ) // **** Handle Damage Immunity and Vulnerability ****
    {
        j = GetLocalInt(oPC, "ws_dam_immun_" + IntToString(i));
        // Add the sum of this Damage Immunity/Vulnerability to the polymorph effect.
        if ( j > 0 ) // Sum was Positive
            ePoly = EffectLinkEffects(EffectDamageImmunityIncrease(ConvertNumToDamTypeConstant ( i ), j), ePoly);
        else if ( j < 0 ) // Sum was Negative
            ePoly = EffectLinkEffects(EffectDamageImmunityDecrease(ConvertNumToDamTypeConstant ( i ), -j), ePoly);
        DeleteLocalInt(oPC, "ws_dam_immun_" + IntToString(i));
    }
    return ePoly; // Finally, we have the entire (possibly huge '<img'>  ) effect to be applied to the shifter.
}
// Returns the spell that applied a Polymorph Effect currently on the player.
// -1 if it was no spell, -2 if no polymorph effect found.
int ScanForPolymorphEffect(object oPC)
{
    effect eEffect = GetFirstEffect(oPC);
    while ( GetIsEffectValid(eEffect) )
    {
        if ( GetEffectType( eEffect ) == EFFECT_TYPE_POLYMORPH )
        {
            return GetEffectSpellId(eEffect);
        }
        eEffect = GetNextEffect(oPC);
    }
    return -2;
}
// Converts a number from iprp_damagetype.2da to the corresponding
// DAMAGE_TYPE_* constants.
int ConvertNumToDamTypeConstant ( int iItemDamType )
{
    switch ( iItemDamType )
    {
        case 0:
            return DAMAGE_TYPE_BLUDGEONING;
            break;
        case 1:
            return DAMAGE_TYPE_PIERCING;
            break;
        case 2:
            return DAMAGE_TYPE_SLASHING;
            break;
        case 5:
            return DAMAGE_TYPE_MAGICAL;
            break;
        case 6:
            return DAMAGE_TYPE_ACID;
            break;
        case 7:
            return DAMAGE_TYPE_COLD;
            break;
        case 8:
            return DAMAGE_TYPE_DIVINE;
            break;
        case 9:
            return DAMAGE_TYPE_ELECTRICAL;
            break;
        case 10:
            return DAMAGE_TYPE_FIRE;
            break;
        case 11:
            return DAMAGE_TYPE_NEGATIVE;
            break;
        case 12:
            return DAMAGE_TYPE_POSITIVE;
            break;
        case 13:
            return DAMAGE_TYPE_SONIC;
            break;
        default:
            return DAMAGE_TYPE_POSITIVE;
            break;
    };
    // This one might seem unneccesary but it wont compile otherwise
    return DAMAGE_TYPE_POSITIVE;
}
// Converts a number from iprp_immuncost.2da to the corresponding percentage of immunity
int ConvertNumToImmunePercentage ( int iImmuneCost )
{
    switch ( iImmuneCost )
    {
        case 1:
            return 5;
            break;
        case 2:
            return 10;
            break;
        case 3:
            return 25;
            break;
        case 4:
            return 50;
            break;
        case 5:
            return 75;
            break;
        case 6:
            return 90;
            break;
        case 7:
            return 100;
            break;
        default:
            return 0;
            break;
    };
    return 0;
}
void WildshapeCopyWeaponProperties(object oPC, object oOld, object oNew)
{
    if (GetIsObjectValid(oOld) && GetIsObjectValid(oNew))
    {
        itemproperty ip = GetFirstItemProperty(oOld);
        // If both are Melee Weapons
        if ( !GetWeaponRanged(oOld) && !GetWeaponRanged(oNew) )
        {
            while (GetIsItemPropertyValid(ip))
            {
                AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew);
                ip = GetNextItemProperty(oOld);
            }// while
        }
        // If both are Ranged Weapons
        else if ( GetWeaponRanged(oOld) && GetWeaponRanged(oNew) )
        {
            int bUnlimitedAmmoFound = FALSE;
            itemproperty ipNew;
            int iOldMightyValue = 0;
            object oAmmo;
            while (GetIsItemPropertyValid(ip))
            {
                if ( GetItemPropertyType(ip) == 61 ) // 61 = Unlimited Ammo
                {
                    // For some reason, when removing/replacing an unlimited
                    // ammo property, the corresponding missile type will get
                    // dropped in the player's inventory, so we have to remove
                    // that missile again to prevent abuse.
                    bUnlimitedAmmoFound = TRUE;
                    oAmmo = GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC);
                    if ( !GetIsObjectValid( oAmmo ) )
                        oAmmo = GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC);
                    if ( !GetIsObjectValid( oAmmo ) )
                        oAmmo = GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC);
                    IPRemoveMatchingItemProperties(oNew, ITEM_PROPERTY_UNLIMITED_AMMUNITION, DURATION_TYPE_PERMANENT );
                    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew);
                    DestroyObject(oAmmo);
                }
                else if ( GetItemPropertyType(ip) == 45 ) // 45 = Mighty
                {
                    ipNew = GetFirstItemProperty(oNew);
                    // Find the mighty value of the Polymorph's weapon
                    while ( GetIsItemPropertyValid(ipNew) )
                    {
                        if ( GetItemPropertyType( ipNew ) == 45 )
                        {
                            iOldMightyValue = GetItemPropertyCostTableValue( ipNew );
                            break;
                        }
                        ipNew = GetNextItemProperty(oNew);
                    } // while
                    // If new mighty value bigger, remove old one and add new one.
                    if ( GetItemPropertyCostTableValue(ip) > iOldMightyValue )
                    {
                        RemoveItemProperty(oNew, ipNew);
                        AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew);
                    }
                }
                else
                    AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew);
                ip = GetNextItemProperty(oOld);
            } // while
            // Add basic unlimited ammo if neccesary
            if ( bUnlimitedAmmoFound == FALSE && !GetItemHasItemProperty(oNew, ITEM_PROPERTY_UNLIMITED_AMMUNITION ) )
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyUnlimitedAmmo(), oNew);
        }
    }
    else if ( GetWeaponRanged(oNew) )
    {
        // Add basic unlimited ammo if neccesary
        if ( !GetItemHasItemProperty(oNew, ITEM_PROPERTY_UNLIMITED_AMMUNITION ) )
            AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyUnlimitedAmmo(), oNew);
    }
}
// Returns TRUE if oItem is a creature claw or bite.
int GetIsCreatureWeapon( object oItem )
{
    int iBaseItemType = GetBaseItemType(oItem);
    switch ( iBaseItemType )
    {
        case BASE_ITEM_CBLUDGWEAPON:
        case BASE_ITEM_CPIERCWEAPON:
        case BASE_ITEM_CSLASHWEAPON:
        case BASE_ITEM_CSLSHPRCWEAP:
            return TRUE;
        default:
            return FALSE;
    };
    return FALSE;
}
// **** End Functions, added by Iznoghoud ****