Aller au contenu

Photo

Turn-and-face trigger


  • Veuillez vous connecter pour répondre
4 réponses à ce sujet

#1
rjshae

rjshae
  • Members
  • 4 509 messages
It's a simple enough thing, but it seems to work and it provides a nice atmospheric effect so I thought I'd post it for others to use. Basically a PC entering the trigger area will cause the designated NPC to turn and face them (unless the PC is in stealth mode.) It's handy for stationary (lurking) NPCs or guards and seems to work okay in combination with the usual immobile animations.

// ms_turn_to_face
/*
    This is the On Enter script for a Trigger blueprint that
    causes an NPC to turn and face the entering creature.
    If the 'NPC_Tag' is not set to the tag of a valid
    creature, it exits. If the 'OnlyFacePC' variable is
    set to 1, it ignores non-PCs who enter the trigger.
    If 'BlurtOnce' is non-null, the NPC will speak the
    string the first time the creature enters the trigger.
*/
// RJH 31may12

const string NPCtag = "NPC_Tag";
const string OnlyFacePC = "OnlyFacePC";
const string BlurtOnce = "BlurtOnce";

void main()
{
    object oTarget = GetEnteringObject();
    if ( ! GetIsObjectValid( oTarget ) )
        return; // Invalid object
    
    // Get the NPC tag
    string sNPC = GetLocalString( OBJECT_SELF, NPCtag );
    if ( GetStringLength( sNPC ) == 0 )
        return; // Variable was not set
    
    // Find the NPC
    object oNPC = GetObjectByTag( sNPC );
    if ( ! GetIsObjectValid( oNPC ) )
        return; // Invalid object
    
    // Get the Face PC setting
    int bFacePC = GetLocalInt( OBJECT_SELF, OnlyFacePC );
    if ( bFacePC && ! GetIsPC( oTarget ) )
        return; // Entering object is not a PC
    
    // Get entering creature's stealth mode
    int nStealthMode = GetStealthMode( oTarget );
    if ( nStealthMode == STEALTH_MODE_ACTIVATED )
        return; // Don't break the illusion of stealthiness
    
    // Get the tag of the entering object
    string sEnterTag = GetTag( oTarget );
    if ( StringCompare( sNPC, sEnterTag ) == 0 )
        return; // Ignore self
    
    // Check for a blurt string
    int bDoBlurt = FALSE;
    string sBlurt = GetLocalString( OBJECT_SELF, BlurtOnce );
    if ( GetStringLength( sBlurt ) > 0 ) {
        string SBlurtCheck = BlurtOnce + sNPC;
        int bHasBlurted = GetLocalInt( oTarget, SBlurtCheck );
        if ( bHasBlurted == 0 ) {
            bDoBlurt = TRUE;
            
            // Clear actions so blurt string is not delayed
            AssignCommand( oNPC, ClearAllActions() );
        }
        SetLocalInt( oTarget, SBlurtCheck, 1 );
    }
    
    // Turn to face target
    vector vTarget = GetPosition( oTarget );
    vector vNPC = GetPosition( oNPC );
    vector vDelta = vTarget - vNPC;
    float fFacing = VectorToAngle( vDelta );
    AssignCommand( oNPC, SetFacing( fFacing ) );
    
    if ( bDoBlurt ) {
        // Speak the blurt string
        DelayCommand( 0.33f, AssignCommand( oNPC,
            ActionSpeakString( sBlurt ) ) );
    }
}

Any enhancements you can think of? 

Modifié par rjshae, 02 juin 2012 - 03:08 .


#2
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Could shorten it up a bit. Maybe something like so:

void main()
{
    string sNPC = GetLocalString(OBJECT_SELF, "MY_NPC_TAG");    
    object oNPC = GetObjectByTag(sNPC);
    
    if (GetIsObjectValid(oNPC))
    {
        object oTarget = GetEnteringObject();
        string sTarget = GetTag(oTarget);
        //Check to see if Target and NPC are the same. If so return.
        if (sNPC == sTarget) return;
        //If oTarget's stealth mode is activated return.
        if (GetStealthMode(oTarget) == STEALTH_MODE_ACTIVATED) return;
        //FacePCOnly int is set and target is not a PC so return.
        int iFacePCOnly = GetLocalInt(OBJECT_SELF, "FACE_PC_ONLY");
        if (iFacePCOnly && !GetIsPC(oTarget)) return;
        
        string sBlurt = GetLocalString(OBJECT_SELF, "NPC_BLURT");
        int iBlurtCheck = GetLocalInt(oTarget, sNPC);        
        vector vTarget = GetPosition( oTarget );
        
        AssignCommand(oNPC, ClearAllActions(TRUE));
        AssignCommand(oNPC, SetFacingPoint(vTarget));   
        if (sBlurt != "" && !iBlurtCheck)
        {
            DelayCommand(0.33, AssignCommand(oNPC, ActionSpeakString(sBlurt)));
            SetLocalInt(oTarget, sNPC, 1);
        }
    }
}

Also eliminated some functions for string comparisons/lengths and used SetFacingPoint so you didn't have to use any vector math.

Modifié par GhostOfGod, 13 juin 2012 - 05:42 .


#3
rjshae

rjshae
  • Members
  • 4 509 messages
Thanks GhostOfGod. My one comment is that I didn't seem to need to perform a ClearAllActions to change the facing; the NPCs turned even in the middle of another (inanimate) animation. Perhaps the facing gets performed in parallel? At any rate, that's why I only perform the ClearAllActions when there is a string to speak.

Modifié par rjshae, 13 juin 2012 - 06:00 .


#4
GhostOfGod

GhostOfGod
  • Members
  • 863 messages

rjshae wrote...

Thanks GhostOfGod. My one comment is that I didn't seem to need to perform a ClearAllActions to change the facing; the NPCs turned even in the middle of another (inanimate) animation. Perhaps the facing gets performed in parallel? At any rate, that's why I only perform the ClearAllActions when there is a string to speak.


Ah ha. That makes sense. Actions still confuse me a bit. Especially in NWN2. :lol:

#5
rjshae

rjshae
  • Members
  • 4 509 messages

GhostOfGod wrote...

rjshae wrote...

Thanks GhostOfGod. My one comment is that I didn't seem to need to perform a ClearAllActions to change the facing; the NPCs turned even in the middle of another (inanimate) animation. Perhaps the facing gets performed in parallel? At any rate, that's why I only perform the ClearAllActions when there is a string to speak.


Ah ha. That makes sense. Actions still confuse me a bit. Especially in NWN2. :lol:


Yah I agree, it's a bit of a black box. Having more knowledge of the internals would be useful.