Global Script : Guard Duty

Scripted ALFA systems & related tech discussions (ACR)

Moderators: ALFA Administrators, Staff - Technical

Locked
User avatar
Baalster
Brown Bear
Posts: 272
Joined: Sun Jan 04, 2004 9:56 pm
Contact:

Global Script : Guard Duty

Post by Baalster »

GuardDuty.txt

System description:
Many modules have guards whos responsibility is top unlock/open/lock/close gates, and use multiple scripts and conversations in order to do so. This system uses the same set of scripts for all NPC guards, whether they are on the inside or outside of the gate. They only need one conversation per gate. The same conversation will handle both the inside and outside gate NPCs.

System components:
000_sc_guard_in : SC to determine if NPC is inside the gate
000_sc_guard_out : SC to determine if NPC is outside the gate
000_guardduty : Main Action Taken script for NPC to perform Guard Duty

In addition a conversation is required

Example:
For the Zhentarim Lumber Camp I have a need to restrict who get access to it. I don't want players with low reputation in the Zhentarim faction to access it, and I want a different behaviour of the guards depending on the PC's reputation.

The gate area looks something like this:

Code: Select all

               |
               | W2-G2
               | 
               G
               |
         W1-G1 |
               |
This is a gate (G) with two guards NPCs on each side (G1 and G2).
They are located at two waypoints (W1 and W2), and are expected to return there after they have performed theyr duties.

The gate tag should be known. In my case it would be "stockage_east_gate".
The two waypoints should be named so we know if they are inside or outside the gate.
W1 : zlc_east_in
W2 : zlc_east_out

Variables on the guard NPC (G1 and G2):

We need the two guards, and depending on your spawn system, you can either have them NESS'ed in, which means you will have to have two different blueprints for guards, or you can place the guards static and change them after they are placed out.
Regardless of method, we need a set of variables on the NPCs. We need two different NPCs, one on each side.

Code: Select all

Variables:
    MANDATORY VARIABLES:
        guard_door : tag of door
        guard_waypoint : waypoint to return to after close
    OPTIONAL VARIABLES:
        guard_doordelay : number of seconds the door should remain open. Default 10 seconds.
        guard_closedelay : number of seconds before NPC returns to waypoint. From start of open action. Default 15 seconds
        guard_nolock : if set to 1, the door will not be locked/unlocked. Only opened/closed. Default 0
        guard_nokey : if set to 1, a key is not required to lock/unlock. Default 0
So using the above example:

Code: Select all

G1 will have the following variables set:
guard_door		string	stockade_east_gate
guard_opendelay		int	15
guard_closedelay	int	20
guard_waypoint		string	zlc_east_in

G2 will have the following variables set:
guard_door		string	stockade_east_gate
guard_opendelay		int	15
guard_closedelay	int	20
guard_waypoint		string	zlc_east_out
They will both be assigned the following conversation:

Code: Select all

Root
  Bugger off it ya'know whats good for ya. Ya have no business here (SC:rep_sc_rep_low)

  [Eyes you suspiciously] What do you want ? (SC:rep_sc_rep_mid)
    I'd like to enter the camp. (SC:guard_sc_out)
      I guess... Don't make any trouble, or the sarge will have your head [gulps] ... and mine. (AT:guard_opengate)
    I'd like to leave the camp. (SC:guard_sc_in)
      Open gate, close gate, open gate and close gate... [rolls eyes] (AT:guard_opengate)
    Never Mind, I'll stay here for now.

  [salutes and springs to attention] Hail <FullName>. Let me get the gates for you <Sir/Madam>.(SC:rep_sc_rep_high)
    And waste no time doing it ... (AT:guard_opengate)
    Never Mind, I'll stay here for now.
This example is used with the WH reputation system and uses the SC scripts rep_sc_rep_* to determine the PCs
reputation with the NPC's faction (Zhentarim). Depending on your reputation you get a different reaction.

- You don't get in if you have too low reputation.
- If you're barely known to them, you get in, but are dealt with rudely
- If you're very known to them, you'll get respect and can respond harsly without problems.

This shows two things.
1. In case 2, you have a different response for the inside and outside guard.
2. In case 3, you have the same response, but could have two if you wanted.

A simpler version without the reputation system would look like this:

Code: Select all

Root
  [Eyes you suspiciously] What do you want ? 
    I'd like to enter the camp. (SC:guard_sc_out)
      I guess... Don't make any trouble, or the sarge will have your head [gulps] ... and mine. (AT:guard_opengate)
    I'd like to leave the camp. (SC:guard_sc_in)
      Open gate, close gate, open gate and close gate... [rolls eyes] (AT:guard_opengate)
    Never Mind, I'll stay here for now.

