AssignCommand(object, action)

From NWN Lexicon
Jump to: navigation, search

Assigns an action command to a creature to act on.

void AssignCommand(
    object oActionSubject,
    action aActionToAssign
);
Question icon.png This article needs clarification. You can help the NWN Lexicon by explaining things better.

Parameters

oActionSubject
The object you want to assign the command to.
aActionToAssign
The action to assign.

Description

Assigns aActionToAssign to oActionSubject's action queue.

No return value, but if an error occurs, the log file will contain "AssignCommand failed.".

Do not assume this always will work and fire correctly. It fails if SetCommandable() is on, or a variety of other scenarios, especially if there are other actions in the target creatures queue already (if a ClearAllActions() is then called and this is still waiting, the AssignCommand also is cleared). Instead for things that must fire make rigorous use of ExecuteScript and DelayCommand, as per the example script at the end of the page.

If the object oActionSubject doesn't exist, nothing happens.

AssignCommand() is very useful for making a creature who isn't calling the script do something, to itself or others, or control it in a cutscene or from an event such as the OnPlayerEquipItem, OnPlayerDeath and so on.

Note that aActionToAssign is more or less a callback function; aActionToAssign can be a void like ClearAllActions(), or a new Foobar() function.

Do not bother to use AssignCommand(OBJECT_SELF, Foobar()), as AssignCommand() is worthless in that situation - the caller of the script automatically uses Foobar even without the AssignCommand, and it is only likely to generate more performance issuses.

Note that AssignCommand'ed commands get executed AFTER the script finishes. For instance, this will cause the caller first to say Foo, then Bar:

AssignCommand(OBJECT_SELF, SpeakString("Bar"));
SpeakString("Foo");

Remarks

A common mistake on AssignCommand seems to be to use OBJECT_SELF. OBJECT_SELF is a constant, so it is more of a NULL pointer than a "this" pointer. If you use it in the AssignCommand or similar functions, the creature will attempt to perform the action against itself which most often seems to not be what the person using it intended.

Also note that, OBJECT_SELF, if used in the aActionToAssign, will be oActionSubject. For instance, the following will cause oPC to speak his own name, not the name of the owner of the script:

AssignCommand(oPC, SpeakString(GetName(OBJECT_SELF)));

Note that, though it takes an "action" type as a parameter, NWScript doesn't allow you to do stuff like action aDo = SendMessageToPC(oPC, "Message"); You need to put the actual SendMessage... (or whatever) inside the AssignCommand call - you can't use the action keyword as a pointer to a function. In fact, the action keyword is unusable, except that BioWare can put it in function declarations, apparently. But we can't use it in function declarations ourselves.

AssignCommand() can be used by any object at any time to delay some code running, it will not assign commands as actions - so AssignCommand(oTarget, Foobar()); should just run the code instantly, not after any additional actions. This is only true for the void functions put into an AssignCommand - any actions put into one will be still added to the end of an action queue on the object.

A use of AssignCommand() and ActionDoCommand() can make a void function be put into the action queue - see ActionDoCommand()'s entry.

Version

1.62

Example

// A simpler example - examine the thing being used by the PC
void main()
{
    // Define oPC AND OBJECT_SELF.
    object oPC = GetLastUsedBy();
 
    // * Note: Remember, defining OBJECT_SELF means that when
    //    we pass oSelf into the AssignCommand()'s
    //    ActionExamine, instead of OBJECT_SELF, it WILL examine
    //    this sign, not the PC. see the note in Remarks.
    object oSelf = OBJECT_SELF;
 
    // Assign the action to read oSelf, not OBJECT_SELF, which
    // would be the PC.
    AssignCommand(oPC, ActionExamine(oSelf));
}
 
// A valid use of AssignCommand to assign a new 
// ActionJumpToObject() (to Another portal) for the PC who 
// last used the portal.
 
// In this case, it also shows a valid use of ClearAllActions() with other AssignCommands().
 
// Note: It could use JumpToObject to put the action on top of the
// action queue - but thats kinda cheating for such a nice example.
 
void main()
{
    // Get PC
    object oUser = GetLastUsedBy();
 
    // Get where we want to go to (another portal placeable)
    object oTarget = GetObjectByTag("PORTAL_TARGET");
 
    // Make them stop what they might have queued (such as
    // spellcasting, skill using, emotes...)
    AssignCommand(oPC, ClearAllActions());
 
    // Force them to move to the target instantly
    // This action will NOT be cleared, because it is called after
    // the ClearAllActions() which is assigned.
    // * also note it does not use OBJECT_SELF, but oTarget, which
    //    means that the PC won't try and jump to itself.
    AssignCommand(oPC, ActionJumpToObject(oTarget));
}
 
