Armor properties matching player level

Is there a simple way to make a custom armor that scales with the player level ?
PC level 3 -> +1AC
PC level 6 -> +2AC, dmg reduction
PC level 9 -> +3AC, dmg reduction, haste
etc…

The armor have to be playable in any campaign.

Thanks.

that depends. which do you mean – creating an armour that will add new item properties as the pc levels, so it will increase in level as s/he does ? or do you mean customising how item levels are interpreted by the game engine so you could define them yourself ?

if the first, the easiest way to do this would be in the toolset. just create a dozen or so items of the same type and check the item level [on the first tab of the item properties pop-up] so it corresponds to what you want.

if the second… wellll… there’s this phrase from dante that comes to mind : 'abandon all hope, ye who entere here ! ’
lmk if that’s really what you want to do. if so, i can dig up the formula for item value calculations out of my notes and send you a list of the [30-or-so] 2da’s that are involved. but only if you promise not to letter-bomb me as a result. :wink:

I think i was not clear enough… sorry, my english is not that good.
I want to play the core campaigns with a custom armor. This armor properties will progress with my character level. When my character reachs level 3, the armor will automatically get a +1 AC bonus. Level 6 -> +2 AC bonus and for exemple haste… etc etc…
Maybe a simple text will appear saying " Your armor has gain more power !"
I dont think it is hard to do but i am new to nwn toolset scripting.

So basically you want a cheat engine for the NWN campaigns? That’s doable in at least three ways:

  1. The easiest way to do it (read: with 0% scripting) would be to make a set of armor templates in the toolset (one for each “step”), extract their *.uti files with nwnexplorer to game’s override folder and spawn them using in-game command dm_spawnitem RESREF (where RESREF is resref of your selected item). You’d need to do that manually every time you want a buffed-up armor.

  2. The second approach is to make a script that selects right armor automatically but still needs to be called manually. Scripting gives you more power so you can dynamically choose the right properties without need to make a dozen of armor templates.

  3. Third approach is to override the vanilla OnPlayerLevelUp script (called x1_playerlevelup to call you armor selector script from #2 automatically. This may break the game, though, but is closest to your original question.

There is a simple way to implement this, as long as the modules you play are using the standard default on equip and on unequip scripts (or even custom scripts that have not disallowed tag based scripts), it’s the usage of tag based scripts.

All you need to do is create a script with the same name as the Tag of the item you want the script to work for. So if the item has a Tag of “Test”, the script you create must be called “test”.

The script should then be placed in your My Documents/Neverwinter Nights/Override folder to make it work across different modules (I think that the blueprint of the item might be need to be there as well if it’s custom).

And then this is the script, it will be called automatically when equipped (adding the properties) and unequipped (removing them).

//::///////////////////////////////////////////////
//:: Specific Custom Armor Scale Script
//:://////////////////////////////////////////////
/*
    Note that this system only works, if
    the following events set on your module
    OnEquip      - x2_mod_def_equ
    OnUnEquip    - x2_mod_def_unequ
*/
//:://////////////////////////////////////////////

#include "x2_inc_switches"

int GetDRBonus(int nLEVEL)
{
    //If you want to make the amount scale, make sure you use
    //the IP_CONST_DAMAGESOEAK constant variable, they scale up by 5 (from 5 to 50), not much room there.
    //In this example, the armor gets 5 DR every 6 levels.
    //Tone down by changing it to nLEVEL / 12 or more if just return a fixed value if you think it's too strong.

    int nTIER = nLEVEL / 6;
    if (nTIER < 1) nTIER = 1;
    else if (nTIER > 10) nTIER = 10;
    return nTIER;
}

void RemoveAllProperties(object oITEM)
{
    itemproperty iPROP = GetFirstItemProperty(oITEM);
    while (GetIsItemPropertyValid(iPROP) == TRUE)
    {
        RemoveItemProperty(oITEM, iPROP);
        iPROP = GetNextItemProperty(oITEM);
    }
}

void AddAllProperties(object oITEM, int nHD)
{
    int nAC = nHD / 3;
    if (nAC > 20) nAC = 20;
    AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonus(nAC), oITEM);
    if (nHD < 6) return;
    int nDR = GetDRBonus(nHD);
    AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDamageReduction(nAC - 1, nDR), oITEM);
    if (nHD < 9) return;
    AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyHaste(), oITEM);
    //continue as you see fit
}