Code: Select all

//////////////////////////////////////////////////////////////////////////////// 
// 
//  System Name : Global ALFA script
//     Filename : 000_sc_guard_in.nss 
//    $Revision::           $ 1.0
//        $Date::           $ 09-OCT-2006
//       Author : Petter Stene (Baalster)
// 
//    Var Prefix: unique name comprised of 3 letter system and feature acronyms 
//  Dependencies: OBJECT_SELF is an NPC with guard duty variables set. See GuardDuty.txt
// 
//  guard_waypoint  : 
// 	tag should end with "_in" for a guard inside the gate
//	tag should end with "_out" for a guard outside the gate
// 
//  Description 
//  Script is a starting condition which determines whether the guard is placed
//  on the inside or outside of the gate. The purpose is to use the same script
//  and conversation file for both the inside and outside guards.
//
//  This SC returns true if the "guard_waypoint" local variable string on the guard contain "_in".
//  Used in Guard Duty NPC conversations. 
//
//  Revision History 
//  09-OCT-2006 Baalster. Initial creation for NWN2
// 
//////////////////////////////////////////////////////////////////////////////// 


int StartingConditional() 
{
	return (FindSubString(GetLocalString(OBJECT_SELF,"guard_waypoint"),"_in")>1);
}

Code: Select all

//////////////////////////////////////////////////////////////////////////////// 
// 
//  System Name : Global ALFA script
//     Filename : 000_sc_guard_out.nss 
//    $Revision::           $ 1.0
//        $Date::           $ 09-OCT-2006
//       Author : Petter Stene (Baalster)
// 
//    Var Prefix: unique name comprised of 3 letter system and feature acronyms 
//  Dependencies: OBJECT_SELF is an NPC with guard duty variables set. See GuardDuty.txt
// 
//  guard_waypoint  : 
// 	tag should end with "_in" for a guard inside the gate
//	tag should end with "_out" for a guard outside the gate
// 
//  Description 
//  Script is a starting condition which determines whether the guard is placed
//  on the inside or outside of the gate. The purpose is to use the same script
//  and conversation file for both the inside and outside guards.
//
//  This SC returns true if the "guard_waypoint" local variable string on the guard contain "_out".
//  Used in Guard Duty NPC conversations. 
//
//  Revision History 
//  09-OCT-2006 Baalster. Initial creation for NWN2
// 
//////////////////////////////////////////////////////////////////////////////// 

int StartingConditional() 
{
	return (FindSubString(GetLocalString(OBJECT_SELF,"guard_waypoint"),"_out")>1);
}

Code: Select all

//////////////////////////////////////////////////////////////////////////////// 
// 
//  System Name : Global ALFA script
//     Filename : 000_guardduty.nss 
//    $Revision::           $ 1.0
//        $Date::           $ 09-OCT-2006
//       Author : Petter Stene (Baalster)
// 
//    Var Prefix: unique name comprised of 3 letter system and feature acronyms 
//  Dependencies: See GuardDuty.txt
//  MANDATORY VARIABLES:
//      guard_door : tag of door
//      guard_waypoint : waypoint to return to after close
//  OPTIONAL VARIABLES:
//      guard_doordelay : number of seconds the door should remain open. Default 10 seconds.
//      guard_closedelay : number of seconds before NPC returns to waypoint. 
//                         From start of open action. Default 15 seconds
//      guard_nolock : if set to 1, the door will not be locked/unlocked. Only opened/closed. Default 0
//      guard_nokey : if set to 1, a key is not required to lock/unlock. Default 0
// 
//  Description 
//  Script relies heavily on variables set on the NPC from which the Action Taken is performed.
//  Based on the variables, the gate and the waypoint it will move to the gate, unlock/open/close/lock
//  and then move back to it's starting waypoint. Called from a Guard Duty NPC conversation.
//
//  Revision History 
//  09-OCT-2006 Baalster. Initial creation for NWN2
// 
//////////////////////////////////////////////////////////////////////////////// 


