XP script for disarming traps

Hi guys,

Sorry if similiar topic is already here, but i couldn’t find it and sorry for my bad english, third language…anyway…

Is there a way to reward PC with XP anytime he or she disarm trap WITHOUT to manualy attach script to OnDisarm event on trap? I want to put that in Override to apply for all modules if possible. Thanks a lot for any answer! Cheers.

Not really since there’s no global hook. The best option would be to loop every trap in the module and simply add the relevant script in to award XP. If you’re not into scripting this isn’t hugely difficult if you know the GetFirstArea/GetNextArea and GetFirstObjectInArea/GetNextObjectInArea, and Get/SetEventScript. You could run such a script in the console window for each module you play (and have the script to run as a ncs file in your override).

You do run the chance of breaking the module though. There might be important scripts on some of those locks…

That’s why you would check if there is already an OnDisarm script set (using GetEventScript) and leave those traps alone.

1 Like

Alright, thanks to all for advises! :+1:

Here’s a (more or less) simple script (for NWN EE) that modifies all traps in a module to give XP on disarming:

// sets the OnDisarmTrap script on every trap in a module
// that doesn't already have an OnDisarmTrap script
// requires our OnDisarmTrap script to be in override folder!

// name of our OnDisarmTrap script
const string ONDISARM_TRAP_SCRIPTNAME = "ondisarmtrap";
// make sure the script does not exist in the module so better give it some gibberish name
// and not just "ondisarmtrap" as this name might be used in some modules.

void main()
{
    object oPC = GetFirstPC();
    int nCountGlobal=0;

    // get location of our OnDisarmTrap script
    string sScrLoc = ResManGetAliasFor(ONDISARM_TRAP_SCRIPTNAME, RESTYPE_NCS);
    if (sScrLoc=="")
    {
        // Something wrong: OnDisarmTrap script does not exist!
        SendMessageToPC(oPC, "Script "+ONDISARM_TRAP_SCRIPTNAME+".ncs does not exist!");
    }
    else if (sScrLoc!="OVERRIDE:")
    {
        // Something wrong (most likely): script with same name as our OnDisarmTrap script found
        SendMessageToPC(oPC, "Script "+ONDISARM_TRAP_SCRIPTNAME+".ncs found in "+sScrLoc+"!");
        SendMessageToPC(oPC, "Either our OnDisarmTrap script is misplaced or a script with the name exists at that location.");
    }
    else
    {
        int nCountArea, nObjectType, nEvent;
        string sEventScript;
        object oObject;
        object oArea = GetFirstArea();
        while (GetIsObjectValid(oArea))
        {
            oObject = GetFirstObjectInArea(oArea);
            nCountArea=0;
            while (GetIsObjectValid(oObject))
            {
                nObjectType = GetObjectType(oObject);
                switch (nObjectType)
                {
                case OBJECT_TYPE_TRIGGER:
                case OBJECT_TYPE_DOOR:
                case OBJECT_TYPE_PLACEABLE:
                    if (GetIsTrapped(oObject) && GetTrapDisarmable(oObject))
                    {
                        switch (nObjectType)
                        {
                        case OBJECT_TYPE_TRIGGER:
                            nEvent = EVENT_SCRIPT_TRIGGER_ON_DISARMED;
                            break;
                        case OBJECT_TYPE_DOOR:
                            nEvent = EVENT_SCRIPT_DOOR_ON_DISARM;
                            break;
                        case OBJECT_TYPE_PLACEABLE:
                            nEvent = EVENT_SCRIPT_PLACEABLE_ON_DISARM;
                            break;
                        }
                        sEventScript = GetEventScript(oObject, nEvent);
                        if (sEventScript == "")
                        {
                            SetEventScript(oObject, nEvent, ONDISARM_TRAP_SCRIPTNAME);
                            nCountArea++;
                        }
                        else if (sEventScript!=ONDISARM_TRAP_SCRIPTNAME)
                        {
                            SendMessageToPC(oPC, "Area "+GetName(oArea)+": Object "+GetName(oObject)+" already has OnDisarmTrap script set ("+sEventScript+").");
                        }
                    }
                    break;
                }
                oObject = GetNextObjectInArea(oArea);
            }
            if (nCountArea>0)
            {
                SendMessageToPC(oPC, "Area "+GetName(oArea)+": "+IntToString(nCountArea)+"trap(s) modified.");
                nCountGlobal+=nCountArea;
            }
            oArea = GetNextArea();
        }
    }

    SendMessageToPC(oPC, "Number of traps modified: "+IntToString(nCountGlobal));
}

And here is a (simple) OnDisarmTrap script:

// OnDisarmTrap script
// Make sure the script name isn't already used in the module
// Of course the name has to match the name used in the other script.