void main()
{
    object oITEM;
    int nEVENT = GetUserDefinedItemEventNumber();
    // * This code runs when the item is equipped
    // * Note that this event fires PCs only
    if (nEVENT == X2_ITEM_EVENT_EQUIP)
    {
        oITEM = GetPCItemLastEquipped();
        RemoveAllProperties(oITEM);
        object oPC = GetPCItemLastEquippedBy();
        int nHD = GetHitDice(oPC);
        if (nHD < 3) return;
        DelayCommand(0.0f, AddAllProperties(oITEM, nHD));
    }

    // * This code runs when the item is unequipped
    // * Note that this event fires PCs only
    else if (nEVENT == X2_ITEM_EVENT_UNEQUIP)
    {
        oITEM  = GetPCItemLastUnequipped();
        RemoveAllProperties(oITEM);
    }
}

Keep in mind that this armor is “non enchantable”, if you customize it further with additional properties in-game via crafting, those properties will be lost when the armor is unequipped (if this is a problem for you, and you have the enhanced edition, with some additional tweaking it can be fixed).

And don’t forget that, when you level up, you will have to unequip and reequip the armor to update its properties with the new values.

You can find more info on tag based scripts here:
https://nwnlexicon.com/index.php?title=Grimlar_-_Introduction_To_Tag_Based_Scripting

The level up event is closer to the desired outcome, I think.

Tag based scripting is easy to do, but what happens if the player doesn’t equip their armour very often? They won’t get the bonus as they level up, until a saved game is loaded.

No, he just needs to equip the armor, the OP is asking for bonuses to the armor as the player levels up, not bonuses to the player.
Loading the saved game has nothing to do with it.

@Clangeddin not quite right. You’re suggesting the use of the on equip event. That fires when a saved game is loaded. So, if the player is wearing the armour, it will gain level-related bonuses at that time.

However, to achieve what the OP is asking for, the armour needs to level up when the player levels up, which needs to happen in the level up event.

No one is suggesting adding bonuses to the player.

There is an issue, of course, if the player is not wearing the armour at level up. If the armour has a unique tag, it can be handled in the level up event, by adding bonuses to the armour regardless of where it happens to be.

Of course, on equip could be used if the armour is not unique, but it would still be necessary to handle it in the level up event to achieve the desired outcome.

The on Equip event may fire when a saved game is loaded, but principally, it fires when an item is equipped. In the case of tag based scripts, when that SPECIFIC item is equipped (which is what the OP is asking for here, since he plans to use a custom armor, not to level up every armor in the game).
If the player does not have that specific armor equipped while levelling up, all he has to do is simply equip it and then the bonuses will be updated accordingly (any time). In fact, he’d have to do it to reupdate the bonuses even if he had it equipped (although from the looks of the OP post, it updates every 3 levels).

And no, to achieve what the OP is asking for, the armor is best levelled up when it’s equipped, since it’s the most available type of event that can handle the proper calculations.
Especially if the character eventually delevels later or if the armor is passed to another character.

Edit: By the way, I’ve edited the above script to avoid a double stacking scenario and fixed some calculations about the damage reduction properties. We could go even further and make the armor “enchantable” allowing properties not related to scaling up of the properties to persist, but that depends on the OP (if he cares about it) and if he has the Enhanced Edition or not.

I’ll try Clangeddin’s solution. I’ll tell you the results.

It works perfectly in the test modul where the script was created. I exported the scrpit (.erf) in the override folder but it didn’t work it any campaign. The blueprint is also a .erf and so i haven’t exported it in the override folder.

You are right, NWN1 seems to have a problem with tag based scripts on original campaigns.
I tested it myself, and for some reason, on the original campaigns they don’t work. They either disabled them or added a prefix to the tag based scripts specific to those campaigns.

Keep in mind, though, that when they are on override, they should be extracted from the .erf, there is a program called NWNpacker to do that.

For the time being, I can only suggest a “manual” level up of the armor with the usage of the Debug Console. I know, it sucks, it would be much better if it could be levelled up automatically, but at the very least this works (I just tested it myself on the original campaign):