void main()
{
    // Get the variables off the NPC guard
    // MANDATORY VARIABLES:
    //    guard_door : tag of door
    //    guard_waypoint : waypoint to return to after close
    // OPTIONAL VARIABLES:
    //    guard_doordelay : number of seconds the door should remain open.
    //    guard_closedelay : number of seconds before NPC returns to waypoint. From start of open action.
    //    guard_nolock : if set to 1, the door will not be locked/unlocked. Only opened/closed.
    //    guard_nokey : if set to 1, a key is not required to lock/unlock.

    object oDoor = GetObjectByTag(GetLocalString(OBJECT_SELF,"guard_door"));
    object oWaypoint = GetObjectByTag(GetLocalString(OBJECT_SELF,"guard_waypoint"));
    float fDoorDelay = GetLocalFloat(OBJECT_SELF,"guard_doordelay");
    float fCloseDelay = GetLocalFloat(OBJECT_SELF,"guard_closedelay");
    int nNolock = GetLocalInt(OBJECT_SELF,"guard_nolock");
    int nNokey = GetLocalInt(OBJECT_SELF,"guard_nokey");    
    float fDirection = GetFacing(OBJECT_SELF);

    // Make sure we have some reasonable default values. nNolock will default to 0.
    if (fDoorDelay==0.0)
        fDoorDelay=10.0;
    if (fCloseDelay==0.0)
        fCloseDelay=15.0;

    // Unlock if needed, and open
    if (!nNolock)
        if (!nNokey)
            AssignCommand(OBJECT_SELF, ActionUnlockObject(oDoor));
        else
            SetLocked(OBJECT_SELF, FALSE);
    AssignCommand(OBJECT_SELF, ActionOpenDoor(oDoor));

    // Close and lock if needed
    AssignCommand(OBJECT_SELF, DelayCommand(fDoorDelay, ActionCloseDoor(oDoor)));
    if (!nNolock)
        if (!nNokey)
            AssignCommand(OBJECT_SELF, DelayCommand((fDoorDelay + 1.0), ActionLockObject(oDoor)));
        else
            SetLocked(OBJECT_SELF, TRUE);

    // Move back to post waypoint and face correctly
    location lWaypoint = GetLocation(oWaypoint);
    AssignCommand(OBJECT_SELF, DelayCommand(fCloseDelay, ActionMoveToLocation(lWaypoint)));
    if (fDirection > 0.0)
        AssignCommand(OBJECT_SELF, 2.0,SetFacing(fDirection);
}
Castles in the air - they are so easy to take refuge in. And so easy to build, too.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

Nice guard template Baalster. We should certainly incorporate these into our conversation scripts, but there are some minor conventions issues that need to be addressed first: script name format should be acr_cs_* (groups global conversation scripts together) and use {} around your conditional blocks (helps with readability). Do you want access to the script repository? Thangorn already has access so either he or I can submit these for you if you don't think you need it.

A few questions:

1. Would it be possible to eliminate the door tag variable and just look for a "gate" within 5 ft? It's minimal overhead which offers some configuration efficiency/simplicity (removing a local variable) that might make life easier for builders.

2. Would a POST waypoint eliminate the need for the "guard_waypoint" variable? The guard may return to his post in between opening and closing (depending on the delay), which gets him out of the way of the oncoming traffic.

3. How do you handle players who try to piggy back or break in? That might be something to have in the guard AI? Wondering if you have any thoughts on this.

4. Just to be sure, guard_sc_out & guard_sc_in & guard_opengate in the conversation template correspond to the 000_sc_guard_out.nss & 000_sc_guard_in.nss & 000_guardduty.nss scripts, respectively?

Thanks for contributing.
User avatar
Baalster
Brown Bear
Posts: 272
Joined: Sun Jan 04, 2004 9:56 pm
Contact:

Post by Baalster »

I rewrote these from the WH module in a rush, so some conversion standard bullets may have missed ... :)

1. Would it be possible to eliminate the door tag variable and just look for a "gate" within 5 ft? It's minimal overhead which offers some configuration efficiency/simplicity (removing a local variable) that might make life easier for builders.

In theory it would. But finding objects within a range was historically an expensive operation, so I wanted to make is as cheap as possible. Also the prospect of a DM posessing the NPC and forgetting to take him back may cause some issues... I found it simpler to just ensure that they have everything, including the key required to lock the gate if needed. Better control from a design point of view.

2. Would a POST waypoint eliminate the need for the "guard_waypoint" variable? The guard may return to his post in between opening and closing (depending on the delay), which gets him out of the way of the oncoming traffic.

The guard_waypoint variable is used to determine if this is an inside or outside guard. A waypoint can be used, but for the same reasons as above, I find it easier to know what's there and what to look for. It is also easier to debug...

3. How do you handle players who try to piggy back or break in? That might be something to have in the guard AI? Wondering if you have any thoughts on this.

