Author Topic: Disable "Take 20"??  (Read 409 times)

Legacy_Belushi1965

  • Newbie
  • *
  • Posts: 16
  • Karma: +0/-0
Disable "Take 20"??
« on: February 06, 2014, 05:54:17 pm »


               Hello everyone!

I searched Google and the usual sources (nwnvault etc) for hours, read all of the forum posts here and also tried to find my way through THE Lexicon...

but....  ':crying:' ... to no effort!

Ok, what I want to do sounds simple: 
I want to create a little "contest of thiefes" quest with many chests and doors to lockpick and traps to disarm.
All of this with increasing difficulty - so it starts out very simple, but ends up almost deadly.

Unfortunately... this is all pretty useless if the contestans always "take 20" instead of using their "true" skill values!
In the contest no enemies will appear, it's all about rogue's skills... but the "take 20" feat of NWN makes a farce out of it!  ':blink:'

HOW can I either disable "Take 20" in a given area - or, which should work too, "simulate" that the contestants are in a battle (without any actual enemies invoked)?!?! 

Somebody can point me into the right direction?

TIA! ':wizard:'
               
               

               


                     Modifié par Belushi1965, 06 février 2014 - 05:55 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Disable "Take 20"??
« Reply #1 on: February 06, 2014, 06:34:06 pm »


               You have to keep the characters in a combat mode. (Some kind of heartbeat that does SignalEvent spellcastat spell id -1 harmful true)
Still there is that feat (cant remember name now) that forces take 20 even in combat. TO get over this is quite problematic... You need to disable that feat either via NWNX or a 2da override (erase all fields in feat.2da for that feat and replace with ****). Or just delevel any rogue which takes this OnLevelUp.
               
               

               
            

Legacy_Squatting Monk

  • Hero Member
  • *****
  • Posts: 776
  • Karma: +0/-0
Disable "Take 20"??
« Reply #2 on: February 06, 2014, 07:08:09 pm »


               ShadoOow, is there a way of doing this with NWNX that I've missed? I tried was to catch the OnUseSkill event for picking the lock, bypass it, and do my own check. However, at least on the Linux version, OnUseSkill does not catch Open Lock unless you're using Thieves' Tools. (Seems to work just fine for Disable Trap, though.)

An alternative using vanilla scripting would be to re-roll the skill check OnUnLock and, if the PC fails, re-lock the object. The big downside to this is the PC wouldn't get any credit for Thieves' Tools (though you might be able to rig up something with OnUnAcquireItem). Still, this is really clunky.
               
               

               
            

Legacy_Belushi1965

  • Newbie
  • *
  • Posts: 16
  • Karma: +0/-0
Disable "Take 20"??
« Reply #3 on: February 06, 2014, 07:10:58 pm »


               Keeping the characters in combat is what I believe to be the most sensible way. I will try to find out how this SignalEvent works '<img'>