So, what you need are the blueprint and the script in the override folder obviously, then you must type the following while in-game

##DebugMode 1

(this enables the cheat console)

##dm_spawnitem xxxx

(where xxxx is the blueprint file (without the .uti file extension) name -not the tag- of that custom armor, it will appear where the cursor is)

and finally, after you equip the armor you must type

##dm_runscript xxxx

(where xxxx is the name of the script below, this will update the armor, and it will have to type this command every time you wish to update the armor, according to the PC’s level).

In this case, the script is different, and it won’t matter if the armor is equipped or not, it will be updated as long as it is in your inventory (when you call the script, manually):

NOTE: In this specific case I tagged the custom armor as “it_evolution”, so you can either change the tag of the custom armor to that or change this script down here to reflect the tag of your custom armor.

//::///////////////////////////////////////////////
//:: Specific Custom Armor Scale Script
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////

int GetDRBonus(int nLEVEL)
{
    //If you want to make the amount scale, make sure you use
    //the IP_CONST_DAMAGESOEAK constant variable, they scale up by 5 (from 5 to 50), not much room there.
    //In this example, the armor gets 5 DR every 6 levels.
    //Tone down by changing it to nLEVEL / 12 or more if just return a fixed value if you think it's too strong.

    int nTIER = nLEVEL / 6;
    if (nTIER < 1) nTIER = 1;
    else if (nTIER > 10) nTIER = 10;
    return nTIER;
}

void RemoveAllProperties(object oITEM)
{
    itemproperty iPROP = GetFirstItemProperty(oITEM);
    while (GetIsItemPropertyValid(iPROP) == TRUE)
    {
        RemoveItemProperty(oITEM, iPROP);
        iPROP = GetNextItemProperty(oITEM);
    }
}

void AddAllProperties(object oITEM, int nHD)
{
    int nAC = nHD / 3;
    if (nAC > 20) nAC = 20;
    AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonus(nAC), oITEM);
    if (nHD < 6) return;
    int nDR = GetDRBonus(nHD);
    AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDamageReduction(nAC - 1, nDR), oITEM);
    if (nHD < 9) return;
    AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyHaste(), oITEM);
    //continue as you see fit
}

void main()
{
    object oPC = OBJECT_SELF;
    object oITEM = GetItemInSlot(INVENTORY_SLOT_CHEST);
    if (GetTag(oITEM) != "it_evolution") oITEM = GetItemPossessedBy(oPC, "it_evolution");
    if (oITEM == OBJECT_INVALID)
    {
        FloatingTextStringOnCreature("Custom Evolving Armor is currently not equipped or found on selected character, aborting script.", oPC, FALSE);
        return;
    }

    RemoveAllProperties(oITEM);
    int nHD = GetHitDice(oPC);
    FloatingTextStringOnCreature(GetName(oITEM) + " scaled up to level " + IntToString(nHD), oPC, FALSE);
    if (nHD < 3) return;
    DelayCommand(0.0f, AddAllProperties(oITEM, nHD));
}

Speaking of which, does anyone know how to modify the OC via the toolset, I remember there was a line to put somewhere in the nwn.ini (or nwtoolset.ini?) but I forgot.
So I can at least look at what’s the problem with the tag based script in the OCs.

So, I went ahead and did some investigation and found out that yes, by default, NWN’s campaign do not use tag based scripts as those modules do not have any On Load, On Equip or On Unequip scripts attached to their module events.

Still, I managed to find a way to re-enable them via the Debug Console, by running the following script in the aformentioned method:

#include "x2_inc_switches"

void main()
{
    SetModuleSwitch(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS, TRUE);
    object oMODULE = GetModule();
    SetEventScript(oMODULE, EVENT_SCRIPT_MODULE_ON_EQUIP_ITEM, "x2_mod_def_equ");
    SetEventScript(oMODULE, EVENT_SCRIPT_MODULE_ON_UNEQUIP_ITEM, "x2_mod_def_unequ");
    FloatingTextStringOnCreature("Tag based scripts enabled for Equip and Unequip Events.", OBJECT_SELF, FALSE);
}

Note: This might only work with the enhanced edition, I don’t think that NWN1 Diamond has the SetEventScript functions…

1 Like

I’ll try that