// change the factor to give more or less XP
const float DISARM_XP_FACTOR = 1.0f;
void main()
{
    object oPC = GetLastDisarmed();
    if (GetIsPC(oPC))
    {
        int nDC = GetTrapDisarmDC(OBJECT_SELF);
        int nXP = FloatToInt(IntToFloat(nDC) * DISARM_XP_FACTOR);
        GiveXPToCreature(oPC, nXP);
    }
}

Compile the scripts using the toolset and put the compiled scripts (extension .ncs) in your override folder.

Or use the attached scripts:
GetXPOnDisarmTrap.zip (2.9 KB)

Then start a module, press Shft+Ctrl+F12 to open the debug console and click NWscript to open the script console. In the box at the bottom enter the name of the first script and then click Execute.

Tested with Wailing Death (Prelude, Chapter 1, Chapter 2) but use on your own risk :D.

4 Likes

Neat. You could also have the original script be fired after your XP is awarded, if the script already exists, but this approach is safer and most modules barely use that particular event.

Thought about that but then it might be that you get XP twice (in case the original script also awards XP).

You could run your XP check immediately after the ExecuteScript and don’t award more XP if their XP has changed heh, but yes much simpler this way!

Works flawlessly, thank you very much! You rocks! :metal:

@Kamiryn
How difficult would it be to modify the script to give XP for picking locks as well as disarming traps?

Please ignore if this is too much hassle.

It’s not very difficult. It’s slightly more complex if want to avoid awarding XP for using keys. Also you probably want to avoid giving XP more than once in case a lock can be re-locked using a key.

In fact I had already modified the script…

// sets the OnDisarmTrap script on every trap in a module
// that doesn't already have an OnDisarmTrap script
// requires our OnDisarmTrap script to be in override folder!

// name of our OnDisarmTrap script
const string ON_DISARM_TRAP_SCRIPTNAME = "mk_ondisarmtrap";

// name of our OnUnlock script
const string ON_UNLOCK_SCRIPTNAME = "mk_onunlock";

// Get current time in seconds (starting from when GetTime is called the first time)
int SECONDS_ZERO = -1;
float GetTime()
{
    sqlquery query = SqlPrepareQueryObject(GetModule(), "SELECT STRFTIME('%s', 'now'), SUBSTR(STRFTIME('%f', 'now'), 4)");
    SqlStep(query);
    int nSeconds = SqlGetInt(query, 0);
    int nMSeconds = SqlGetInt(query, 1);
    if (SECONDS_ZERO<0)
    {
        SECONDS_ZERO = nSeconds;
        nSeconds = 0;
    }
    else
    {
        nSeconds-=SECONDS_ZERO;
    }
    return IntToFloat(nSeconds)+IntToFloat(nMSeconds)/1000.0;
}