The feat you mean (don't remember the name right now, too) is a legally aquired feat and part of the character. Of course, if she/he has that feat, it will be leggit to use it in the contest! It's just the "regular" take 20 that drives me nuts....
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Disable "Take 20"??
« Reply #4 on: February 06, 2014, 10:49:06 pm »


               

Squatting Monk wrote...

ShadoOow, is there a way of doing this with NWNX that I've missed? I tried was to catch the OnUseSkill event for picking the lock, bypass it, and do my own check. However, at least on the Linux version, OnUseSkill does not catch Open Lock unless you're using Thieves' Tools. (Seems to work just fine for Disable Trap, though.)

An alternative using vanilla scripting would be to re-roll the skill check OnUnLock and, if the PC fails, re-lock the object. The big downside to this is the PC wouldn't get any credit for Thieves' Tools (though you might be able to rig up something with OnUnAcquireItem). Still, this is really clunky.

No idea, I only suggested nwnx for removing the feat (perhaps temporary).

Myself, using only the keep in combat script as I didnt needed to nerf the Skill Mastery feat (I remembered ':wizard:').
               
               

               
            

Legacy_Belushi1965

  • Newbie
  • *
  • Posts: 16
  • Karma: +0/-0
Disable "Take 20"??
« Reply #5 on: February 07, 2014, 02:13:46 pm »


               

ShaDoOoW wrote...
Myself, using only the keep in combat script as I didnt needed to nerf the Skill Mastery feat (I remembered).


Me neither '<img'>
If Skill Mastery is part of the character, he may use it of course. It's only the darn take 20 for lower level rogues that are not in combat that bothers me! ':whistle:'
               
               

               
            

Legacy_Belushi1965

  • Newbie
  • *
  • Posts: 16
  • Karma: +0/-0
Disable "Take 20"??
« Reply #6 on: February 07, 2014, 03:36:38 pm »


               whooops.... now, thanks to you all: The task was easy with your hints!
Here's the solution for single-player:

1. Add a harmless looking "enemy"  to the area (may be a placable; I used a brazier) and give it a unique tag ("bel_spellcaster01" in this script).

2. Create this tiny skript (aera properties; edit "OnHeartbeat"):

void main()
{
  object oPC = GetFirstPC();
  object oCaster = GetObjectByTag("bel_spellcaster01");
  SignalEvent(oPC, EventSpellCastAt(oCaster, SPELL_MAGIC_MISSILE, TRUE));
}

(Pros would add a check vor validity of the objects, of course!)

3. Save the script (should now be linked to "OnHeartbeat" of the area)

ALL DONE! '<img'>

For the beginners: 
This script tells the PC every 6 seconds or so ("heartbeat") that the brazier has fired a magic missile at him.
But that's a lie - of course, the brazier has NOT done that. However, it makes the PC believe it did - and set's him into "combat mode". Voila - no more "take 20" when using rogue's skills!!!
Best thing: The more dangerous traps do no longer get "auto-detected" without having search enabled. A trap with a difficulty of 30 for "search" was not detected at all while testing with a low-leveled character, which is goooood! HARHARHAR.... >8^} 

Works like a charm - and can easily be extend for multiplayer using GetFirstPC and GetNextPC in a loop, so the brazier simulates "attacks" on every rogue in the contest.
The only (minor and completely unimportant) thing is, that - when the brazier fires it's FIRST magic missle - an initative roll is displayed in the stats line. But I really don't care about that!
               
               

               


                     Modifié par Belushi1965, 07 février 2014 - 04:15 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Disable "Take 20"??
« Reply #7 on: February 07, 2014, 08:26:13 pm »


               What Ive done is a Pseudo-heartbeat (hmm a tutorial about this would be nice!) that is added to the player in the area's OnEnter.

Your solution needs polishing, this way it will affect pc in all module not only in the specified area!
               
               

               
            

Legacy_MrZork

  • Hero Member
  • *****
  • Posts: 1643
  • Karma: +0/-0
Disable "Take 20"??
« Reply #8 on: February 08, 2014, 03:02:33 am »


               It may be worth noting that the PC may not be able to rest when that heartbeat script is running.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Disable "Take 20"??
« Reply #9 on: February 08, 2014, 04:09:14 am »


               

MrZork wrote...

It may be worth noting that the PC may not be able to rest when that heartbeat script is running.

Yea sure, good point.
               
               

               
            

Legacy_MrZork

  • Hero Member
  • *****
  • Posts: 1643
  • Karma: +0/-0
Disable "Take 20"??
« Reply #10 on: February 08, 2014, 06:40:11 am »


               Here is a version of the heartbeat script that allows a character to rest. Basically, it only fires the hostile action if the toon is doing something that would allow a take 20. It checks the module and/or the area to see if Take 20s should be nerfed.

// InhibitTake20.nss
// Heartbeat script for inhibiting take 20s in module-wide or per-area fashion.
// Does not prevent PC resting.

// Name of the module or area variable where we store whether we are inhibiting take 20s.
// If the module has an int variable with this name set to 1, we inhibit Take 20s everywhere.
// If the INHIBIT_TAKE_20_CHECK_ALL_AREAS switch is true, then then each area a PC 
// is in is checked for an int variable with this name.
const string INHIBIT_TAKE_20 = "INHIBIT_TAKE_20";
// If the following is true, then we check each area for inhibition. Ignored if the variable is set true for the module
const int INHIBIT_TAKE_20_CHECK_ALL_AREAS = TRUE;