// A good example of coding. You can make one call of
// AssignCommand() to make the target to many actions.
// Advantages: Much more efficent, faster, easier to code in 
//   the main script, can use includes efficiently. Much easier to 
//   have all the actions to assign in one place too, and makes
//   sure, 100%, the order they fire in.
 
// Function, can be in an include for example.
void IMoveToLocation(location lTarget)
{
    // We move here
    ClearAllActions();
    JumpToLocation(lTarget);
}
 
// Main function
void main()
{
    // Get location to move to, a waypoint
    location lTarget = GetLocation(GetWaypointByTag("WAYPOINT"));
 
    // Move the person
    object oUser = GetLastUsedBy();
    AssignCommand(oUser, IMoveToLocation(lTarget));
}

A further example shows how best to pass variables to a DelayCommand very safely; along with ExecuteScript and AssignCommand usage.

void main()
{
    // There are a lot of use cases for DelayCommand, AssignCommand and ExecuteScript. Some best practices for all 3:
    object oNPC = GetObjectByTag("my_favorite_npc");
    object oModule = GetModule();
 
    // This will fire the script "npc_script" after 10 seconds on oNPC. However:
    // - If we (OBJECT_SELF) die/are destroyed, it will not execute the script at all
    // - If oNPC doesn't exist the script will not be executed
    // This is not recommended except for if OBJECT_SELF is always valid (eg: being called from a module script), even
    // then you have no idea if it will execute safely if oNPC dies in the intervening time
    DelayCommand(10.0, ExecuteScript("npc_script", oNPC));
 
    // AssignCommand could help work around the caller of this script dying meaning unintended behaviour
 
    // Here we immediately assign the DelayCommand to oNPC, but what is ExecuteScript() running on? oNPC or OBJECT_SELF? Unclear.
    AssignCommand(oNPC, DelayCommand(10.0, ExecuteScript("npc_script", OBJECT_SELF)));
 
    // A little better - now we know that the ExecuteScript will always run on oNPC, a lot clearer!
    // AssignCommand() on a creature object can fail - eg if SetCommandable() is set to FALSE
    AssignCommand(oNPC, DelayCommand(10.0, ExecuteScript("npc_script", oNPC)));
 
    // So we can make it safer by assigning the command to oModule, to execute a script on oTarget.
    // Safer but we still can have times this will fail silently, mainly if oNPC is dead/destroyed
    AssignCommand(oModule, DelayCommand(10.0, ExecuteScript("npc_script", oNPC)));
 
    // For fire and forget delays with no AssignCommand you could put in a script that executes immediately that itself 
    // calls DelayCommand(). This still has issues if oNPC dies
    ExecuteScript("delay_npc_script", oNPC);
 
    // For things that must run, regardless of oNPC or OBJECT_SELF existing, use this method
    // You make the module do the script, which will always exist, and it does a DelayCommand() to itself to do a future script call
    // which can then ExecuteScript() on the oNPC if it exists, and sends a debug message if it doesn't.
    SetLocalObject(oModule, "DELAY_NPC_OBJ", oNPC);
    ExecuteScript("delay_npc_modsc", oModule);
}
 
// npc_script
void main()
{
    SpeakString("Hello!");    
}
 
// delay_npc_script
void main()
{
 
    DelayComand(10.0, ExecuteScript("npc_script", oHello))    
}
 
// delay_npc_modsc
void main()
{
    DelayComand(10.0, ExecuteScript("mod_npc_script", OBJECT_SELF));    
}
 
// mod_npc_script
void main()
{
    // Get and delete the passed through variable
    object oModule = OBJECT_SELF;
    object oNPC = GetLocalObject(oModule, "DELAY_NPC_OBJ");
 
    // Valid NPC we can do the npc_script immediately
    if(GetIsObjectValid(oNPC))
    {
        ExecuteScript("npc_script", oNPC);        
    }
    else
    {
        // Something went wrong, where is oNPC! Debug message to first PC.
        SendMessageToPC(GetFirstPC(), "Error, NPC not found for delayed npc_script call!");       
    }
}

See Also

functions:  SetCommandable


author: Charles Feduke, editor: Jasperre, additional contributor(s): Edward Wilson, Jasperre, Lilac Soul