Problems with adding AC bonus on item equip

Hey guys, need some help with one simple feature, that I’m trying to implement in my PW.

What I want to do is to give player a small AC bonus on equip certain weapons (double sided in this case).

I wrote a simple function to use it onPlayerEquip:

void ApplyDoubleSidedWeaponEquipAC(object oPC, object oItem)
{
int nBase = GetBaseItemType(oItem);
switch (nBase)
{
case BASE_ITEM_TWOBLADEDSWORD:
case BASE_ITEM_DIREMACE:
case BASE_ITEM_DOUBLEAXE:
case BASE_ITEM_QUARTERSTAFF:

    effect eAC = SupernaturalEffect(EffectACIncrease(3, AC_SHIELD_ENCHANTMENT_BONUS));
    ApplyEffectToObject(DURATION_TYPE_PERMANENT, eAC, oPC);

    return;
}

}

And same thing for onPlayerUnqeuip with EffectACDecrease.

Got two problems with this approach though:

  • AC bonus applied as a SupernaturalEffect dissapears on player’s rest (which is weird, because on nwnlexicon it says, that ‘Supernatural (isn’t removed by resting)’
  • unequipping weapon and reequipping it makes the bonus stack, so it doesn’t reapply correctly (it doesn’t ‘reset’ as it should)

Could you guys give me some tips how to approach these issues? What’s the best practice to apply bonuses like that?

as for bonus removed by resting, i am not sure whether there is some issue with supernatural effect, but you should check the resting script if there isn’t some code that strips effects

as for stacking, there are two possible issues you could ran into:

  1. unequip event runs when the item being unequipped is still in slot - that means that if you wrote your script in certain way, script will consider player to still have the item and thus will not remove the effect

  2. you are removing effect in wrong way:

  • there are 3 possibilities how to code this to not happen:
    2.1) use special object (invisible placeable) as an effect creator:
    see code snippet from my PRC Lite project where I give shield AC for Two Weapon Defense feat

object oCreator = GetObjectByTag(“EC_WEAPON”);
AssignCommand(oCreator,ApplyEffectToObject(DURATION_TYPE_EQUIPPED,EffectACIncrease(nACWeapon,AC_SHIELD_ENCHANTMENT_BONUS),oPC));
and then remove all effects of this creator

    effect e = GetFirstEffect(oPC);
    while(GetIsEffectValid(e))
    {
        if(GetEffectCreator(e) == oCreator)
        {
            RemoveEffect(oPC,e);
        }
        e = GetNextEffect(oPC);
    }

2.2) instead of the invisible placeable use item itself as creator - this is however not suitable when you have more than one source of the bonus and want to combine them (ie. +1 ac for double weapon and +1 ac for having 20+ lvl ranger etc.)

2.3) remove all effects which are supernatural, permanent and type of EFFECT_TYPE_AC_INCREASE

here is my mini-library for unequip scripts which will allow you to code the unequip in similar way how you can code OnPlayerRest script (Note, I am not saying you need this, it is possible to code what you want without it)

const int UNEQUIP_EVENTTYPE_UNEQUIP_INVALID = -1;
const int UNEQUIP_EVENTTYPE_UNEQUIP_STARTED = 0;
const int UNEQUIP_EVENTTYPE_UNEQUIP_FINISHED = 1;

void main();

void GetUnEquipFinishedEvent(object oPC, object oItem, int nSlot)
{
object oItem2 = GetItemInSlot(nSlot,oPC);
 if(!GetIsObjectValid(oItem2))
 {
 SetLocalInt(oPC,"GetLastUnEquipEventType()",UNEQUIP_EVENTTYPE_UNEQUIP_FINISHED);
 SetLocalInt(oPC,"GetPCItemLastUnequippedSlot()",nSlot);
 SetLocalObject(oPC,"GetPCItemLastUnequipped()",oItem);
 main();
 }
 else if(oItem2 != oItem)
 {
 SetLocalInt(oPC,"GetLastUnEquipEventType()",UNEQUIP_EVENTTYPE_UNEQUIP_FINISHED);
 SetLocalInt(oPC,"GetPCItemLastUnequippedSlot()",nSlot);
 SetLocalObject(oPC,"GetPCItemLastUnequipped()",oItem);
 main();
 }
 else
 {
 DelayCommand(0.1,GetUnEquipFinishedEvent(oPC,oItem,nSlot));
 return;
 }
SetLocalInt(oPC,"GetLastUnEquipEventType()",0);
SetLocalInt(oPC,"GetPCItemLastUnequippedSlot()",0);
SetLocalObject(oPC,"GetPCItemLastUnequipped()",OBJECT_INVALID);
}