void main()
    {

    // is the inhibition module-wide?
    int iInhibitTake20 = GetLocalInt(GetModule(),INHIBIT_TAKE_20);

    object oPC = GetFirstPC();
    int iCurAction;
    while (GetIsObjectValid(oPC))
        {
        if ( iInhibitTake20 || ( INHIBIT_TAKE_20_CHECK_ALL_AREAS && GetLocalInt(GetArea(oPC),INHIBIT_TAKE_20) ) )
            {
            // only bother if the PC is doing something where we want to prevent a take 20
            iCurAction = GetCurrentAction(oPC);
            if ( // traps
                 ( iCurAction == ACTION_DISABLETRAP ) || ( iCurAction == ACTION_EXAMINETRAP ) ||
                 ( iCurAction == ACTION_FLAGTRAP ) || ( iCurAction == ACTION_RECOVERTRAP ) ||
                 ( iCurAction == ACTION_SETTRAP ) ||
                 // locks
                 ( iCurAction == ACTION_LOCK ) || ( iCurAction == ACTION_OPENLOCK )
                 // other
                 || ( iCurAction == ACTION_ANIMALEMPATHY ) || ( iCurAction == ACTION_HEAL ) )
                {
                SignalEvent(oPC, EventSpellCastAt(OBJECT_SELF, SPELL_ALL_SPELLS, 1));
                }
            }
        oPC = GetNextPC();
        }
    }
               
               

               


                     Modifié par MrZork, 08 février 2014 - 06:46 .
                     
                  


            

Legacy_Belushi1965

  • Newbie
  • *
  • Posts: 16
  • Karma: +0/-0
Disable "Take 20"??
« Reply #11 on: February 08, 2014, 12:11:48 pm »


               ooook... I already had the feeling of "that was TOO easy"!

First, about the resting-thing: I have seen that, and I planned to add areas without "enemies" (or introducing a "rest area") where the PC is allowed to rest. Outside of that, "no sleep" is fine.
However, I understood that there was an error in my idea: I believed that the brazier would only attack the PC while he is in range/sight. Obviously, that was stupid, because the brazier actually never attacks the PC! The SignalEvent only "tells" the PC he has been attacked - no matter if this really happened or not!

The fact that the script would be active in the whole module is fine, too, because it will be a "contest of thieves", and the disabling of take 20 is in fact needed module-wide. But you are right - due to my error from above this is not quite the thing I wanted to achieve.

These are no excuses, I just explained why I was thinking my little script could do the job for this special purpose.
However, I understand your arguements! And after reading and (successfully) understandig MrZork's script I must admit it is WAY more elegant, especially because it does not need any brazier around.
'<img'>

Then, unfortunately - the script also has one issue that looks important to me because of the thing I want to do:
It still allows "take 20" for "SKILL_SEARCH / SUBSKILL_FLAGTRAP"!!!!
It is fired when the PC is actually going to interact with the door/chest/whatever. At that point, however, searching will already be done (most probably by taking 20 ...)
While my script is way more crude (name it primitive, if you like '<img'> ), it does deny take 20 for searches, too. But  I also do understand the drawbacks! So, I will have to put some more brains into it:

nwn.wikia / The Lexicon say, that it is possible to GetCurrentAction of "ACTION_MODE_DETECT", but if I understood that right that event is only triggered if either the PC is actively using "search" OR standing around. It will not be triggered while walking "normally" or running. By thus, using that flag would deny take 20s while standing or searching, but NOT while walking/running - which, of course, is the worst thing that could happen!
IF there's a function or constant that could be checked for whenever a "search" happens, I was not able to find it. '<img'>  '<img'>
May be that could be done fiddling around with GetDetectMode(), but that would require to manually calculate the failure/success for each object in sight of the PC - in the hope of overriding the default AI. It's weird that there seems to be no flag for "ACTION_SKILL_*" or something like that.


So, here's plan B!!