You can't ... I guess it's a RP issue. But in case of a zhent piggybacking into the harper stronghold, the persistant reputation system would make the stay very unpleasant and painful. For situations where the AI doesn't kick in, the dialogs and questgivers should have this situation in mind. "I don't remember you checking in at the gate... Guards!!!!!"

4. Just to be sure, guard_sc_out & guard_sc_in & guard_opengate in the conversation template correspond to the 000_sc_guard_out.nss & 000_sc_guard_in.nss & 000_guardduty.nss scripts, respectively?

Ya, things were in a rush ... not bad for first attempt though...

Baalster
Castles in the air - they are so easy to take refuge in. And so easy to build, too.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

No worries about the conventions stuff. :)

1. What about making the variable an optional parameter? In the absence of one, you scan in a 15 ft radius for a door. in the absence of any doors within that radius, you log an error.

2. What happens if the guard is lured away by combat (say an attack on the Zhentarim outpost)? He could end up just about anywhere, right? It seems necessary to use a POST for these guards that's tied to the AI so they always return to where they need to be, not just after the conversation. If you were to rely on the POST mechanism, I think you could dispense with the waypoint scan in the conversation script and simply make a delayed call to WalkWaypoints. You might also want to explore locking in the action queue with SetCommandable so it can't be modified/interrupted by external factors, though I'm not certain if this function is limited to PCs. Just a thought.

Anyway, these are just ideas. Who's committing these scripts?
User avatar
Baalster
Brown Bear
Posts: 272
Joined: Sun Jan 04, 2004 9:56 pm
Contact:

Post by Baalster »

ç i p h é r wrote:1. What about making the variable an optional parameter? In the absence of one, you scan in a 15 ft radius for a door. in the absence of any doors within that radius, you log an error.

2. What happens if the guard is lured away by combat (say an attack on the Zhentarim outpost)? He could end up just about anywhere, right? It seems necessary to use a POST for these guards that's tied to the AI so they always return to where they need to be, not just after the conversation. If you were to rely on the POST mechanism, I think you could dispense with the waypoint scan in the conversation script and simply make a delayed call to WalkWaypoints. You might also want to explore locking in the action queue with SetCommandable so it can't be modified/interrupted by external factors, though I'm not certain if this function is limited to PCs. Just a thought.
1. Doable. We have to be careful that the guards don't happen to be within 15 feet of another door ...

2. Then we need another way of finding out whether the guard is inside and outside the gate, in order to provide the correct conversation. In my designs, the strongest NPCs are never the guards ... the strongest ones come to investigate and punish ... I would expect the guards to die if fighting. They would of course shout alarm and drag out a contingent of zhent soldiers. Not sure how the AI has improved in NWN2 with different behaviour... But it's worth considering as well.

Baalster
Castles in the air - they are so easy to take refuge in. And so easy to build, too.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

I added your scripts to the repository Baalster. If you want to incorporate any of the above, just resubmit the changed files.
User avatar
Baalster
Brown Bear
Posts: 272
Joined: Sun Jan 04, 2004 9:56 pm
Contact:

Post by Baalster »

Regarding 2, we can change use the tag of the guards to find out where they are posted - inside or outside the gate. Then have them return to their POST waypoint afterwards.

My intitial thought when I created this, was to avoid dependencies on the tag, since the variable can be changed dynamically during runtime, the tag cannot. So if you want a guard to switch location, all you have to do is to have an interface to change the variables. ie a Guard Universal Rotation Pointdevice (GULP) :)

Baalster
Castles in the air - they are so easy to take refuge in. And so easy to build, too.
User avatar
Baalster
Brown Bear
Posts: 272
Joined: Sun Jan 04, 2004 9:56 pm
Contact:

Post by Baalster »

Here is a new version that handles issue 2. Somehow I'm not sure if it will be any better, but what the heck... I'm easy.

B.

Code: Select all