int GetLastUnEquipEventType()
{
object oItem = GetPCItemLastUnequipped();
object oPC = GetPCItemLastUnequippedBy();
int ret = GetLocalInt(oPC,"GetLastUnEquipEventType()");
 if(GetIsObjectValid(oPC) && GetIsObjectValid(oItem))
 {
  if(!ret)
  {
  GetUnEquipFinishedEvent(oPC,oItem,GetSlotByItem(oItem,oPC));
  }
 }
 else
 {
 return UNEQUIP_EVENTTYPE_UNEQUIP_INVALID;
 }
return ret;
}

Put this code above void main(), then in script body you can use this:

int nType = GetLastUnEquipEventType();
if(nType == UNEQUIP_EVENTTYPE_UNEQUIP_FINISHED)//item is no longer in slot
{
    //some code
}

This may be a naive question, but why not just use an item ac bonus? No coding required. If it needs to be shield AC I think you could just edit the baseitem.2Da.

in my case - I wanted to avoid modifying baseitems.2da and by default weapons’ AC type is deflection

if that is not the concern, then indeed you can change that to shield and use itemproperty

and if this is a global change (which wasn’t in my case), and everyone equipping the double sided weapon is supposed to get this bonus then you can indeed add this AC directly into blueprints or once by scripting in OnAcquire (check for local variable on item, if not there add it, then add AC itemproperty)

@Shadooow thanks for the tips, appreciate it. I’ll try to use that invisible object with tag, dunno if my nwscript skills will be good enough tho :stuck_out_tongue:

Btw using a skin object would be also a good idea? Are there any step by step guides how to use skins (I’ve been googling that, but haven’t found anything helpful for people lacking nwscript experience like me)? I’m not sure when should I apply the bonuses on oSkin, whether I should check for an existing oSkin on my oPC or delete the existing oSkin etc.

If this is only for your module using skin is fine (for my project PRC Lite I had to abadon it though as it is not realiable when you want to make it work in every module player might play), although you need to be aware of the issue when skin is destroyed upon login if your module uses ILR or ELC (multiplayer issue only basically). (And community patch fixes this problem)

There is also a problem with removing itemproperty damage immunity increase, at least in non-EE version of the game, the actual benefit of this itemproperty is not removed with removing itemproperty itself and you need to force re-equip skin or destroy it and create new skin.

And both issues, when you run into them are further complicated when player has full inventory as new skin will fail to spawn in their inventory (again community patch solves this).

But if you can avoid these issues then skin is great place for adding permanent bonuses because usual effects are removed by death and they also show icon.

To “get” skin, I recommend to use:
#include “x3_inc_horse”

and function object SKIN_SupportGetSkin(object oCreature)

or use your own scripting entirely.

1 Like

@Shadooow which CPP version are u referring to? Those fixes are in include scripts?

issue with skin being destroyed at login is fixed in 1.71
issue with full inventory is in 1.72

but first fix fails to work without second if (re)logging player has full inventory

the first issue is solved in UnAcquire event script, based on the version you can find the code for this inside x2_mod_def_unaqu (1.71) or 70_mod_def_unaqu (latest 1.72)

the second issue is fixed via baseitems.2da - skin item is set to have 0x0 icon which then allows to spawn skin into full inventory

Mkay, got it. Cheers! Thank you for the feedback.

@Shadooow one more thing: is there any chance to externalize those scripts in CPP builders resources? I mean those module events (onEnter, onPlayerEquip etc.).

Btw u refer to 1.72 CPP. Isn’t it only for EE version?

1.72 is for both 1.69 and EE the versions differ though: the version for 1.69 has these fixes inside scripts 70_mod_def_* (as these scripts are ran via NWNX/NWNCX in every module you play simultaneously with normal module events) while EE version has these fixes inside x2_mod_def_* scripts and manual merging is expected.

If you download the 1.69 version then the 70_mod_def_* scipts are externalized in “1.72 builder resources\all custom scripts”

And version for EE is not compiled at all. All files are left unpacked.

2 Likes