void main()
{
    object oPC = GetFirstPC();
    float fTime0 = GetTime();
    int nCountDisarmGlobal=0;
    int nCountUnlockGlobal=0;

    // get location of our OnDisarmTrap script
    string sScrLocDisarm = ResManGetAliasFor(ON_DISARM_TRAP_SCRIPTNAME, RESTYPE_NCS);

    // get location of our OnUnlock script
    string sScrLocUnlock = ResManGetAliasFor(ON_UNLOCK_SCRIPTNAME, RESTYPE_NCS);

    int bModifyTraps = FALSE;
    int bModifyLocks = FALSE;

    if (sScrLocDisarm=="")
    {
        // Something wrong: OnDisarmTrap script does not exist!
        SendMessageToPC(oPC, "Script "+ON_DISARM_TRAP_SCRIPTNAME+".ncs does not exist!");
    }
    else if (sScrLocDisarm!="OVERRIDE:")
    {
        // Something wrong (most likely): script with same name as our OnDisarmTrap script found
        SendMessageToPC(oPC, "Script "+ON_DISARM_TRAP_SCRIPTNAME+".ncs found in "+sScrLocDisarm+"!");
        SendMessageToPC(oPC, "Either our OnDisarmTrap script is misplaced or a script with the name exists at that location.");
    }
    else
    {
        bModifyTraps = TRUE;
    }

    if (sScrLocUnlock=="")
    {
        // Something wrong: OnDisarmTrap script does not exist!
        SendMessageToPC(oPC, "Script "+ON_UNLOCK_SCRIPTNAME+".ncs does not exist!");
    }
    else if (sScrLocUnlock!="OVERRIDE:")
    {
        // Something wrong (most likely): script with same name as our OnUnlock script found
        SendMessageToPC(oPC, "Script "+ON_UNLOCK_SCRIPTNAME+".ncs found in "+sScrLocUnlock+"!");
        SendMessageToPC(oPC, "Either our OnUnlock script is misplaced or a script with the name exists at that location.");
    }
    else
    {
        bModifyLocks = TRUE;
    }

    if (bModifyTraps || bModifyLocks)
    {
        int nCountDisarmArea, nCountUnlockArea, nObjectType, nEventDisarm, nEventUnlock;
        string sEventScript;
        object oObject;
        object oArea = GetFirstArea();
        while (GetIsObjectValid(oArea))
        {
            oObject = GetFirstObjectInArea(oArea);
            nCountDisarmArea=0;
            nCountUnlockArea=0;
            while (GetIsObjectValid(oObject))
            {
                nEventDisarm=-1;
                nEventUnlock=-1;
                nObjectType = GetObjectType(oObject);
                switch (nObjectType)
                {
                case OBJECT_TYPE_TRIGGER:
                    nEventDisarm = EVENT_SCRIPT_TRIGGER_ON_DISARMED;
                    break;
                case OBJECT_TYPE_DOOR:
                    nEventDisarm = EVENT_SCRIPT_DOOR_ON_DISARM;
                    nEventUnlock = EVENT_SCRIPT_DOOR_ON_UNLOCK;
                    break;
                case OBJECT_TYPE_PLACEABLE:
                    nEventDisarm = EVENT_SCRIPT_PLACEABLE_ON_DISARM;
                    nEventUnlock = EVENT_SCRIPT_PLACEABLE_ON_UNLOCK;
                    break;
                }

                if (bModifyTraps && (nEventDisarm!=-1) && GetIsTrapped(oObject) && GetTrapDisarmable(oObject))
                {
                    sEventScript = GetEventScript(oObject, nEventDisarm);
                    if (sEventScript == "")
                    {
                        SetEventScript(oObject, nEventDisarm, ON_DISARM_TRAP_SCRIPTNAME);
                        nCountDisarmArea++;
                    }
                    else if (sEventScript!=ON_DISARM_TRAP_SCRIPTNAME)
                    {
                        SendMessageToPC(oPC, "Area "+GetName(oArea)+": Object "+GetName(oObject)+" already has OnDisarmTrap script set ("+sEventScript+").");
                    }
                }

                if (bModifyLocks && (nEventUnlock!=-1) && GetLocked(oObject) && !GetLockKeyRequired(oObject))
                {
                    sEventScript = GetEventScript(oObject, nEventUnlock);
                    if (sEventScript == "")
                    {
                        SetEventScript(oObject, nEventUnlock, ON_UNLOCK_SCRIPTNAME);
                        nCountUnlockArea++;
                    }
                    else if (sEventScript!=ON_UNLOCK_SCRIPTNAME)
                    {
                        SendMessageToPC(oPC, "Area "+GetName(oArea)+": Object "+GetName(oObject)+" already has OnUnlock script set ("+sEventScript+").");
                    }

                }

                oObject = GetNextObjectInArea(oArea);
            }

            if ((nCountDisarmArea>0) || (nCountUnlockArea>0))
            {
                SendMessageToPC(oPC, "Area "+GetName(oArea)+": "+IntToString(nCountDisarmArea)+" trap(s) modified, "+IntToString(nCountUnlockArea)+" lock(s) modified.");
                nCountDisarmGlobal+=nCountDisarmArea;
                nCountUnlockGlobal+=nCountUnlockArea;
            }

            oArea = GetNextArea();
        }
    }

    float fTime1 = GetTime();

    SendMessageToPC(oPC, "Number of traps modified: "+IntToString(nCountDisarmGlobal));
    SendMessageToPC(oPC, "Number of locks modified: "+IntToString(nCountUnlockGlobal));
    SendMessageToPC(oPC, "Duration: "+FloatToString(fTime1-fTime0, 0, 3)+"s.");

}

and the OnUnLock event script…

// OnDisarmTrap script
// script name should not exist in any module
const float UNLOCK_XP_FACTOR = 1.0f;

void main()
{
    object oPC = GetLastUnlocked();
    if (GetIsPC(oPC))
    {
        if (!GetLocalInt(OBJECT_SELF, "_DO_ONCE_XP_ON_UNLOCK"))
        {
            SetLocalInt(OBJECT_SELF, "_DO_ONCE_XP_ON_UNLOCK", TRUE);

            string sKey = GetLockKeyTag(OBJECT_SELF);
            object oKey = GetItemPossessedBy(oPC, sKey);

            if (!GetIsObjectValid(oKey))
            {
                int nDC = GetLockUnlockDC(OBJECT_SELF);
                int nXP = FloatToInt(IntToFloat(nDC) * UNLOCK_XP_FACTOR);
                GiveXPToCreature(oPC, nXP);
            }
        }
    }
}

GiveXPforUsingThiefSkills.zip (4.1 KB)

1 Like

But what is, if the key is automatically removed? (“Remove” checked on the lock)? Will it still be there to allow the check if the PC possesses the key?

Many many thanks @Kamiryn - brilliant stuff!

I think the key is still there. At least in my test module I don’t get XP in this case.

I’ve modified the scripts a little bit, improved them, put them all in a single script, added a configuration/statistic window and made a mod from it…

https://neverwintervault.org/project/nwnee/script/award-xp-using-thieving-skills-axpfuts

1 Like