//////////////////////////////////////////////////////////////////////////////// 
// 
//  System Name : Global ALFA script 
//     Filename : 000_guardduty.nss 
//    $Revision::           $ 1.1 
//        $Date::           $ 09-OCT-2006 
//       Author : Petter Stene (Baalster) 
// 
//    Var Prefix: unique name comprised of 3 letter system and feature acronyms 
//  Dependencies: See GuardDuty.txt 
//  MANDATORY VARIABLES: 
//      guard_waypoint : waypoint to return to after close 
//  OPTIONAL VARIABLES: 
//      guard_door : tag of door 
//      guard_doordelay : number of seconds the door should remain open. Default 10 seconds. 
//      guard_closedelay : number of seconds before NPC returns to waypoint. 
//                         From start of open action. Default 15 seconds 
//      guard_nolock : if set to 1, the door will not be locked/unlocked. Only opened/closed. Default 0 
//      guard_nokey : if set to 1, a key is not required to lock/unlock. Default 0 
// 
//  Description 
//  Script relies heavily on variables set on the NPC from which the Action Taken is performed. 
//  Based on the variables, the gate and the waypoint it will move to the gate, unlock/open/close/lock 
//  and then move back to it's starting waypoint. Called from a Guard Duty NPC conversation. 
// 
//  Revision History 
//  09-OCT-2006 Baalster. Initial creation for NWN2 
//  14-OCT-2006 Baalster. Made guard_door variable optional. It will look for a door within 3 meters if variable is not set.
// 
//////////////////////////////////////////////////////////////////////////////// 


void main() 
{ 
    // Get the variables off the NPC guard 
    // MANDATORY VARIABLES: 
    //    guard_waypoint : waypoint to return to after close 
    // OPTIONAL VARIABLES: 
    //    guard_door : tag of door 
    //    guard_doordelay : number of seconds the door should remain open. 
    //    guard_closedelay : number of seconds before NPC returns to waypoint. From start of open action. 
    //    guard_nolock : if set to 1, the door will not be locked/unlocked. Only opened/closed. 
    //    guard_nokey : if set to 1, a key is not required to lock/unlock. 


    object oDoor = GetObjectByTag(GetLocalString(OBJECT_SELF,"guard_door")); 

    // Make guard_door variable optional
    // GetLocalString on a non existant variable returns "". GetObjectByTag("") returns OBJECT_INVALID.
    if (oDoor == OBJECT_INVALID) (
        // Get the nearest object of type door
        oDoor = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, 1); 
    }

    // The float value returned by this is in in-game meters. Each in-game tile is 10x10M. 3meters=15feet
    if (GetDistanceBetween(OBJECT_SELF, oDOOR) > 3.0) {
        // SpeakString with SILENT_SHOUT will send message to DM channel. 
        SpeakString("Guard open gate error. Distance to gate > 3 meters. Guard tag="+GetTag(OBJECT_SELF), SILENT_SHOUT);
        // !!! Log format to be changed to conform with ALFA standard log format.
        WriteTimestampedLogEntry("Guard cannot find gate error. Guard tag="+GetTag(OBJECT_SELF));
    }
    else {
        object oWaypoint = GetObjectByTag(GetLocalString(OBJECT_SELF,"guard_waypoint")); 
        float fDoorDelay = GetLocalFloat(OBJECT_SELF,"guard_doordelay"); 
        float fCloseDelay = GetLocalFloat(OBJECT_SELF,"guard_closedelay"); 
        int nNolock = GetLocalInt(OBJECT_SELF,"guard_nolock"); 
        int nNokey = GetLocalInt(OBJECT_SELF,"guard_nokey");    
        float fDirection = GetFacing(OBJECT_SELF); 

        // Make sure we have some reasonable default values. nNolock will default to 0. 
        if (fDoorDelay==0.0) 
            fDoorDelay=10.0; 
        if (fCloseDelay==0.0) 
            fCloseDelay=15.0; 

        // Unlock if needed, and open 
        if (!nNolock) 
            if (!nNokey) 
                AssignCommand(OBJECT_SELF, ActionUnlockObject(oDoor)); 
            else 
                SetLocked(OBJECT_SELF, FALSE); 
        AssignCommand(OBJECT_SELF, ActionOpenDoor(oDoor)); 

        // Close and lock if needed 
        AssignCommand(OBJECT_SELF, DelayCommand(fDoorDelay, ActionCloseDoor(oDoor))); 
        if (!nNolock) 
            if (!nNokey) 
                AssignCommand(OBJECT_SELF, DelayCommand((fDoorDelay + 1.0), ActionLockObject(oDoor))); 
            else 
                SetLocked(OBJECT_SELF, TRUE); 

        // Move back to post waypoint and face correctly 
        location lWaypoint = GetLocation(oWaypoint); 
        AssignCommand(OBJECT_SELF, DelayCommand(fCloseDelay, ActionMoveToLocation(lWaypoint))); 
        if (fDirection > 0.0) 
            AssignCommand(OBJECT_SELF, 2.0,SetFacing(fDirection); 
    }    
}
Castles in the air - they are so easy to take refuge in. And so easy to build, too.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

Image

Now all you need is your own SVN account. *hint* 8)
Locked