I seems obvious to me (correct me, if I'm wrong!!), that the "thread" denying the take 20 for searching traps must be triggered already from quite some distance to the chest/door/floor that has a trap.

Suggestion:
Create a named "common" subarea (or "trigger area"; don't know if this is the correct translation of the toolset - mine is in german '<img'> ).
Then adding a GetMyArea(OBECT_SELF) function to the script instead of the GetLastAction() and compare the result (which should be a string) to the name of the subarea. If TRUE, the simulated attack will be triggered using MrZorks improved version of SignalEvent.
Additonally, a single attack will be signalized "onEnter" of the subarea to ensure that the heartbeat (6 seconds can be a long time in a narrow dungeon!) does not allow the PC to come close to the trap.

Less elegant, but at least the attack-warnings will only come up within these named areas - and resting should now be possible outside of them. Best of two worlds??
Btw., how big/complex can subaeras be? The toolset seems to allow pretty huge areas, but I'm unsure about what's legit! '<img'>


[EDIT - SUGGESTION NOT WORKING!!!!]
Ok, so I'm wrong again: Apart from the fact that GetMyArea always seems to return an empty string, also GetArea and GetTag(oArea) don't do what I want - there seems to be no way to find out if the PC is inside a closed trigger area. GetTag(oArea) always tells me it's "Area001" - which is the module's area. Also adding variables to the trigger and such stuff did not help - the heartbeat script is not able to determine if the PC is inside or outside the subarea.
[/EDIT]

Sorry for bothering you again, but that search traps thing is important to me. However, I'm open to all suggestions! And I hope you can see that I really try to fiddle things out myself before posting.

TIA!
Belushi ':wizard:'
               
               

               


                     Modifié par Belushi1965, 08 février 2014 - 02:01 .
                     
                  


            

Legacy_Belushi1965

  • Newbie
  • *
  • Posts: 16
  • Karma: +0/-0
Disable "Take 20"??
« Reply #12 on: February 08, 2014, 05:07:03 pm »


               Ok. This thing seems to work - I needed to invoke "onEnter" and "onExit" and learned how to use local variables.
I hope it does not only work by pure coincidence...

Script 1: "onEnter" event of a subregion ("trigger"; closed polygone):
This initiates and sets a "combat" flag to the entering PC, which seems to be legit. Also, it sends one single combat signal to avoid the 6 seconds heartbeat issue. I believe this is multiplayer compatible, but I believed many things in the last 3 days that came up to be wrong! ;-)

void main()
{
    object oPC = GetEnteringObject();
    if ( GetIsPC(oPC) && GetIsObjectValid(oPC) )
    {
      SetLocalInt(oPC,"IsInCombat",1);
      SignalEvent(oPC, EventSpellCastAt(oPC, SPELL_ALL_SPELLS, 1));
    }
}


Script 2: "onExit" event of the subregion:
This removes the flag from the PC that exits the region.

void main()
{
    object oPC = GetExitingObject();
     if ( GetIsPC(oPC) && GetIsObjectValid(oPC) )
     {
       SetLocalInt(oPC,"IsInCombat",0);
    }
}


Script 3: "heartbeat" of the module:
Checks every player in the game for the flag to be set and fires a combat signal, if true.
I attached it to the module, not to the marked region, because that would cause many heartbeat scripts to be fired. There will be many regions in the module when I will ever be able to finish it. '<img'>

void main()
{
    object oPC = GetFirstPC();
    while (GetIsObjectValid(oPC) == TRUE)
    {
        int oCombat = GetLocalInt(oPC,"IsInCombat");
        if (oCombat == 1) {
          SignalEvent(oPC, EventSpellCastAt(oPC, SPELL_ALL_SPELLS, 1));
        }
        oPC = GetNextPC();
    }
}

Again, does what I want - avoiding some of the issues of my primitive first try. And prevents the PC from taking 20 for "search" and most other skills. And allows resting outside the marked areas. :-)

What do you think???

':police:'
               
               

               


                     Modifié par Belushi1965, 08 février 2014 - 05:14 .
                     
                  


            


Legacy_MrZork

  • Hero Member
  • *****
  • Posts: 1643
  • Karma: +0/-0
Disable "Take 20"??
« Reply #14 on: February 08, 2014, 07:51:12 pm »


               Just ran a quick test. I put down a trap with a detect DC of 26 and took in a toon with a search skill of 6. Then I walked him near the trap while not in combat. It took him about a minute of standing there to spot the trap. If he had been getting Take 20s, he should have spotted almost instantly. So, I am thinking there is no Take 20 for the search roll in trap detection.

(That isn't to say that searches aren't penalized somehow during combat, although the wiki doesn't mention it.)